Identidade de grãos
Os grãos em cada um têm um identificador único, exclusivo e definido pelo usuário, que consiste em Orleans duas partes:
- O nome do tipo de grão, que identifica exclusivamente a classe de grãos.
- A chave de grão, que identifica exclusivamente uma instância lógica dessa classe de grão.
O tipo de grão e a chave são representados como cadeias legíveis por Orleans humanos e, por convenção, a identidade do grão é escrita com o tipo de grão e a chave separados por um /
caractere. Por exemplo, shoppingcart/bob65
representa o tipo de grão nomeado shoppingcart
com uma chave bob65
.
Não é comum construir identidades de grãos diretamente. Em vez disso, é mais comum criar referências de grãos usando Orleans.IGrainFactoryo .
As seções a seguir discutem nomes de tipos de grãos e chaves de grão com mais detalhes.
Nomes de tipos de grãos
Orleans cria um nome de tipo de grão para você com base em sua classe de implementação de grão removendo o sufixo "Grain" do nome da classe, se ele estiver presente, e convertendo a cadeia de caracteres resultante em sua representação minúscula. Por exemplo, uma classe nomeada ShoppingCartGrain
receberá o nome shoppingcart
do tipo de grão . Recomenda-se que os nomes e chaves de tipo de grão consistam apenas em caracteres imprimíveis, como caracteres alfanuméricos (a
-z
,Z
A
- , e9
0
- ) e símbolos como -
, _
, @
=
, . Outros caracteres podem ou não ser suportados e muitas vezes precisarão de tratamento especial quando impressos em logs ou aparecendo como identificadores em outros sistemas, como bancos de dados.
Como alternativa, o Orleans.GrainTypeAttribute atributo pode ser usado para personalizar o nome do tipo de grão para a classe de grão à qual ele está anexado, como no exemplo a seguir:
[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
// Add your grain implementation here
}
No exemplo anterior, a classe de grãos, ShoppingCartGrain
tem um nome de tipo de grão de cart
. Cada grão só pode ter um nome de tipo de grão.
Para grãos genéricos, a aridade genérica deve ser incluída 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 de grão tem dois parâmetros genéricos, de modo que um backtick `
seguido pela aridade genérica, 2, é adicionado ao final do nome do tipo de grão, dict
para criar o nome dict`2
do tipo de grão, conforme especificado no atributo na classe de grão, [GrainType("dict`2")]
.
Chaves de grão
Por conveniência, Orleans expõe métodos que permitem a construção de chaves de grão de um Guid ou um Int64, além de um String. A chave primária tem como escopo o tipo de grão. Portanto, a identidade completa de um grão é formada a partir do tipo do grão e sua chave.
O chamador do grão decide qual esquema deve ser usado. 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 strings.
Situações que exigem uma instância de grão singleton podem usar um valor fixo bem conhecido, como "default"
. Esta é apenas uma convenção, mas ao aderir a esta convenção torna-se claro no local de chamada que um grão de uma única tonelada está em uso.
Usando identificadores globais exclusivos (GUIDs) como chaves
System.Guid Crie chaves úteis quando a aleatoriedade e a exclusividade global forem desejadas, como ao criar um novo trabalho em um sistema de processamento de trabalho. Você não precisa coordenar a alocação de chaves, o que poderia introduzir um único ponto de falha no sistema ou um bloqueio do lado do sistema em um recurso que poderia apresentar um gargalo. Há uma chance muito baixa de GUIDs colidirem, então eles são uma escolha comum ao arquitetar um sistema que precisa alocar identificadores aleatórios.
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 grão:
public override Task OnActivateAsync()
{
Guid primaryKey = this.GetPrimaryKey();
return base.OnActivateAsync();
}
Usando inteiros como chaves
Um inteiro longo também está disponível, o que faria sentido se o grão fosse persistido em um banco de dados relacional, onde os índices numéricos são preferidos em relação aos 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 grão:
public override Task OnActivateAsync()
{
long primaryKey = this.GetPrimaryKeyLong();
return base.OnActivateAsync();
}
Usando cadeias de caracteres como teclas
Uma cadeia de caracteres também está disponível.
Fazendo referência a um grão por String no código do cliente:
var grain = grainFactory.GetGrain<IExample>("myGrainKey");
Recuperando a chave primária do código de grão:
public override Task OnActivateAsync()
{
string primaryKey = this.GetPrimaryKeyString();
return base.OnActivateAsync();
}
Usando chaves compostas
Se você tiver um sistema que não se encaixa bem com GUIDs ou longs, você pode optar por uma chave primária composta, que permite usar uma combinação de um GUID ou long e uma string para fazer referência a um grão.
Você pode herdar sua interface ou IGrainWithGuidCompoundKey IGrainWithIntegerCompoundKey interface 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, podemos chamar uma sobrecarga no GrainExtensions.GetPrimaryKey método (o GrainExtensions.GetPrimaryKeyLong):
public class ExampleGrain : Orleans.Grain, IExampleGrain
{
public Task Hello()
{
long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
Console.WriteLine($"Hello from {keyExtension}");
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 palavra-chave new
, a referência que você recebe de volta representa todos os aspetos de sua identidade, exceto aqueles que mapeiam o objeto para alguma entidade externa que ele representa. Orleans é projetado para sistemas distribuídos. Em sistemas distribuídos, as referências de objeto não podem representar a identidade da instância, uma vez que as referências de objeto são limitadas ao espaço de endereçamento 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 ao longo dos tempos de vida do processo e sejam portáteis de um processo para outro, permitindo que sejam armazenadas e posteriormente recuperadas ou enviadas através de uma rede para outro processo no aplicativo, tudo isso sem deixar de se referir à mesma entidade: o grão para o qual a referência foi criada.