DEV Community

Elxpro
Elxpro

Posted on • Edited on

Essa e a maior vantagem de Utilizar Elixir

Saudacao

Seja muito bem vindo, seja muito bem vinda ao FullstackElxpro
Do you want to learn Elixir in three months? https://elxpro.com/sell

Aqui nós discutimos as estratégias e dicas de sua jornada de aprendizado em Elixir. Isso é do zero até a sua primeira vaga como desenvolvedor elixir

Eu sou o Gustavo e o tema de hoje é: Sistemas Distribuidos em Elixir

ps: Voce pode acompanhar o artigo com o video

O que é?

Um sistema distribuído é uma coleção de programas de computador que utilizam recursos computacionais em vários pontos centrais de computação diferentes para atingir um objetivo comum e compartilhado. Os sistemas distribuídos visam remover gargalos ou pontos centrais de falha de um sistema.

nesse caso na linguagem que escolhemos (Elixir) vamos utilizar recursos do NODE

https://hexdocs.pm/elixir/1.12/Node.html

Por quê Entender sobre Distribuicao de Sistemas é importante?

Qual que é o princípio de programar em Elixir? E quais são as vantagens?

Todo mundo que escolhe trabalhar com Elixir e sempre o mesmo assunto:

  • Linguagem Funcional
  • Concorrencia
  • Paralelismo

O importante e o que eu vejo que pode ser uma tendencia no futuro em desenvolvedores Elixir avancados e a utilizacao principalmente de concorrencia e paralelismo em sistemas distribuidos para diminuir o custo em uma empresa.

E como Elixir nos ajuda com distribuição?

Uma lista de frameworks abaixo, mas antes de utilizar esses frameworks, você precisa de entender um conceito muito importante.

MNESIA - https://elixirschool.com/pt/lessons/storage/mnesia
PUBSUB - https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html
LibCluster - https://github.com/bitwalker/libcluster

E a leitura deste artigo: https://dashbit.co/blog/you-may-not-need-redis-with-elixir

Eu poderia falar sobre o MNESIA, Pubsub, Libcluster, Só que antes de entender e antes de começar a distribuir sistemas com Elixir você precisa entender o que está por trás de todos esses frameworks.

Ainda não respondi o porquê e o porquê você entendeu desse tribuição de sistemas É principalmente baixo custo e utilizar performance de sistemas de computadores e de hardware uma linguagem fantástica que no nosso caso e Elixir

Como você chegou nessa conclusão?

Cheguei nessa conclusão resolvendo uma Feature onde eu tinha minhas aplicações rodando em três computadores (nos de Kubernetes) diferentes eu precisava ler um arquivo e mandar esse serviço mandar esse arquivo para o serviço externo, e a esse serviço externo salvar informações no banco de dados quando tava rodando esse serviço produção ou staging, algumas vezes esses arquivos não eram lidos, então procuramos entender o que que tava acontecendo.

O nosso problema é que tinhamos 3 nós rodando e o arquivo e algumas vezes era enviado para o batch e na hora do processamento em background, e ao tentar ler o arquivo ele não encontrava.

A solução mais simples para esse caso seria salvar esses dados um Storage (S3). porém quando a gente fala de linguagem funcional e uma linguagem poderosa que facilita a concorrência, paralelismo e Distribuição, 3 minutos para ler arquivos é muito tempo e daí que veio a ideia da próxima versão utilizar sistemas distribuídos e alguns recursos só que esses recursos que exige muito conhecimento de como Elixir funciona por debaixo dos panos por isso cheguei nessa conclusão e quero compartilhar minha experiência com você e como você pode tomar decisões sábias após esse artigo.

Por onde começar?

Primeiros passos com Distribuicao em Elixir

Este artigo sao exemplos de como utilizar Nodes e RPC. O nosso primeiro passo e saber como criar nos e RPC (remote procedure call).

Vamos comecar pelo IEX

iex --sname gus #desta maneira voce criar um shortname
Enter fullscreen mode Exit fullscreen mode
iex --name gus@ip #desta maneira voce criar um name 
Enter fullscreen mode Exit fullscreen mode

a maior diferenca e o acesso entre internet e local.

Image description

Vamos aprender um pouco dos comandos acima:

Node.ping :app_name 

Enter fullscreen mode Exit fullscreen mode

Voce vai fazer um ping verificando se e possivel se conectar com o APP

Node.connect :app_name

Enter fullscreen mode Exit fullscreen mode

Voce vai se conectar a um Node.

Node.list
Enter fullscreen mode Exit fullscreen mode

voce vai ver a lista de conecoes que voce possui. O que e interessante e que neste caso, quando voce vai conectando os nodes eles criam comunicacao entre todos eles. conforme voce ve no Ultimo Node.list

Chamando funcoes.

Antes de comecar a falar de processos, algo que o Elixir nos entrega de graca e um :rpc. coisa que e muito complexo em outras aplicacoes.

Vamos ver o cenario abaixo:

Image description

No exemplo acima temos o RPC que segue o seguinte padrao para ser chamado:

:rpc.call app_name, MODULE, :function, [parametros] 
Enter fullscreen mode Exit fullscreen mode

E interessante observar quando nao existe o modulo e quando existe, o retorno sempre e para o node que esta chamando.

Utilizando processos em Chamadas.

