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 ERLANG, 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
Top comments (3)
Muito bom, uma explicação bem simples e didática.
Gostei do artigo e é sempre bom conhecer essas estratégias para ter como opção a ser considerada quando um problema que pode ser resolvido com ela aparecer.
Eu realmente gostei do artigo. É sempre benéfico estar ciente dessas estratégias, pois elas fornecem opções valiosas a serem consideradas sempre que surge um problema que pode ser resolvido de forma eficaz com seu uso. Ter um conjunto diversificado de técnicas de resolução de problemas à sua disposição é crucial tanto em ambientes pessoais quanto profissionais. Isso não apenas aumenta sua capacidade de enfrentar desafios com eficiência, mas também aumenta sua confiança na busca de soluções inovadoras. Os insights fornecidos no artigo não são apenas informativos, mas também práticos, oferecendo uma base sólida para abordar uma ampla gama de questões.