Essa e a maior vantagem de Utilizar Elixir
Elxpro
Posted on June 21, 2022
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
Posted on June 21, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.