Granularidades de trabalho sem estado

Por padrão, o runtime do Orleans não cria mais do que uma ativação de uma granularidade dentro do cluster. Essa é a expressão mais intuitiva do modelo de Ator Virtual com cada granularidade correspondente a uma entidade com um tipo/identidade exclusivo. No entanto, também há casos em que um aplicativo precisa executar operações funcionais sem estado que não estão vinculadas a uma entidade específica no sistema. Por exemplo, se o cliente enviar solicitações com conteúdos compactados que precisam ser descompactados antes de serem roteados para a granularidade de destino para processamento, essa lógica de descompactação/roteamento não está vinculada a uma entidade específica no aplicativo e pode facilmente expandir-se.

Quando o StatelessWorkerAttribute é aplicado a uma classe de granularidade, indica ao runtime do Orleans que as granularidades dessa classe devem ser tratados como granularidades de trabalho sem estado. As granularidades de trabalho sem estado têm as propriedades a seguir que tornam sua execução muito diferente das classes normais de granularidade.

  1. O runtime do Orleans poderá e criará várias ativações de uma granularidade de trabalho sem estado em diferentes silos do cluster.
  2. As solicitações feitas aos grãos de trabalho sem estado são executadas localmente, desde que o silo seja compatível e, portanto, não incorrerão em custos de rede ou serialização. Se o silo local não for compatível, as solicitações serão encaminhadas para um silo compatível.
  3. O Runtime do Orleans cria automaticamente ativações adicionais de uma granularidade de trabalho sem estado se os já existentes estiverem ocupados. O número máximo de ativações de uma granularidade de trabalho sem estado que o runtime cria por silo é limitado por padrão pelo número de núcleos da CPU no computador, a menos que especificado explicitamente pelo argumento opcional maxLocalWorkers.
  4. Devido a 2 e 3, as ativações de granularidades de trabalho sem estado não são endereçáveis individualmente. Duas solicitações subsequentes para uma granularidade de trabalho sem estado podem ser processadas por ativações diferentes dela.

As granularidades de trabalho sem estado fornecem uma maneira simples de criar um pool gerenciado automaticamente de ativações de granularidades que aumenta e reduz automaticamente com base na carga real. O runtime sempre verifica se há ativações de granularidade de trabalho sem estado disponíveis na mesma ordem. Por isso, ele sempre envia solicitações para a primeira ativação local ociosa que pode encontrar e só chega à última se todas as ativações anteriores estiverem ocupadas. Se todas as ativações estiverem ocupadas e o limite de ativação não tiver sido atingido, ele criará mais uma ativação no final da lista e enviará a solicitação para ela. Isso significa que, quando a taxa de solicitações para uma granularidade de trabalho sem estado aumenta e as ativações existentes estão ocupadas no momento, o runtime expande o pool de suas ativações até o limite. Por outro lado, quando a carga diminui e pode ser tratada por um número menor de ativações da granularidade de trabalho sem estado, as ativações na parte final da lista não serão enviadas para eles. Eles ficarão ociosos e, eventualmente, desativados pelo processo de coleta de ativação padrão. Portanto, o pool de ativações acabará reduzindo para corresponder à carga.

O exemplo a seguir define uma classe MyStatelessWorkerGrain de granularidade de trabalho sem estado com o limite de número de ativação máximo padrão.

[StatelessWorker]
public class MyStatelessWorkerGrain : Grain, IMyStatelessWorkerGrain
{
    // ...
}

Fazer uma chamada para uma granularidade de trabalho sem estado é o mesmo que qualquer outra granularidade. A única diferença é que, na maioria dos casos, é usada uma única ID de grão, por exemplo, 0 ou Guid.Empty. Várias IDs de granularidade podem ser usadas ao ter vários pools de granularidade de trabalho sem estado, um por ID é desejável.

var worker = GrainFactory.GetGrain<IMyStatelessWorkerGrain>(0);
await worker.Process(args);

Isso define uma classe de granularidade de trabalho sem estado com não mais do que uma ativação de uma granularidade por silo.

[StatelessWorker(1)] // max 1 activation per silo
public class MyLonelyWorkerGrain : ILonelyWorkerGrain
{
    //...
}

Observe que StatelessWorkerAttribute não altera a reentrância da classe de granularidade de destino. Assim como qualquer outra granularidade, as granularidade de trabalho sem estado são não reentrantes por padrão. Elas podem ser explicitamente feito reentrantes adicionando uma ReentrantAttribute à classe de granularidade.

Estado

A parte “sem estado” do “trabalho sem estado” não significa que um trabalho sem estado não possa ter um estado e esteja limitado apenas à execução de operações funcionais. Como qualquer outra granularidade, uma granularidade de trabalho sem estado pode carregar e manter na memória qualquer estado necessário. Isso ocorre porque várias ativações de uma granularidade de trabalho sem estado podem ser criadas nos mesmos e diferentes silos do cluster, não há mecanismo fácil para coordenar o estado mantido por ativações diferentes.

Vários padrões úteis envolvem o estado de retenção de trabalho sem estado.

Itens de cache frequentes escalonados

Para itens de cache frequentes que experimentam alta taxa de transferência, manter cada item desse tipo em uma granularidade de trabalho sem estado faz com que ele:

  1. Escale horizontalmente e automaticamente dentro de um silo e em todos os silos no cluster e;
  2. Torna os dados sempre disponíveis localmente no silo que recebeu a solicitação do cliente por meio do seu gateway de cliente, para que as solicitações possam ser respondidas sem um salto de rede extra para outro silo.

Reduzir agregação de estilo

Em alguns cenários, os aplicativos precisam calcular determinadas métricas em todas as granularidades de um tipo específico no cluster e relatar as agregações periodicamente. Exemplos são relatar vários jogadores por mapa de jogo, a duração média de uma chamada VoIP, etc. Se cada um dos milhares ou milhões de granularidades relatasse suas métricas para um único agregador global, o agregador ficaria imediatamente sobrecarregado sem poder processar a enxurrada de relatórios. A abordagem alternativa é transformar essa tarefa em uma etapa 2 (ou mais) para reduzir a agregação de estilo. A primeira camada de agregação é feita relatando granularidades enviando suas métricas para uma granularidade de pré-agregação de trabalho sem estado. O runtime do Orleans criará automaticamente várias ativações da granularidade de trabalho sem estado com cada silo. Como todas essas chamadas serão processadas localmente sem chamadas remotas ou serialização das mensagens, o custo dessa agregação será significativamente menor do que em um caso remoto. Agora, cada uma das ativações de granularidade de trabalho sem estado de pré-agregação, independentemente ou em coordenação com outras ativações locais, pode enviar seus relatórios agregados para o agregador final global (ou para outra camada de redução, se necessário) sem sobrecarregá-lo.