Essa e a maior vantagem de Utilizar Elixir
Published: 2022-06-21 21:38:21

# 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](https://youtu.be/Yo5w6E8QxeU)_


# 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.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0vlqqo0lei2c72f2eok.png)


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: 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a46l7zeqf766z35kbqnr.png)



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

```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svjndvi6l4lk122nccoy.png)


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
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sklyi1iwwhntnobit96o.png)


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
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0n2jymi45g5z2wbfvzft.png)


 
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: <NUMERO_DA_CONEXAO_DO_NODE.PID_ORIGEM>.

### 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
```

 ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vq091nkzvtdu284wkkfe.png)
 
 

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