Actor Model, modelo do ator em programação paralela! Você conhece?
O Actor Model é um modelo matemático para sistemas concorrentes em ciência da computação. Ou em outras palavras: o modelo de ator fornece regras e primitivas para lidar com o paralelismo.
Actor Model é um modelo conceitual de computação concorrente, surgiu em 1973.
As regras, que normalmente são impostas pela linguagem, são as seguintes:
- Tudo (não primitivo) é um ator
- Um ator tem estado local (“variáveis”)
- Um ator é uma entidade computacional (“processo”) que executa sequencialmente (“single-threaded”)
- Um ator tem um endereço (no sentido de caixa de correio, não de memória)
- Um ator pode receber mensagens e reagir a elas por:
- Mutando seu estado local
- Criando novos atores
- Envio de mensagens para outros atores de forma assíncrona usando seus endereços
Embora cada ator seja executado sequencialmente, ou seja, apenas uma mensagem é processada por um ator por vez, dois atores podem muito bem ser executados em paralelo.
As implementações mais famosas para o Actor Model são Akka & Erlang. Falaramos mais de Erlang.
Os atores são leves e é muito fácil criar milhões deles, pois consomem menos recursos do que os Threads.
O estado privado do ator só pode ser alterado pelo processamento de uma mensagem, ele pode manipular apenas uma mensagem por vez e eles funcionam de forma assíncrona, isso significa que um ator nunca espera por uma resposta de outro ator.
No cenário distribuído, é possível que os Atores estejam em máquinas diferentes, portanto, eles se comunicam com endereços com mensagens, um endereço pode ser um endereço local ou um endereço remoto.
A linguagem Erlang, construída por Joe Armstrong na Ericson, ainda é usada hoje para sistemas extremamente escaláveis e tolerantes a falhas.
Por exemplo, os backbones de sua conexão LTE são escritos em Erlang e também são usados para produtos como WhatsApp ou usados por empresas como Facebook e Amazon.
Actor Model não precisa de nenhuma primitiva de sincronização como mutexes ou semáforos. Nenhum ator pode modificar o estado local de outro ator e cada ator em si é **single-threaded. **
Qualquer recurso exigido por vários atores deve ter seu próprio ator designado que gerencia o acesso a esse recurso. Todos os outros atores podem solicitar operações a serem executadas enviando uma mensagem ao ator gestor.
"Um ator é a unidade primitiva de computação. É aquilo que recebe uma mensagem e faz algum tipo de cálculo com base nela."
Basicamente como Alan Kays definia OOP.
A ideia é muito parecida com a que temos nas linguagens orientadas a objetos: um objeto recebe uma mensagem (uma chamada de método) e faz algo dependendo de qual mensagem ele recebe (qual método estamos chamando).
A principal diferença é que os **atores estão completamente isolados **uns dos outros e nunca compartilharão a memória. Também vale a pena notar que um ator pode manter um estado privado que nunca pode ser alterado diretamente por outro ator.
Uma diferença importante entre passar mensagens e chamar métodos é que as mensagens não têm valor de retorno. Ao enviar uma mensagem, um ator delega trabalho a outro ator, se esperasse um valor de retorno, o ator remetente precisaria bloquear ou executar o trabalho do outro ator no mesmo thread. Em vez disso, o ator receptor entrega os resultados em uma mensagem de resposta.
Os atores reagem às mensagens da mesma forma que os objetos “reagem” aos métodos invocados neles. A diferença é que, em vez de vários encadeamentos “projetando” em nosso ator e causando estragos no estado interno e invariantes, os atores executam independentemente dos remetentes de uma mensagem e reagem às mensagens recebidas sequencialmente, uma de cada vez. Enquanto cada ator processa as mensagens enviadas a ele sequencialmente, diferentes atores trabalham simultaneamente uns com os outros para que um sistema de ator possa processar tantas mensagens simultaneamente quantas o hardware suportar.
No modelo de ator tudo é um ator e eles precisam ter endereços para que um ator possa enviar uma mensagem para outro.
Atores têm caixas de correio
É importante entender que, embora vários atores possam ser executados ao mesmo tempo, um ator processará uma determinada mensagem sequencialmente. Isso significa que se você enviar 3 mensagens para o mesmo ator, ele executará apenas uma de cada vez. Para que essas 3 mensagens sejam executadas simultaneamente, você precisa criar 3 atores e enviar uma mensagem para cada um.
As mensagens são enviadas de forma assíncrona para um ator, que precisa armazená-las em algum lugar enquanto processa outra mensagem. A caixa de correio é o local onde essas mensagens são armazenadas.
Os atores se comunicam entre si enviando mensagens assíncronas. Essas mensagens são armazenadas nas caixas de correio de outros atores até serem processadas.
O uso de passagem de mensagens evita travamentos e bloqueios.
Em vez de chamar métodos(oop), os atores enviam mensagens uns aos outros. O envio de uma mensagem não transfere o thread de execução do remetente para o destino. Um ator pode enviar uma mensagem e continuar sem bloquear. Portanto, ele pode realizar mais na mesma quantidade de tempo.
O que os atores fazem
Quando um ator recebe uma mensagem, ele pode fazer uma destas 3 coisas:
- Criar mais atores
- Enviar mensagens para outros atores
- Designar o que fazer com a próxima mensagem
Um ator pode manter um estado privado.
“Designar o que fazer com a próxima mensagem”
significa basicamente definir como será esse estado para a próxima mensagem que receber. Ou, mais claramente, é como os atores mudam de estado.
Vamos imaginar que temos um ator que se comporta como uma calculadora e que seu estado inicial é simplesmente o número 0. Quando este ator recebe a mensagem add(1), ao invés de mudar seu estado original, ele designa que para a próxima mensagem que receber, o estado será 1.
Tolerância a erro
Erlang introduziu a filosofia **“deixe travar”. **A ideia é que você não precise programar defensivamente, tentando antecipar todos os possíveis problemas que podem acontecer e encontrar uma maneira de lidar com eles, simplesmente porque não há como pensar em cada ponto de falha.
O que Erlang faz é simplesmente deixá-lo travar, mas fazer com que esse código crítico seja supervisionado por alguém cuja única responsabilidade seja saber o que fazer quando esse travamento acontecer(como redefinir essa unidade de código para um estado estável), e o que torna tudo isso possível é o modelo de ator.
Todo código roda dentro de um process(é basicamente assim que Erlang chama seus atores).
Isse process é completamente isolado, o que significa que seu estado não influenciará nenhum outro process. Temos um supervisor, que é basicamente outro process(tudo é ator, lembra?), que será avisado quando o supervisionado process travar e poderá fazer algo a respeito.
Isso possibilita a criação de sistemas que se “autocuram”, o que significa que se um ator chegar a um estado excepcional e travar, por qualquer motivo, um supervisor pode fazer algo a respeito para tentar colocá-lo em um estado consistente novamente (e há existem várias estratégias para fazer isso, sendo a mais comum apenas reiniciar o ator com seu estado inicial).
Distribuição
Outro aspecto interessante do modelo de ator é que não importa se o ator para o qual estou enviando uma mensagem está sendo executado localmente ou em outro nó.
Pense nisso, se um ator é apenas esta unidade de código com uma caixa de correio e um estado interno, e apenas responde a mensagens, quem se importa em qual máquina ele está realmente rodando? Contanto que possamos fazer a mensagem chegar lá, estamos bem.
Isso nos permite criar sistemas que aproveitam vários computadores e nos ajudam a recuperar se um deles falhar.
Prós do modelo de ator
- Fácil de escalar
- Tolerante a falhas
- Nenhum estado compartilhado
Contras do modelo de ator
- A caixa de correio pode transbordar
- Suscetível a impasses
O Actor Model é bem conceituado e faz todo o sentido usar o Actor Model se alguém estiver projetando sistemas concorrentes.
Fontes:
https://surma.dev/things/actormodel/
https://www.brianstorti.com/the-actor-model/
https://en.wikipedia.org/wiki/Actor_model
https://eximia.co/reactive-messaging-patterns-with-the-actor-model/
https://medium.com/@KtheAgent/actor-model-in-nutshell-d13c0f81c8c7
https://doc.akka.io/docs/akka/current/typed/guide/actors-intro.html
https://getakka.net/articles/intro/what-are-actors.html
https://s3-sa-east-1.amazonaws.com/thedevconf/presentations/TDC2019SP/dotnet/UJJ-3987_2019-07-17T025204_Introdu%C3%A7%C3%A3o%20ao%20Actor%20Model%20com%20Microsoft%20Orleans.pdf