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
iex --name gus@ip #desta maneira voce criar um name
a maior diferenca e o acesso entre internet e local.
Vamos aprender um pouco dos comandos acima:
Node.ping :app_name
Voce vai fazer um ping verificando se e possivel se conectar com o APP
Node.connect :app_name
Voce vai se conectar a um Node.
Node.list
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:
No exemplo acima temos o RPC que segue o seguinte padrao para ser chamado:
:rpc.call app_name, MODULE, :function, [parametros]
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
vamos comecar pelo start da aplicacao:
❯ iex --sname elxpro-server -S mix
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
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}}
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
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
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
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
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
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
Top comments (0)