Compartilhar via


Identidade de grãos

Os grãos em Orleans têm um identificador único, exclusivo e definido pelo usuário, que consiste em duas partes:

  1. Nome do tipo de grão, identificando exclusivamente a classe de grão.
  2. A chave do grão, que identifica exclusivamente uma instância lógica dessa classe de grãos.

Orleans representa o tipo de grão e a chave como cadeias de caracteres legíveis por humanos. Por convenção, escreva a identificação do grão com o tipo de grão e a chave separados pelo caractere /. Por exemplo, shoppingcart/bob65 representa o tipo de grão nomeado shoppingcart com a chave bob65.

Construir identidades de grãos diretamente é incomum. Em vez disso, é mais comum criar referências a grãos usando Orleans.IGrainFactory.

As seções a seguir discutem nomes de tipo de grão e chaves de grãos com mais detalhes.

Nomes dos tipos de grão

Orleans cria um nome de tipo de grão com base na classe de implementação de grãos. Ele remove o sufixo "Grain" do nome da classe, se presente, e converte a cadeia de caracteres resultante em sua representação minúscula. Por exemplo, uma classe chamada ShoppingCartGrain recebe o nome do tipo de grão shoppingcart. É recomendável que os nomes e as chaves de tipo de grão consistam apenas em caracteres imprimíveis, como caracteres alfanuméricos (a-z, A-Z, e 0-9) e símbolos como -, _, @, e =. Outros caracteres podem não ter suporte e geralmente exigem tratamento especial quando impressos em logs ou aparecem como identificadores em outros sistemas, como bancos de dados.

Como alternativa, use o Orleans.GrainTypeAttribute atributo para personalizar o nome do tipo de grão para a classe de grão à qual ele está anexado, conforme mostrado no exemplo a seguir:

[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
    // Add your grain implementation here
}

No exemplo anterior, a classe ShoppingCartGrain de grãos tem o nome cartdo tipo de grão. Cada grão só pode ter um nome de tipo de grão.

Para grãos genéricos, inclua a aridade genérica no nome do tipo de grão. Por exemplo, considere a seguinte DictionaryGrain<K, V> classe:

[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
    // Add your grain implementation here
}

A classe grain tem dois parâmetros genéricos, portanto, um backtick ` seguido pela aridade genérica, 2, é adicionado ao final do nome dict do tipo de grão para criar o nome dict`2 do tipo de grão. Isso é especificado no atributo na classe granulada: [GrainType("dict2")]'.

Chaves de grão

Para conveniência, Orleans expõe métodos que permitem a construção de identificadores de grãos a partir de um Guid ou Int64, além de um String. A chave primária está limitada ao tipo de grão. Portanto, a identidade completa de um grão é formada pelo seu tipo e chave.

O chamador do grão decide qual esquema usar. As opções são:

Como os dados subjacentes são os mesmos, os esquemas podem ser usados de forma intercambiável: todos eles são codificados como cadeias de caracteres.

Situações que exigem uma instância de grão singleton podem usar um valor fixo conhecido, como "default". Esta é apenas uma convenção, mas aderir a ela esclarece no site do chamador que um grão singleton está em uso.

Usando GUIDs (identificadores globalmente exclusivos) como chaves

System.Guid fazer chaves úteis quando a aleatoriedade e a exclusividade global são desejadas, como ao criar um novo trabalho em um sistema de processamento de trabalho. Não é necessário coordenar a alocação de chave, o que pode introduzir um único ponto de falha ou um bloqueio do sistema em um recurso, potencialmente criando um gargalo. A chance de colisões GUID é muito baixa, tornando-as uma escolha comum ao arquitetar sistemas que precisam de alocação aleatória de identificadores.

Referenciando um grão por GUID no código do cliente:

var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());

Recuperando a chave primária do código de granulação:

public override Task OnActivateAsync()
{
    Guid primaryKey = this.GetPrimaryKey();
    return base.OnActivateAsync();
}

Usando inteiros como chaves

Um inteiro longo também está disponível. Isso faz sentido se os dados persistirem em um banco de dados relacional, em que os índices numéricos geralmente são preferidos a GUIDs.

Referenciando um grão por um inteiro longo no código do cliente:

var grain = grainFactory.GetGrain<IExample>(1);

Recuperando a chave primária do código de granulação:

public override Task OnActivateAsync()
{
    long primaryKey = this.GetPrimaryKeyLong();
    return base.OnActivateAsync();
}

Usando cadeias de caracteres como chaves

Uma chave de cadeia de caracteres também está disponível.

Referenciando um grão por cadeia de caracteres no código do cliente:

var grain = grainFactory.GetGrain<IExample>("myGrainKey");

Recuperando a chave primária do código de granulação:

public override Task OnActivateAsync()
{
    string primaryKey = this.GetPrimaryKeyString();
    return base.OnActivateAsync();
}

Usando chaves compostas

Se o sistema não se encaixar bem com GUIDs ou longs, opte por uma chave primária composta. Isso permite usar uma combinação de um GUID ou longo e uma cadeia de caracteres para fazer referência a um grão.

Herda a interface de IGrainWithGuidCompoundKey ou IGrainWithIntegerCompoundKey assim:

public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
    Task Hello();
}

No código do cliente, isso adiciona um segundo argumento ao IGrainFactory.GetGrain método na fábrica de grãos:

var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);

Para acessar a chave composta no grão, chame uma sobrecarga do GrainExtensions.GetPrimaryKey método (como GrainExtensions.GetPrimaryKeyLong):

public class ExampleGrain : Orleans.Grain, IExampleGrain
{
    public Task Hello()
    {
        long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
        Console.WriteLine($"Hello from {keyExtension}");

        return Task.CompletedTask;
    }
}

Por que os grãos usam identificadores lógicos

Em ambientes orientados a objetos como o .NET, a identidade de um objeto é difícil de distinguir de uma referência a ele. Quando um objeto é criado usando a new palavra-chave, a referência retornada representa todos os aspectos de sua identidade, exceto aqueles que mapeiam o objeto para alguma entidade externa que ele representa. Orleans foi projetado para sistemas distribuídos. Em sistemas distribuídos, as referências de objeto não podem representar a identidade da instância porque estão limitadas ao espaço de endereço de um único processo. Orleans usa identificadores lógicos para evitar essa limitação. Os grãos usam identificadores lógicos para que as referências de grãos permaneçam válidas durante o tempo de vida do processo e sejam portáteis de um processo para outro. Isso permite que eles sejam armazenados e recuperados posteriormente ou enviados por uma rede para outro processo no aplicativo, enquanto ainda se referem à mesma entidade: o grão para o qual a referência foi criada.