Fazia tempo que estava para blogar algo sobre como é fácil fazer programação distribuída com Erlang. Derrepente, hoje, acabei perdendo o sono e eis aqui o post.
O mais simples do mundo
Pra começar, vamos ver o exemplo mais simples de computação distribuída que se pode fazer e aprender alguma coisa com ele. Ele é como um simples chat. Pois muito bem, vejamos!
Fazendo o setup
No terminal, inicie um Eshell com o seguinte comando:
$ erl -sname jose
E num outro terminal, inicie outro Eshell com:
$ erl -sname maria
Agora vá no primeiro terminal, no Eshell do José, e execute o seguinte comando:
(jose@codezone)1> register(console_jose, self()).
E no segundo terminal, no Eshell da Maria, faça o mesmo:
(maria@codezone)1> register(console_maria, self()).
Tudo bem, setup feito, hora do bate-papo!
Jogando conversa fora
“Ei José, diga oi…”
(jose@codezone)2> {console_maria, 'maria@codezone'} ! "Oi Maria, tudo bem?".
Veja o terminal da Maria. O que aconteceu? Nada? A mensagem não chegou? Chegou, chegou sim. Vejamos:
(maria@codezone)2> regs().
Na primeira coluna, procure pelo processo console_maria. Achou? Agora veja na última coluna desse processo. Que número tem aí? 1? Pois é, isso nos indica uma coisa. Ainda nesse Eshell, execute o seguinte comando:
(maria@codezone)3> flush().
Opa! E agora? Shell got “Oi Maria, tudo bem?”, certo? Taí a mensagem que a Maria recebeu do José.
Agora, que tal a Maria ser gentil com o José e responde-lo cordialmente?
(maria@codezone)4> {console_jose, 'jose@codezone'} ! "Vou bem, e voce?".
Se você quiser, pode ver o registro de processos no Eshell do José, como fez anteriormente com o da Maria. Ou pode simplesmente:
(jose@codezone)3> flush().
E receber a mensagem Shell got “Vou bem, e voce?”.
Entendendo tudo isso
Por sorte, não há muito o que explicar até aqui — já que fizemos o exemplo mais simples do mundo. Uff!
Que tal começar do começo? Vamos entender os passos de setup?
1- A primeira coisa que fizemos foi iniciar dois Eshell que, na verdade, tornaram-se dois nós do nosso cluster de runtimes Erlang.
Todas as vezes que você inicia um Eshell com a flag -sname, o que você está fazendo é criar um nó de runtime Erlang na sua rede. Na verdade, isso pode ser feito também com a flag -name.
Qual a diferença entre elas?
-sname cria o nó e dá a ele um “short name”: nome_do_no@host;
-name, pelo contrário, cria o nó dá a ele um “long name”: nome_do_no@ip_address ou nome_do_no@host.local, no caso de rede local.
Você pode perfeitamente iniciar diversos nós em uma mesma máquina, como fizemos a pouco. No entanto, você não pode duplicar os nomes dos nós, uma vez que estes são usados para identificá-los no cluster e torná-los comunicáveis uns com os outros.
“Lembrando que o nome dos nós são sempre sufixados pelo nome/ip do host, não há problema algum em ter o mesmo nome de nó em máquinas diferentes.”
Uma outra coisa interessante de aprender aqui é como os nós se comunicam. Toda comunicação entre nós é feita por um daemon chamado epmd. Este programa faz parte do sistema de runtime Erlang e tem um papel fundamental em sua arquitetura de clusters, porque sempre que “o primeiro nó” é iniciado numa máquina, automaticamente, ele também é iniciado, caso ainda não tenha sido, e passa a escutar mensagens na porta 4369 e rotea-las para o nó apropriado.
“Independente de quantos nós estejam rodando numa máquina, sempre haverá apenas uma instância do epmd rodando.”
Por fim, ainda sobre o setup dos nós do cluster, há uma outra flag que não demonstrei no exemplo acima, mas que é bem interessante e você depois pode pesquisar a respeito.
-setcookie define um “secret cookie” que serve como um tipo de token para autenticar a comunicação entre os nós do cluster. Isso quer dizer que, somente os nós iniciados com um mesmo cookie podem se comunicar. Por outro lado, sempre que um nó é iniciado sem especificar um cookie, o runtime Erlang toma a liberdade de cria um e gravar no arquivo ~/.erlang.cookie. Nestes casos, todos os nós não iniciados com um cookie compartilharão o mesmo cookie guardado nesse arquivo.
No caso do nosso exemplo ultra simples, um cookie poderia ser usado para criar uma sala de bate-papo, onde: se um nós é iniciado informando um cookie, ele entra na sala equivalente; no entanto, quando não informado, vai para uma sala genérica.
Interessante? Pois é… E esse assunto acaba puxando o da flag -hidden, que você pode aproveitar e pesquisar também.
2- A segunda coisa que fizemos no setup foi registrar um processo alvo de nossas mensagens.
Como a idéia aqui é explicar — um pouco, porque ainda há uma porção de coisas que poderiam ser faladas, mas isso deixaria esse post imenso! Dê uma olhada na documentação oficial — como funciona a arquitetura de cluster de runtimes Erlang e mostrar como este provê uma maneira simples de programação distribuída, preferi não escrever um programa de chat, mas apenas usar o próprio Eshell para isso.
Com este pré-requisito em mente, o que fiz foi apenas registrar o próprio processo do Eshell — usando self(), que você entende como funciona — com um nome especifico em cada nó do cluster (console_jose para um e console_maria em o outro) para fazer a troca de mensagens entre eles.
E o bate-papo, como é que rolou?
Essa é a parte mais fácil da coisa, na minha opinião. Se você já lidou com processos em Erlang, vai tirar de letra.
1- Trocamos mensagens entre os dois processos de maneira bem semelhante ao que fazemos normalmente com processos contidos num único runtime.
A única diferença aqui é que, em vez de simplesmente codarmos algo como console_jose ! “Show de bola”, codamos {console_jose, ‘jose@codezone’} ! “Show de bola”. Ou seja, além no nome do processo, informamos também em que nó do cluster ele está, tudo em uma tupla.
Um exercício legal de você fazer é o seguinte…
a. No Eshell da Maria execute:
(maria@codezone)5> console_maria ! "Show de bola".
(maria@codezone)6> flush().
b. Depois, ainda no Eshell dela, execute:
(maria@codezone)7> console_jose ! "Show de bola".
O que aconteceu? Na primeira execução, o Eshell da Maria recebeu uma mensagem, correto? Mas na segunda execução, o Eshell “crasheou”. Por quê? Porque ele não reconheceu o átomo console_jose como sendo o nome de um processo, uma vez que o processo registrado como console_jose está em outro nó do cluster. Aliás, como o Eshell da Maria crasheou, se você tentar enviar mensagens a ele novamente, como fez anteriormente e havia funcionado, ele vai crashear novamente, porque a referência ao processo que havia sido registrada tornou-se inválida.
Você precisa ficar muito atento a isso, porque quando você manda um mensagem para um processo que está em outro nó, mesmo que ele não exista, você não receberá erro — exatamente como acontece com envio de mensagens pra processos locais inexistentes.
Quer ver? Faça o seguinte teste:
(maria@codezone)8> {console_jose_123, 'jose_abc@codezone_xyz'} ! "Nao vai crashear".
E agora, o que aconteceu? Nada!
2- Outra coisa que fizemos foi usar os truques de flush() e regs(), mas esses já são bem conhecidos.
Se você costuma usar o Eshell, com certeza, já deve ter usado flush() e regs(). Caso ainda não tenha usado, aqui vai um resumo:
– flush() descarrega as mensagens contidas na caixa postal do Eshell (que como você sabe, nada mais é do que um processo Erlang qualquer);
– regs() lista os processos registrados do runtime em execução.
Só o gostinho
Como disse em algum canto nesse post, ainda há bastante o que falar sobre programação distribuída com Erlang. Só pra variar, esse post foi só pra dar o gostinho. Ainda há muito mais!
Bem, quem sabe qualquer dia desses não perco o sono novamente e escrevo um pouco mais sobre o assunto. Quem sabe?