Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Os grãos podem ter vários objetos de dados persistentes nomeados associados a eles. Esses objetos de estado são carregados do armazenamento durante a ativação de grãos para que fiquem disponíveis durante as solicitações. A persistência dos dados usa um modelo de plug-in extensível, permitindo o uso de provedores de armazenamento de dados para qualquer tipo de base de dados. Esse modelo de persistência foi projetado para simplicidade e não se destina a cobrir todos os padrões de acesso a dados. Os grãos também podem acessar bancos de dados diretamente sem usar o modelo de persistência de grãos.
No diagrama anterior, UserGrain tem um estado Profile e um estado Cart , cada um armazenado em um sistema de armazenamento separado.
Objetivos
- Ofereça suporte a vários objetos de dados persistentes nomeados por grão.
- Permita vários provedores de armazenamento configurados, cada um potencialmente com uma configuração diferente e apoiado por um sistema de armazenamento diferente.
- Permitir que a comunidade desenvolva e publique provedores de armazenamento.
- Dê aos fornecedores de armazenamento controle total sobre a forma como armazenam dados de estado granular no armazenamento persistente de suporte. Corolário: o Orleans não fornece uma solução de armazenamento abrangente para ORM, mas permite que os provedores de armazenamento personalizados ofereçam suporte a requisitos específicos de ORM conforme necessário.
Pacotes
Você pode encontrar Orleans provedores de armazenamento de grãos no NuGet. Os pacotes mantidos oficialmente incluem:
- Microsoft.Orleans. Persistence.AdoNet: Para bancos de dados SQL e outros sistemas de armazenamento suportados pelo ADO.NET. Para obter mais informações, consulte ADO.NET persistência de grãos.
- Microsoft.Orleans.Persistence.AzureStorage: Para o Armazenamento do Azure, incluindo Blob Storage e Table Storage do Azure (através da API de Table Storage do Azure). Para obter mais informações, consulte Persistência de Dados no Armazenamento Azure.
- Microsoft.Orleans. Persistence.Cosmos: O provedor do Azure Cosmos DB. Para mais informações, veja Persistência de grão no Azure Cosmos DB.
- Microsoft.Orleans. Persistence.DynamoDB: Para o Amazon DynamoDB. Para obter mais informações, consulte Persistência de grãos do Amazon DynamoDB.
API (Interface de Programação de Aplicações)
Os grãos interagem com seu estado persistente usando IPersistentState<TState>, onde TState é o tipo de estado serializável:
public interface IPersistentState<TState> : IStorage<TState>
{
}
public interface IStorage<TState> : IStorage
{
TState State { get; set; }
}
public interface IStorage
{
string Etag { get; }
bool RecordExists { get; }
Task ClearStateAsync();
Task WriteStateAsync();
Task ReadStateAsync();
}
public interface IPersistentState<TState> where TState : new()
{
TState State { get; set; }
string Etag { get; }
Task ClearStateAsync();
Task WriteStateAsync();
Task ReadStateAsync();
}
Orleans injeta instâncias de IPersistentState<TState> no grão como parâmetros do construtor. Você pode anotar esses parâmetros com um PersistentStateAttribute atributo para identificar o nome do estado que está sendo injetado e o nome do provedor de armazenamento que o fornece. O exemplo a seguir demonstra isso injetando dois estados nomeados no UserGrain construtor:
public class UserGrain : Grain, IUserGrain
{
private readonly IPersistentState<ProfileState> _profile;
private readonly IPersistentState<CartState> _cart;
public UserGrain(
[PersistentState("profile", "profileStore")] IPersistentState<ProfileState> profile,
[PersistentState("cart", "cartStore")] IPersistentState<CartState> cart)
{
_profile = profile;
_cart = cart;
}
}
Diferentes tipos de grãos podem usar diferentes provedores de armazenamento configurados, mesmo que ambos sejam do mesmo tipo (por exemplo, duas instâncias diferentes do provedor de Armazenamento de Tabela do Azure conectadas a diferentes contas de Armazenamento do Azure).
Estado de leitura
O estado do grão é lido automaticamente quando o grão é ativado, mas os grãos são responsáveis por desencadear explicitamente a escrita para qualquer estado de grão que tenha sido alterado quando necessário.
Se um grão deseja reler explicitamente o seu estado mais recente do armazenamento de suporte, deverá chamar o método ReadStateAsync. Isso recarrega o estado do grão a partir do armazenamento persistente através do provedor de armazenamento. A cópia na memória anterior do estado do grão é sobreposta e substituída quando o Task de ReadStateAsync() é concluído.
Acesse o valor do estado usando a State propriedade. Por exemplo, o método a seguir acessa o estado do perfil declarado no código acima:
public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);
Não há necessidade de ligar ReadStateAsync() durante a operação normal, Orleans carrega o estado automaticamente durante a ativação. No entanto, você pode usar ReadStateAsync() para atualizar o estado modificado externamente.
Consulte a seção Modos de falha abaixo para obter detalhes sobre mecanismos de tratamento de erros.
Estado de escrita
Você pode modificar o estado através da State propriedade. O estado modificado não é persistido automaticamente. Em vez disso, você decide quando persistir o estado chamando o WriteStateAsync método. Por exemplo, o método a seguir atualiza uma propriedade em State e persiste o estado atualizado.
public async Task SetNameAsync(string name)
{
_profile.State.Name = name;
await _profile.WriteStateAsync();
}
Conceitualmente, o Orleans Runtime usa uma cópia profunda do objeto de dados de estado de grão para seu uso durante quaisquer operações de gravação. Nos bastidores, o runtime pode usar regras de otimização e heurísticas para evitar executar parte ou toda a cópia profunda em certas circunstâncias, desde que a semântica de isolamento lógico esperada seja preservada.
Consulte a seção Modos de falha abaixo para obter detalhes sobre mecanismos de tratamento de erros.
Limpar o estado
O ClearStateAsync método limpa o estado do grão no armazenamento. Dependendo do provedor, essa operação pode, opcionalmente, excluir totalmente o estado de grão.
Comece
Antes que um grão possa usar persistência, você deve configurar um provedor de armazenamento no silo.
Primeiro, configure os provedores de armazenamento, um para o estado do perfil e outro para o estado do carrinho:
using IHost host = new HostBuilder()
.UseOrleans(siloBuilder =>
{
siloBuilder.AddAzureTableGrainStorage(
name: "profileStore",
configureOptions: options =>
{
// Configure the storage connection key
options.ConfigureTableServiceClient(
"DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1");
})
.AddAzureBlobGrainStorage(
name: "cartStore",
configureOptions: options =>
{
// Configure the storage connection key
options.ConfigureTableServiceClient(
"DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2");
});
})
.Build();
var host = new HostBuilder()
.UseOrleans(siloBuilder =>
{
siloBuilder.AddAzureTableGrainStorage(
name: "profileStore",
configureOptions: options =>
{
// Use JSON for serializing the state in storage
options.UseJson = true;
// Configure the storage connection key
options.ConnectionString =
"DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1";
})
.AddAzureBlobGrainStorage(
name: "cartStore",
configureOptions: options =>
{
// Use JSON for serializing the state in storage
options.UseJson = true;
// Configure the storage connection key
options.ConnectionString =
"DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2";
});
})
.Build();
Importante
A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.
Agora que você configurou um provedor de armazenamento chamado "profileStore", você pode acessar esse provedor a partir de um grão.
Você pode adicionar um estado persistente a um grão de duas maneiras principais:
- Injetando
IPersistentState<TState>no construtor do grão. - Ao herdar de Grain<TGrainState>.
A maneira recomendada de adicionar armazenamento a um grão é injetando IPersistentState<TState> no construtor do grão com um atributo associado [PersistentState("stateName", "providerName")] . Para obter mais detalhes sobre Grain<TState>, consulte abaixo Usando Grain<TState> para adicionar armazenamento a um grão. O uso de Grain<TState> ainda é suportado, mas é considerado uma abordagem obsoleta.
Declare uma classe para manter o estado do seu grão:
[Serializable]
public class ProfileState
{
public string Name { get; set; }
public Date DateOfBirth { get; set; }
}
Injete IPersistentState<ProfileState> no construtor do grão:
public class UserGrain : Grain, IUserGrain
{
private readonly IPersistentState<ProfileState> _profile;
public UserGrain(
[PersistentState("profile", "profileStore")]
IPersistentState<ProfileState> profile)
{
_profile = profile;
}
}
Importante
O estado do perfil não será carregado no momento em que é injetado no construtor, portanto, o acesso a ele é inválido naquele momento. O estado será carregado antes OnActivateAsync de ser chamado.
Agora que o grão tem um estado persistente, você pode adicionar métodos para ler e gravar o estado:
public class UserGrain : Grain, IUserGrain
{
private readonly IPersistentState<ProfileState> _profile;
public UserGrain(
[PersistentState("profile", "profileStore")]
IPersistentState<ProfileState> profile)
{
_profile = profile;
}
public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);
public async Task SetNameAsync(string name)
{
_profile.State.Name = name;
await _profile.WriteStateAsync();
}
}
Modos de falha para operações de persistência
Modos de falha para operações de leitura
Falhas retornadas pelo provedor de armazenamento durante a leitura inicial dos dados de estado de um grão específico falham na operação de ativação desse grão. Nesses casos, não haverá nenhuma chamada para o método de retorno de chamada do ciclo de vida desse OnActivateAsync grão. A solicitação original para o grão que causou as falhas de ativação é devolvida ao chamador, tal como qualquer outra falha durante a ativação do grão. As falhas encontradas pelo provedor de armazenamento ao ler dados de estado para um grão específico resultam em uma exceção do ReadStateAsyncTask. O grão pode optar por manipular ou ignorar a Task exceção, assim como qualquer outro Task em Orleans.
Qualquer tentativa de enviar uma mensagem para um grão que falhou ao carregar durante o arranque do silo devido a uma configuração de provedor de armazenamento ausente ou incorreta retorna o erro permanente BadProviderConfigException.
Modos de falha nas operações de escrita
As falhas encontradas pelo provedor de armazenamento ao gravar dados de estado para um grão específico resultam em uma exceção lançada pelo WriteStateAsync()Task. Normalmente, isso significa que a exceção de chamada de grain é lançada de volta ao chamador cliente, desde que o WriteStateAsync()Task esteja corretamente encadeado no retorno Task final para este método de grain. No entanto, em determinados cenários avançados, você pode escrever código de grão para lidar especificamente com esses erros de gravação, assim como lidar com qualquer outro com defeito Task.
Os componentes que executam o tratamento de erros ou o código de recuperação devem capturar exceções ou falhas WriteStateAsync()Taske não relançá-las, significando que trataram com sucesso o erro de escrita.
Recomendações
Usar a serialização JSON ou outro formato de serialização tolerante à versão
O código evolui, e isso geralmente inclui tipos de armazenamento. Para acomodar essas alterações, configure um serializador apropriado. Para a maioria dos provedores de armazenamento, uma UseJson opção ou similar está disponível para usar JSON como o formato de serialização. Certifique-se de que, ao evoluir os contratos de dados, os dados já armazenados ainda possam ser carregados.
Utilizando Grain<TState> para adicionar armazenamento a um grão
Importante
Usar Grain<T> para adicionar armazenamento a um grão é considerado uma função antiga. Adicione o armazenamento de grãos usando IPersistentState<T> como descrito anteriormente.
As classes de grãos que herdam de Grain<T> (onde T é um tipo de dados de estado específico da aplicação que requer persistência) têm o seu estado carregado automaticamente do armazenamento especificado.
Marque esses grãos com uma StorageProviderAttribute especificação de uma instância nomeada de um provedor de armazenamento para usar para ler/gravar os dados de estado desse grão.
[StorageProvider(ProviderName="store1")]
public class MyGrain : Grain<MyGrainState>, /*...*/
{
/*...*/
}
A Grain<T> classe base define os seguintes métodos para subclasses chamarem:
protected virtual Task ReadStateAsync() { /*...*/ }
protected virtual Task WriteStateAsync() { /*...*/ }
protected virtual Task ClearStateAsync() { /*...*/ }
O comportamento destes métodos corresponde aos seus homólogos correspondentes definidos anteriormente em IPersistentState<TState>.
Criar um provedor de armazenamento
Há duas partes nas APIs de persistência de estado: a API exposta ao grão via IPersistentState<T> ou Grain<T>, e a API do provedor de armazenamento, centrada em IGrainStorage — a interface que os provedores de armazenamento devem implementar.
/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface IGrainStorage
{
/// <summary>Read data function for this storage instance.</summary>
/// <param name="stateName">Name of the state for this grain</param>
/// <param name="grainId">Grain ID</param>
/// <param name="grainState">State data object to be populated for this grain.</param>
/// <typeparam name="T">The grain state type.</typeparam>
/// <returns>Completion promise for the Read operation on the specified grain.</returns>
Task ReadStateAsync<T>(
string stateName, GrainId grainId, IGrainState<T> grainState);
/// <summary>Write data function for this storage instance.</summary>
/// <param name="stateName">Name of the state for this grain</param>
/// <param name="grainId">Grain ID</param>
/// <param name="grainState">State data object to be written for this grain.</param>
/// <typeparam name="T">The grain state type.</typeparam>
/// <returns>Completion promise for the Write operation on the specified grain.</returns>
Task WriteStateAsync<T>(
string stateName, GrainId grainId, IGrainState<T> grainState);
/// <summary>Delete / Clear data function for this storage instance.</summary>
/// <param name="stateName">Name of the state for this grain</param>
/// <param name="grainId">Grain ID</param>
/// <param name="grainState">Copy of last-known state data object for this grain.</param>
/// <typeparam name="T">The grain state type.</typeparam>
/// <returns>Completion promise for the Delete operation on the specified grain.</returns>
Task ClearStateAsync<T>(
string stateName, GrainId grainId, IGrainState<T> grainState);
}
/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface IGrainStorage
{
/// <summary>Read data function for this storage instance.</summary>
/// <param name="grainType">Type of this grain [fully qualified class name]</param>
/// <param name="grainReference">Grain reference object for this grain.</param>
/// <param name="grainState">State data object to be populated for this grain.</param>
/// <returns>Completion promise for the Read operation on the specified grain.</returns>
Task ReadStateAsync(
string grainType, GrainReference grainReference, IGrainState grainState);
/// <summary>Write data function for this storage instance.</summary>
/// <param name="grainType">Type of this grain [fully qualified class name]</param>
/// <param name="grainReference">Grain reference object for this grain.</param>
/// <param name="grainState">State data object to be written for this grain.</param>
/// <returns>Completion promise for the Write operation on the specified grain.</returns>
Task WriteStateAsync(
string grainType, GrainReference grainReference, IGrainState grainState);
/// <summary>Delete / Clear data function for this storage instance.</summary>
/// <param name="grainType">Type of this grain [fully qualified class name]</param>
/// <param name="grainReference">Grain reference object for this grain.</param>
/// <param name="grainState">Copy of last-known state data object for this grain.</param>
/// <returns>Completion promise for the Delete operation on the specified grain.</returns>
Task ClearStateAsync(
string grainType, GrainReference grainReference, IGrainState grainState);
}
Crie um provedor de armazenamento personalizado implementando essa interface e registrando essa implementação. Para obter um exemplo de uma implementação de provedor de armazenamento existente, consulte AzureBlobGrainStorage.
Semântica do provedor de armazenamento
Um valor opaco específico Etag do provedor (string) pode ser definido por um provedor de armazenamento como parte dos metadados de estado de grão preenchidos quando o estado foi lido. Alguns provedores podem optar por deixar isso como null se não usassem Etags.
Qualquer tentativa de executar uma operação de gravação quando o provedor de armazenamento detetar uma Etag violação de restrição deve fazer com que a gravação Task seja prejudicada com erro InconsistentStateException transitório e encapsulando a exceção de armazenamento subjacente.
public class InconsistentStateException : OrleansException
{
public InconsistentStateException(
string message,
string storedEtag,
string currentEtag,
Exception storageException)
: base(message, storageException)
{
StoredEtag = storedEtag;
CurrentEtag = currentEtag;
}
public InconsistentStateException(
string storedEtag,
string currentEtag,
Exception storageException)
: this(storageException.Message, storedEtag, currentEtag, storageException)
{
}
/// <summary>The Etag value currently held in persistent storage.</summary>
public string StoredEtag { get; }
/// <summary>The Etag value currently held in memory, and attempting to be updated.</summary>
public string CurrentEtag { get; }
}
Quaisquer outras condições de falha de uma operação de armazenamento devem resultar na quebra do retorno Task, com uma exceção indicando o problema de armazenamento subjacente. Em muitos casos, essa exceção pode ser retornada ao invocador que desencadeou a operação de armazenamento ao chamar um método no grain. É importante considerar se o chamador pode desserializar essa exceção. Por exemplo, o cliente pode não ter carregado a biblioteca de persistência específica que contém o tipo de exceção. Por esse motivo, é aconselhável converter exceções em exceções que podem se propagar de volta para o chamador.
Mapeamento de dados
Os provedores de armazenamento individuais devem decidir a melhor forma de armazenar o estado dos dados – bloco de dados (vários formatos / formas serializadas) ou coluna por campo, que são escolhas óbvias.
Registrar um provedor de armazenamento
O runtime resolve um fornecedor de armazenamento a partir do fornecedor de serviços (Orleans) quando um grain é criado. O tempo de execução resolve uma instância de IGrainStorage. Se o provedor de armazenamento for nomeado (por exemplo, por meio do [PersistentState(stateName, storageName)] atributo), uma instância nomeada de IGrainStorage será resolvida.
Para registar uma instância nomeada de IGrainStorage, use o método de extensão AddSingletonNamedService, seguindo o exemplo do provedor AzureTableGrainStorage aqui.