Minha visão sobre o Orleans – o framework de atores da Microsoft

Vou tentar neste artigo passar uma visão básica do Microsoft Orleans, o framework que permite a criação de aplicações distribuídas utilizado pela Microsoft, ele fornece uma abordagem direta para a construção de aplicativos de grande escala sem a necessidade e aplicar ou aprender uma concorrência complexa.

Ainda parece ser um framework não muito utilizado no mercado mas sua versatilidade pode ser vista em grande jogos da Microsoft como a serie do Halo e até no Age of Empires e em projetos como Azure PlayFab, Xbox Game Studios entre outros.

Microsoft Orlens é utilizado em vários projetos da Microsoft, vale a pena dar uma olhada!

O Orlens usa o conceito de atores e uma arquitetura stateful, mas porque deixar a abordagem mais usual ou a arquitetura mais clássica de três camadas e olhar para o Orlens mudando a abordagem para uma arquitetura baseada em atores? Podemos apontar alguns “problemas” na Arquitetura Stateless:

  • Como podemos fornecer dados em tempo real ou em quase tempo real?
  • como lidar com milhares de Requests simultâneos?
  • Problemas de concorrência;
  • Problemas de sincronização em uma camada de cache;
  • Alto custo com banco de dados;
  • Limitado por desempenho e escalabilidade da camada de armazenamento;
  • Ter que utilizar técnicas como CQRS para conseguir mais desempenho e que acabam elevando a complexibilidade de código;
  • Para escalar precisa colocar mais servidores, colocar mais memória, etc;

Vamos “guardar esses problemas” e agora entender como funciona os principais componentes do Orlens.

O Orlens é um “Actor-based framework” ou um framework baseado em atores o que significa que cada ator tem a sua responsabilidade, os atores no Orlens recebem o nome de “Grain“, um grain ou “Grão” no nosso idioma representa um usuário, sessão, estoque, produto ou pedido enfim qualquer classe comum. Basicamente um grão é uma classe .NET simples que implementa uma interface do Orleans.

Um grão deve possuir um identificador único, ele pode ter seu estado persistido em um sistema de armazenamento (banco de dados), em memória ou muitas vezes em ambos. Dentro do Orleans um grão pode chamar outro grão e efetuar operações.

O grão basicamente é um objeto distribuído composto por três coisas: a identidade, o comportamento e o estado conforme mostra a figura ao lado:

fonte: https://dotnet.github.io/orleans/docs/index.html

O grão assim como um controller MVC, ou uma classe do Angular possui um ciclo de vida (LifeCycle) que permite ao desenvolvedor subscrever seus métodos:

https://dotnet.github.io/orleans/docs/index.html
  • A ativação ocorre em uma chamada qualquer para o grão;
  • Estando ativos na memoria os grãos podem modificar seu estado;
  • A desativação ocorre quando um determinado grão não é chamado por um tempo para eficiência de consumo de recursos;
  • No final depois de desativado se ele possuir um estado ele será persistido.

Sabendo-se o que é um grão precisamos agora entender o conceito de SILO que nada mais é que a camada onde os grãos serão hospedados. O Silo pode ser uma aplicação .NET como um “console application”, ele é responsável por hospedar e executar os grãos. Um Silo pode conter um ou n grãos. Pode ser executado de forma autônoma em um contêiner, no Service Fabric ou em um cluster kubernets.

Os Silos possuem mecanismos como por exemplo de garbage collector onde caso um grão fique muito tempo inativo, ele é removido da memoria e seu estado e salvo no banco.

Podemos em uma aplicação Orleans ter mais de um SILO, o que forma um CLUSTER.

Um Cluster pode ser composto por dois ou mais Silos, em caso de falha em algum silo o framework consegue aplicar medidas de segurança para detectar todos os grãos que estavam no silo com problema e reativa-los em outro silo de forma transparente para quem utiliza.

Com o entendimento dos conceitos de grão e silo podemos ver abaixo o desenho simples da arquitetura:

