Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
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 estejam disponíveis durante as solicitações. A persistência de dados usa um modelo de plug-in extensível, permitindo que você utilize provedores de armazenamento para qualquer banco de dados. Esse modelo de persistência foi projetado para simplificar e não se destina a abranger 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 de perfil e um estado de carrinho , cada um armazenado em um sistema de armazenamento separado.
Metas
- Dê suporte a vários objetos de dados persistentes nomeados por grão.
- Permitir 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 provedores de armazenamento controle total sobre como armazenam dados de estado de granulação no repositório de backup persistente. Corolário: Orleans não fornece uma solução de armazenamento ORM abrangente, mas permite que os provedores de armazenamento personalizados ofereçam suporte a requisitos orm específicos, 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 compatíveis com ADO.NET. Para obter mais informações, consulte ADO.NET persistência de grãos.
- Microsoft.Orleans.Persistence.AzureStorage: para Armazenamento do Azure, incluindo Armazenamento de Blobs do Azure e Armazenamento de Tabelas do Azure (usando a API de Armazenamento de Tabelas do Azure). Para obter mais informações, consulte a persistência de grãos do Armazenamento do Azure.
- Microsoft.Orleans. Persistence.Cosmos: o provedor do Azure Cosmos DB. Para obter mais informações, consulte a persistência de grãos do Azure Cosmos DB.
- Microsoft.Orleans. Persistence.DynamoDB: para o Amazon DynamoDB. Para obter mais informações, consulte a persistência de grãos do Amazon DynamoDB.
API (Interface de Programação de Aplicativos)
Os grãos interagem com seu estado persistente usando IPersistentState<TState>, onde TState está 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 de 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 provedores de armazenamento configurados diferentes, mesmo que ambos sejam do mesmo tipo (por exemplo, duas instâncias diferentes do provedor de Armazenamento de Tabelas 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 acionar explicitamente a gravação de qualquer estado modificado do grão quando necessário.
Se um grão quiser ler explicitamente seu estado mais recente do repositório de backup, ele deverá chamar o ReadStateAsync método. Isso recarrega o estado de grão do repositório persistente por meio do provedor de armazenamento. A cópia anterior na memória do estado do grain é sobrescrita e substituída quando Task de ReadStateAsync() é concluído.
Acesse o valor do estado usando a State propriedade. Por exemplo, o método a seguir acessa o estado de perfil declarado no código acima:
public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);
Não é necessário chamar 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 gravação
Você pode modificar o estado por meio da State propriedade. O estado modificado não é mantido automaticamente. Em vez disso, você decide quando manter o estado chamando o WriteStateAsync método. Por exemplo, o método a seguir atualiza uma propriedade 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 qualquer operação de gravação. Nos bastidores, o runtime pode usar regras de otimização e heurísticas para evitar realizar algumas ou todas as cópias profundas em determinadas 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.
Estado de limpeza
O método ClearStateAsync limpa o estado do grão em armazenamento. Dependendo do provedor, essa operação pode, opcionalmente, excluir totalmente o estado de grãos.
Comece agora
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 usar 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 é 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 o estado persistente a um grão de duas maneiras principais:
- Injetando
IPersistentState<TState>no construtor do grão. - Herdando de Grain<TGrainState>.
A maneira recomendada de adicionar armazenagem a um grão é injetar IPersistentState<TState> no construtor do grão com um atributo [PersistentState("stateName", "providerName")] associado. Para obter detalhes sobre Grain<TState>, consulte Usando Grain<TState> para adicionar armazenamento a um grão abaixo. O uso Grain<TState> ainda tem suporte, mas é considerado uma abordagem herdada.
Declare uma classe para manter o estado de 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 ele for injetado no construtor, portanto, acessar o estado do perfil é 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 determinado grão 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 ao grain que causou as falhas de ativação retorna ao chamador, assim como qualquer outra falha durante a ativação do grain. Falhas encontradas pelo provedor de armazenamento ao ler dados de estado para um determinado grão resultam em uma exceção do ReadStateAsyncTask. O grão pode optar por tratar ou ignorar a Task exceção, assim como qualquer outro Task em Orleans.
Qualquer tentativa de enviar uma mensagem para um grão que não foi carregado na inicialização do silo devido a uma configuração de provedor de armazenamento ausente ou inválida retorna o erro BadProviderConfigExceptionpermanente.
Modos de falha para operações de gravação
Falhas encontradas pelo provedor de armazenamento ao gravar dados de estado para um determinado "grain" resultam na geração de uma exceção pelo WriteStateAsync()Task. Normalmente, isso significa que a grain call exception é lançada de volta para o chamador do cliente, desde que esteja WriteStateAsync()Task encadeada corretamente no retorno Task final desse método de grain. No entanto, em determinados cenários avançados, você pode escrever código granulado para lidar especificamente com esses erros de gravação, assim como lidar com qualquer outro erro Task.
Os grãos que executam código de recuperação ou tratamento de erros devem capturar exceções ou falhas e não lançá-las novamente, significando que trataram com êxito o erro de gravação.
Recomendações
Usar a serialização JSON ou outro formato de serialização tolerante a 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. Verifique se, ao evoluir os contratos de dados, os dados já armazenados ainda podem ser carregados.
Usar Grain<TState> para adicionar armazenamento a um grão
Importante
Usar Grain<T> para adicionar armazenamento a um grão é considerado uma funcionalidade herdada . Adicione o armazenamento de grãos usando IPersistentState<T> como descrito anteriormente.
As classes granuladas herdadas de Grain<T> (em que T um tipo de dados de estado específico do aplicativo precisa de persistência) têm seu estado carregado automaticamente do armazenamento especificado.
Marque esses grãos com uma StorageProviderAttribute instância nomeada de um provedor de armazenamento a ser usada para ler/gravar os dados de estado para esse grão.
[StorageProvider(ProviderName="store1")]
public class MyGrain : Grain<MyGrainState>, /*...*/
{
/*...*/
}
A Grain<T> classe base define os seguintes métodos para as subclasses chamarem:
protected virtual Task ReadStateAsync() { /*...*/ }
protected virtual Task WriteStateAsync() { /*...*/ }
protected virtual Task ClearStateAsync() { /*...*/ }
O comportamento desses métodos corresponde aos seus equivalentes sobre IPersistentState<TState> definidos anteriormente.
Criar um provedor de armazenamento
Há duas partes nas APIs de persistência de estado: a API exposta ao grain por meio de IPersistentState<T> ou Grain<T>, e a API do provedor de armazenamento, centralizada em torno de 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 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 como null se não usarem Etag.
Qualquer tentativa de executar uma operação de gravação quando o provedor de armazenamento detectar uma Etag violação de restrição deve fazer com que a gravação Task falhe com um 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 em um retorno Task quebrado com uma exceção indicando o problema de armazenamento subjacente. Em muitos casos, essa exceção pode ser revertida para o chamador que disparou a operação de armazenamento chamando um método no grão. É importante considerar se o chamador consegue 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 ser propagadas de volta para o chamador.
Mapeamento de dados
Os provedores de armazenamento individuais devem decidir a melhor maneira de armazenar o estado do grão – blob (vários formatos/formas serializadas) ou coluna por campo, que são escolhas óbvias.
Registrar um provedor de armazenamento
O Orleans runtime resolve um provedor de armazenamento a partir do provedor de serviços (IServiceProvider) quando um grain é criado. O runtime resolve uma instância de IGrainStorage. Se o provedor de armazenamento for nomeado (por exemplo, por meio do atributo [PersistentState(stateName, storageName)]), então uma instância nomeada de IGrainStorage é resolvida.
Para registrar uma instância nomeada de IGrainStorage, use o AddSingletonNamedService método de extensão, seguindo o exemplo do provedor AzureTableGrainStorage aqui.