Antipatterns Akka: muitos atores

imagem

Por akka, existem poucos materiais em Habré. Decidi traduzir alguns dos antipadrões descritos por Manuel em seu blog. Eles realmente podem não ser óbvios para as pessoas que encontraram a estrutura pela primeira vez.

Ocorreu-me que ainda não havia escrito sobre esse antipadrão muito frequente. Muitas vezes, pode ser encontrado no código de desenvolvedores que estão apenas começando a trabalhar com o modelo de ator.

Existem duas maneiras de obter muitos atores:

- tendo desenvolvido um sistema com muitos tipos diferentes de atores, muitos dos quais não são necessários
- criando um número muito grande de atores em tempo de execução quando não é necessário e ineficiente

Vamos examinar essas opções em detalhes.

Muitos tipos de atores


A ideia geral é mais ou menos assim: "nós temos atores, então tudo deve ser um ator".

O modelo de ator simplifica a gravação de aplicativos assíncronos. Ele faz isso fornecendo a ilusão de execução de código síncrono dentro de um ator - não há necessidade de se preocupar com o acesso simultâneo ao estado de um ator, porque apenas um ator pode acessar seu estado e as mensagens são processadas uma por vez.

Mas, de fato, nem tudo precisa ser feito de forma assíncrona. As chamadas de método que são exclusivamente associadas à CPU (e não são "bloqueadas" no sentido de que não sobrecarregam completamente a CPU, por exemplo, calculando o valor Pi), não devem ser executadas de forma assíncrona.

Muitas vezes vejo código com um grande número de atores diferentes interagindo entre si e sem fazer nada, o que tem uma grande vantagem na execução assíncrona ou simultânea. Nesses projetos, o mesmo estado deve ser armazenado por cada um desses atores ou transmitido a eles em cada mensagem.

Essa abordagem tem duas desvantagens:

"Você não recebe nada em termos de desempenho." Pelo contrário, há sobrecarga associada à criação de mensagens e sua transmissão.
- Com cada tipo de ator e mensagens relacionadas, o sistema se torna mais difícil de entender e manter.

Portanto, ao projetar sistemas de atores, você precisa pensar sobre o que realmente deve ser assíncrono, basicamente isso:

- chamadas para sistemas externos (fora da sua jvm)
- chamadas para operações de bloqueio (APIs desatualizadas, computação pesada, ...)

Muitos atores em tempo de execução


A idéia geral é mais ou menos assim: "quanto mais atores tivermos, mais rápido tudo correrá".

Na verdade, os atores são leves e você pode executar milhões deles em uma máquina virtual . Sim você pode. Mas isso é necessário?

imagem

Se você puder, isso não significa que você deveria

Resposta curta: nem sempre - depende do que você faz com os atores.

Se o seu sistema tiver muitos atores de longa duração, cada um contendo um pequeno estado, e interagir de vez em quando, é possível que você esteja com um milhão de atores - e este é um caso de uso legítimo, muito bem suportado pela Akka. Por exemplo, você pode criar um sistema com um grande número de usuários, onde cada usuário é representado por um ator. Um ator Akka puro ocupa apenas 300 bytes de memória; portanto, é possível criar milhões em uma máquina e deixá-los trabalhando sem se preocupar com nada. E se, no final, você criar muitos atores ou atores com uma grande fortuna que eles não caberão mais na memória de uma máquina, o sharding de cluster simplificará a distribuição de atores em várias máquinas.

No entanto, se você possui vários tipos de atores envolvidos na computação de alguma coisa - por exemplo, na análise de um documento XML - é duvidoso criar milhões desses atores (isso não importa diretamente ou através de um roteador).

O processador tem um número fixo de núcleos (threads de hardware) à sua disposição, e os atores Akka processam mensagens em um ExecutionContext com base em um pool de threads. Por padrão, este é o fork-join-executor baseado no ForkJoinPool adicionado no Java 7.

Mas, apesar de sua vantagem técnica, o forkjoinpool não é mágico que revoga as leis da física. Se você tiver um milhão de atores, cada um dos quais analisa um documento XML (já carregado na memória) e 4 threads de hardware, o sistema não funcionará muito melhor do que se você tivesse apenas 4 atores que analisam esses documentos XML (quando condição de carga homogênea). De fato, seu sistema funcionará muito melhor com 4 atores, porque haverá apenas uma sobrecarga mínima em termos de planejamento e gerenciamento de memória. A propósito, se seu sistema tiver apenas alguns atores, verifique seu pool de encadeamentos, que provavelmente está tentando reutilizar o mesmo encadeamento para o mesmo ator.

Em geral, o sistema não funcionará mais rápido se você criar muitos atores.

Atores apátridas


Os atores são orientados a objetos corretamente (diferentemente dos objetos em Java): seu estado não é visível do exterior e eles se comunicam por meio de mensagens. É impossível quebrar o encapsulamento, porque você não pode olhar para o estado do ator durante seu trabalho. Esse é o ponto principal dos atores: eles fornecem a ilusão de um espaço seguro no qual as mensagens são executadas seqüencialmente, uma após a outra, permitindo que você use um estado mutável dentro do ator sem se preocupar com as condições da corrida (condições da corrida - aprox. Por.) (Bem, quase sem se preocupar: o principal é não deixar vazar o estado).

É por isso que o uso de atores sem estado é um tanto estranho, para dizer o mínimo. Com exceção dos atores que controlam a observação de grandes partes da hierarquia do sistema de atores (por exemplo, configurando supervisores de backup), os atores são realmente projetados para trabalhar com cálculos longos que possuem estado. Por muito tempo, quero dizer que o ator processará várias mensagens ao longo de sua vida, produzindo resultados diferentes dependendo de sua condição, em oposição a cálculos únicos. Para eles, os futuros são uma grande abstração: eles permitem a execução assíncrona de código, ideal para casos de trabalho com uma rede ou computação relacionada ao disco (ou tarefas realmente intensas do processador), podem ser compostas e possuem um mecanismo de processamento de falhas. Eles também se integram bem aos atores Akka (usando o padrão de tubos).

Em geral: não use atores se você não tiver um estado - eles não se destinam a isso.

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


All Articles