Mensagens -> PubSub dentro do OTP

OTP significa Open Telecom Platform ; isso aconteceu historicamente, porque a plataforma foi criada para as necessidades e para o dinheiro da Ericsson . Mas, em princípio, esse nome tem tantas conotações com sua funcionalidade quanto maçãs com telefones de qualidade média.


A principal característica distintiva do OTP, de acordo com os autores, é a tolerância a falhas. Sem multithreading, sem modelo de ator, sem recursos avançados de correspondência de padrões, nem mesmo com cluster transparente e atualizações de códigos quentes. Tolerância a falhas.


A máquina virtual de Erlang é superficialmente projetada com muita simplicidade: há um monte de "processos" (não processos do sistema, processos erlang) com memória isolada que pode trocar mensagens. Só isso. Aqui está o que Joe Armstrong disse sobre isso:


No meu blog, argumentei que os processos deveriam se comportar da mesma maneira que as pessoas. As pessoas têm memórias particulares e trocam dados transmitindo mensagens.
- Por que não gosto de memória compartilhada

As mensagens no OTP são muito simples: um processo envia uma mensagem para outro (ou um grupo de outros processos), de forma síncrona ou assíncrona. Mas, para isso, você precisa saber a quem enviar essas mensagens. Ou seja, o remetente é o gerente de troca. Mas e se quisermos apenas enviar transmissão e permitir que todos os processos interessados ​​se inscrevam nessa mensagem?


Sim, este é um PubSub comum, mas pronto para uso no OTP não está implementado. Bem, não importa, temos todos os tijolos em uma hora para trazê-lo de joelhos. Vamos começar.


Opções de implementação


Basicamente, o Elixir inclui um módulo de Registry que pode ser usado como um andaime para o pubsub . Um pouco de código caseiro, uma aparência cuidada de todos os participantes (um supervisor para todos) e pronto. O único problema é que o Registry local e não sabe como agrupar. Ou seja, em um ambiente distribuído (nós distribuídos) essa beleza não funcionará.


Phoenix.PubSub nós, existe uma implementação distribuída do Phoenix.PubSub que vem com duas implementações Phoenix.PubSub.PG2 : Phoenix.PubSub.PG2 e Phoenix.PubSub.Redis . Bem, Redis é claramente um elo extra em nossa cadeia, mas o PG2 , trabalhando no topo dos grupos Erlang dos processos da pg2 , é isso. Além disso, no entanto, sem um clichê não serve.


Portanto, temos tudo para estabelecer assinaturas convenientes do PubSub em nosso aplicativo. Está na hora de abrir um editor de texto? "Na verdade não." Não gosto de duplicar código de projeto para projeto, e tudo o que posso isolar em uma biblioteca é isolado para reutilização.


Envío


Assim nasceu o pacote Envío. Como as conversas, como você sabe, não valem um centavo, começaremos com exemplos de uso.


Boletim local → Registry


 defmodule MyApp.Sub do use Envio.Subscriber, channels: [{MyApp.Pub, :main}] def handle_envio(message, state) do # optionally call the default implementation {:noreply, state} = super(message, state) # handle it! IO.inspect({message, state}, label: "Received") # respond with `{:noreply, state}` as by contract {:noreply, state} end end 

Isso, em geral, é tudo. Resta MyApp.Sub para a nossa árvore de supervisor, e esse processo começará a receber todas as mensagens enviadas usando as funções do MyApp.Pub , que também não está sobrecarregado com código.


 defmodule MyApp.Pub do use Envio.Publisher, channel: :main def publish(channel, what), do: broadcast(channel, what) def publish(what), do: broadcast(what) # send to :main end 

Boletim Distribuído → PG2


Para sistemas distribuídos constituídos por muitos nós, esse método não funcionará. Precisamos ser capazes de assinar mensagens de outros nós, e o Registry não Registry um assistente aqui. Mas há o PG2 que implementa o mesmo behaviour .


 defmodule Pg2Sucker do use Envio.Subscriber, channels: ["main"], manager: :phoenix_pub_sub def handle_envio(message, state) do {:noreply, state} = super(message, state) IO.inspect({message, state}, label: "Received") {:noreply, state} end end 

A única diferença do código independente acima é o parâmetro manager: :phoenix_pub_sub , que passamos para use Envio.Subscriber (e use Envio.Publisher ) para criar um módulo baseado em :pg2 vez do Registry local. Agora, as mensagens enviadas usando este Publisher estarão disponíveis em todos os nós no cluster.


Aplicação


Envío suporta os chamados back - end . Envio.Slack vem com a Envio.Slack , o que permite simplificar o envio de mensagens para o Slack . Tudo o que é necessário do aplicativo - envie uma mensagem para o canal configurado no config/prod.exs - o config/prod.exs fará o Envío . Aqui está um exemplo de configuração:


 config :envio, :backends, %{ Envio.Slack => %{ {MyApp.Pub, :slack} => [ hook_url: {:system, "SLACK_ENVIO_HOOK_URL"} ] } } 

Agora todas as mensagens enviadas chamando MyApp.Pub.publish(:slack, %{foo: :bar}) serão entregues no canal correspondente no Slack , lindamente formatado. Para parar de enviar mensagens para o Slack , basta parar o processo Envio.Slack . Mais exemplos (por exemplo, um log IO ) podem ser encontrados em testes.


Por que estou crucificando, tente você mesmo.


 def deps do [ {:envio, "~> 0.8"} ] end 

Boa comunicação!

Source: https://habr.com/ru/post/pt481460/


All Articles