voce pode utilizar o projeto: https://github.com/elxpro-br/distributed_stock

mas voce pode tambem criar o modulo abaixo:

defmodule DistributedStock do
  def handle_sock(stock) do
    receive do
      {:add, product} ->
        stock_updated = [product] ++ stock
        handle_sock(stock_updated)
      :status ->
        IO.inspect stock
        handle_sock(stock)
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

Image description

vamos comecar pelo start da aplicacao:

❯ iex --sname elxpro-server -S mix 
Enter fullscreen mode Exit fullscreen mode

neste exemplo iniciamos uma aplicacao normal em elixir porem com um short-name

um processo foi criado e registrado com um nome.

> pid = spawn DistributedStock, :handle_sock, [[]]
> Process.register pid, :my_stock
Enter fullscreen mode Exit fullscreen mode

ps: O artigo abaixo vai ajudar a entender mais sobre spawn, send e receive

https://www.youtube.com/watch?v=em4QECkQx4s&t=801s

E agora comecamos a enviar dados para atualizar o nosso estoque, porem no mesmo modulo que comecamos a aplicacao.

> send :my_stock, :status
> send :my_stock, {:add, {"pumpkin", 5}} 
> send :my_stock, :status  
> send :my_stock, {:add, {"pumpkin", 5}} 
Enter fullscreen mode Exit fullscreen mode

O que e interessante notar e o que e feito apos a conexao com a aplicacao servidora. E como vamos atualizar o estoque. Voce vai reparar que para chamar um processo em outro app voce so precisa incluir no seu send a seguinte estrutura.

 send {:pid_name, :app_name}, mensagem
Enter fullscreen mode Exit fullscreen mode

E ao chamar N vezes o servico sempre vai para o app servidor conforme a imagem acima.

Lidando com Respostas entre processos

defmodule DistributedStock do
  def handle_sock(stock) do
    receive do
      {:add, from, product} ->
        stock_updated = [product] ++ stock
        send(from, stock_updated)
        handle_sock(stock_updated)

      {:status, from} ->
        send(from, stock)
        handle_sock(stock)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Image description

No exemplo acima, atualizamos o nosso codigo para sempre retornar a resposta para algum processo seja ele o que for. Usamos funcoes como Process.info(pid, :messages) para saber se tem mensagens e flush para ler as mensagens.

Sobre comunicacao de Nodes e Processos

defmodule DistributedStock do
  def handle_sock(stock) do
    receive do
      {:add, from, product} ->
        stock_updated = [product] ++ stock
        log(from, :add)
        send(from, stock_updated)
        handle_sock(stock_updated)

      {:status, from} ->
        log(from, :status)
        send(from, {:show, stock})
        handle_sock(stock)
    end
  end

  defp log(pid, :add) do
    IO.inspect("#{:erlang.pid_to_list(pid)} added a new item on stock")
  end

  defp log(pid, :status) do
    IO.inspect("#{:erlang.pid_to_list(pid)} checked the stock")
  end
end
Enter fullscreen mode Exit fullscreen mode

Image description

Na imagem acima, so aplicamos o que aprendemos durante todo este artigo. O que mudou foi a comunicacao entre os Nodes e processos agora sempre quando um pid chama um servidor voce pode observar que tem um numero como: <19077.122.0> que traduzindo seria: .

Vamos particar um pouco sobre distribuicao.

Imagine que voce tem uma central para estocar os produtos de diversas lojas, e toda a vez que um cliente solicita um produto na loja, as lojas verifica se a central possui o produto, e retorna a mensagem para o cliente. Segue o exemplo abaixo.

defmodule DistributedStock do
  def handle_sock(stock) do
    receive do
      {:add, from, product} ->
        stock_updated = [product] ++ stock
        log(from, :add)
        send(from, stock_updated)
        handle_sock(stock_updated)

      {:status, from} ->
        log(from, :status)
        send(from, stock)
        handle_sock(stock)
    end
  end

  defp log(pid, :add) do
    IO.inspect("#{:erlang.pid_to_list(pid)} added a new item on stock")
  end

  defp log(pid, :status) do
    IO.inspect("#{:erlang.pid_to_list(pid)} checked the stock")
  end
end

Enter fullscreen mode Exit fullscreen mode
defmodule DistributedProviders do
  def get() do
    receive do
      {:ask, from, product} ->
        IO.inspect("#{:erlang.pid_to_list(from)} wants to know if get a product")
        send({:server, :"server@Gustavos-MacBook-Pro"}, {:status, self()})
        return_itens(from, product)
    end

    get()
  end

  def return_itens(from, product) do
    receive do
      products ->
        case Enum.find(products, &(&1.name == product)) do
          nil -> send(from, {:print, "There is no Product #{product}"})
          product -> send(from, {:print, product})
        end
    end
  end

  def show_msg do
    receive do
      {:print, msg} -> msg
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Image description

No exemplo acima, se voce testou o codigo. Voce teve o seguinte resultado:

  • O servidor guarda os produtos (estado do processo)
  • As lojas, alteram o estado (adicionando ou buscando produtos)
  • E clientes consultam os produtos.

Espero ter ajudado, o aprendizado ainda continua e teremos segunda parte deste artigo.

Referencias

https://elixir-lang.org/getting-started/mix-otp/distributed-tasks.html

https://hexdocs.pm/elixir/1.12/Node.html

Top comments (0)