Onde a camada de front end pode acionar qualquer grão da camada intermediaria, e os grãos possuem e guardam seu estado indo ao repositório somente quando for necessário, formando o que conhecemos como “Arquitetura Stateful“. Podemos citar algumas vantagens:

  • Facilidade para escalar, visto que o escalonamento será horizontal simplesmente adicionando mais maquinas ao cluster;
  • Redução de I/O no banco de dados;
  • Tolerância a falhas;

Use esse estudo para comparar com a “Arquitetura Stateless” apresentada no inicio do artigo.

Eu penso que toda arquitetura tem suas vantagens e desvantagens e cabe a você decidir se vai utilizar em seus projetos ou não, o intuito deste artigo não é defender uma ou outra abordagem e sim deixar mais uma “carta” em sua manga. Deixei um código no meu github para exemplificar o uso do Orlens, não vou explicar aqui o código linha por linha, mas peço que baixe e tente rodar o mesmo em sua maquina, havendo duvidas entre em contato comigo.

Prova de Conceito

Basicamente vamos ter dentro de uma solução uma API, o projeto de interfaces ou contratos, um projeto contendo o grão de exemplo e dois projetos que serão os silos de nosso exemplo. A “solution” pode ser vista abaixo:

O grão de exemplo é bem simples e representa um “Produto” onde podemos setar e recuperar seu estoque:

namespace DemoOrleans.Grains
{
    [StorageProvider(ProviderName = "Products")]
    public class ProductGrain : Grain<Product>, IProduct
    {
        public Task<int> GetStock()
        {
            //Forma de chamar outro grão (como um ator chama o outro)
            //var otherProduct = GrainFactory.GetGrain<IProduct>(2);
            return Task.FromResult(State.QuantityInStock);
        }

        public async Task SetStock(int quantity)
        {
            State.QuantityInStock = quantity;
            await WriteStateAsync();
        }
    }
}

A ideia é subir a API e fazer as chamadas aos métodos e observar pelo “dashboard do Orleans” onde os grãos vão sendo armazenados, você pode usar ali um JMETER para efetuar uma carga de acessos e pode também derrubar um Silo para ver a resiliência do cluster em funcionamento.

Depois de baixar o projeto, faça o “rebuild” para atualizar os pacotes e configure os projetos da API e os dois consoles para rodarem juntos conforme imagem ao lado:

Precisaremos para rodar este exemplo ter um banco de dados SQL SERVER, eu utilizei o localDB, caso não tenha em sua maquina instale pelo próprio instalador do Visual Studio (tem vários artigos na internet mostrando como fazer). Com o banco rodando, será necessário criar o banco de dados e depois executar alguns scripts para gerar as tabelas de controle do Orleans. Estes scripts podem ser encontrados neste link.

Depois de aplicar os scripts, rode a aplicação, se tudo correr bem vc deverá ter a API rodando e duas instancias de SILO também em pé na sua maquina. Acesse o link do dashboard do Orleans no endereço: http://192.168.1.8:8081/ (pode ser http://localhost:8081), o dashboard sobre por default na porta 8080 mas configurei para subir na 8081 devido a ter aplicação na minha maquina já ouvindo na porta 8080.

Faça operações pela interface do swagger e verifique como se porta o dashboard do Orleans, pode também efetuar queries no SQL SERVER para entender o funcionamento.

Novamente o código fonte se encontra no meu github. Havendo dúvidas entre em contato por aqui ou pelo e-mail que pode ser encontrado aqui na minha página logo ali abaixo do “Sobre Mim”. Ah procurei comentar no código tudo que achei importante, então imagino que vai ser tranquilo o entendimento.

Minha intenção foi mostrar um pouco desta ferramenta, tem pouco material na internet principalmente em português, se por acaso te ajudou comenta ai, compartilha se possível com seus amigos desenvolvedores, opiniões sobre o conteúdo positivas ou negativas são bem vindas!

Até o próximo artigo!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *