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.
Antes de escrever código para implementar uma classe de grãos, crie um novo projeto da Biblioteca de Classes direcionado ao .NET Standard ou ao .NET Core (preferencial) ou ao .NET Framework 4.6.1 ou superior (se não for possível usar o .NET Standard ou o .NET Core devido a dependências). Você pode definir interfaces de grão e classes de grão no mesmo projeto de Biblioteca de Classes ou em dois projetos diferentes para melhor separar as interfaces da implementação. Em ambos os casos, os projetos precisam referenciar o Microsoft.Orleans. Sdk pacote NuGet.
Para obter instruções mais detalhadas, consulte a seção Configuração do Projeto do Tutorial Um – Orleans Básicos.
Interfaces e classes de grãos
Os grãos interagem entre si e são chamados de fora invocando métodos declarados como parte de suas respectivas interfaces de grãos. Uma classe de grãos implementa uma ou mais interfaces de grão declaradas anteriormente. Todos os métodos de uma grain interface devem retornar um Task (para void métodos), um Task<TResult>, ou um ValueTask<TResult> (para métodos que retornam valores de tipo T).
Veja a seguir um trecho do exemplo do Serviço de Presença do Orleans:
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
Task LeaveGame(IGameGrain game);
}
public class PlayerGrain : Grain, IPlayerGrain
{
private IGameGrain _currentGame;
// Game the player is currently in. May be null.
public Task<IGameGrain> GetCurrentGame()
{
return Task.FromResult(_currentGame);
}
// Game grain calls this method to notify that the player has joined the game.
public Task JoinGame(IGameGrain game)
{
_currentGame = game;
Console.WriteLine(
$"Player {GetPrimaryKey()} joined game {game.GetPrimaryKey()}");
return Task.CompletedTask;
}
// Game grain calls this method to notify that the player has left the game.
public Task LeaveGame(IGameGrain game)
{
_currentGame = null;
Console.WriteLine(
$"Player {GetPrimaryKey()} left game {game.GetPrimaryKey()}");
return Task.CompletedTask;
}
}
Tempo limite de resposta para métodos de grain
O runtime Orleans permite que você imponha um tempo limite de resposta por método granular. Se um método de grão não for concluído dentro do tempo limite, o runtime gerará um TimeoutException. Para impor um tempo limite de resposta, adicione o ResponseTimeoutAttribute à definição do método grain da interface. É crucial adicionar o atributo à definição do método de interface, não à implementação do método na classe grain, pois o cliente e o silo precisam estar cientes do tempo limite.
Estendendo a implementação de PlayerGrain anterior, o exemplo a seguir mostra como impor um tempo limite de resposta no método LeaveGame:
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
[ResponseTimeout("00:00:05")] // 5s timeout
Task LeaveGame(IGameGrain game);
}
O código anterior define um tempo limite de resposta de cinco segundos no método LeaveGame. Ao sair de um jogo, se levar mais de cinco segundos, um TimeoutException é lançado.
Configurar o tempo limite de resposta
Semelhante aos tempos limite de resposta do método de grão individual, você pode configurar um tempo limite de resposta padrão para todos os métodos de grãos. As chamadas aos métodos de grain expiram se uma resposta não for recebida dentro do período especificado. Por padrão, esse período é 30 segundos. Você pode configurar o tempo limite de resposta padrão:
- Configurando ResponseTimeout em ClientMessagingOptions, em um cliente externo.
- Configurando ResponseTimeout em SiloMessagingOptions, em um servidor.
Para obter mais informações sobre como configurar Orleans, consulte a configuração do cliente em ou a configuração do servidor em .
Retornar valores de métodos de grãos
Defina um método de grão que retorna um valor de tipo T em uma interface de grãos como retornando um Task<T>.
Para métodos relacionados a grãos não marcados com a palavra-chave async, quando o valor de retorno estiver disponível, você geralmente o retorna usando a seguinte declaração:
public Task<SomeType> GrainMethod1()
{
return Task.FromResult(GetSomeType());
}
Defina um método de grão que não retorna nenhum valor (efetivamente um método nulo) em uma interface de grãos como retornando um Task. O retornado Task indica a execução assíncrona e a conclusão do método. Para métodos de grain não marcados com a palavra-chave async, quando um método "void" conclui sua execução, ele precisa retornar o valor especial Task.CompletedTask.
public Task GrainMethod2()
{
return Task.CompletedTask;
}
Um método de grão marcado como async retorna o valor diretamente:
public async Task<SomeType> GrainMethod3()
{
return await GetSomeTypeAsync();
}
Um método de grão void marcado como async que não retorna nenhum valor simplesmente retorna no final de sua execução:
public async Task GrainMethod4()
{
return;
}
Se um método de grão receber o valor retornado de outra chamada de método assíncrono (para um grão ou não) e não precisar executar o tratamento de erros para essa chamada, ele poderá simplesmente retornar o Task que recebe dessa chamada assíncrona:
public Task<SomeType> GrainMethod5()
{
Task<SomeType> task = CallToAnotherGrain();
return task;
}
Da mesma forma, um método de granulação void pode retornar um Task que foi retornado a ele por outra chamada, em vez de aguardá-lo.
public Task GrainMethod6()
{
Task task = CallToAsyncAPI();
return task;
}
ValueTask<T> pode ser usado em vez de Task<T>.
Referências de grãos
Uma referência de grão é um objeto proxy que implementa a mesma interface de grão que a classe de grão correspondente. Ele encapsula a identidade lógica (tipo e chave exclusiva) do grão de destino. Você usa referências de grãos para fazer chamadas para o grão de destino. Cada referência de grão aponta para um único grão (uma única instância da classe de grãos), mas você pode criar várias referências independentes para o mesmo grão.
Como uma referência de grão representa a identidade lógica do grão de destino, ela é independente da localização física do grão e permanece válida mesmo após uma reinicialização completa do sistema. Você pode usar referências de grãos como qualquer outro objeto .NET. Você pode passá-lo para um método, usá-lo como um valor de retorno de método, etc., e até mesmo salvá-lo no armazenamento persistente.
Você pode obter uma referência de grão passando a identidade de um grão para o IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) método, onde T está a interface de grãos e key é a chave exclusiva do grão dentro de seu tipo.
Os exemplos a seguir mostram como obter uma referência de grãos para a IPlayerGrain interface definida anteriormente.
De dentro de uma classe de grãos:
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
Do código do cliente Orleans.
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
Para obter mais informações sobre referências de grãos, consulte o artigo de referência de grãos .
Invocação do método grain
O modelo de programação Orleans é baseado em de programação assíncrona. Usando a referência de grãos do exemplo anterior, veja como você executa uma invocação de método de grão:
// Invoking a grain method asynchronously
Task joinGameTask = player.JoinGame(this);
// The await keyword effectively makes the remainder of the
// method execute asynchronously at a later point
// (upon completion of the Task being awaited) without blocking the thread.
await joinGameTask;
// The next line will execute later, after joinGameTask has completed.
players.Add(playerId);
Você pode unir dois ou mais Tasks. A operação de junção cria uma nova Task que é concluída quando todos os seus constituintes Tasks são concluídos. Esse padrão é útil quando um grão precisa iniciar vários cálculos e aguardar que todos eles sejam concluídos antes de prosseguir. Por exemplo, um grão de front-end que gera uma página da Web feita de muitas partes pode fazer várias chamadas de back-end (uma para cada parte) e receber um Task para cada resultado. O grão aguardaria então a junção de todos esses Tasks. Quando a junção Task é resolvida, os indivíduos Tasks foram concluídos, e todos os dados necessários para a formatação da página da Web foram recebidos.
Exemplo:
List<Task> tasks = new List<Task>();
Message notification = CreateNewMessage(text);
foreach (ISubscriber subscriber in subscribers)
{
tasks.Add(subscriber.Notify(notification));
}
// WhenAll joins a collection of tasks, and returns a joined
// Task that will be resolved when all of the individual notification Tasks are resolved.
Task joinedTask = Task.WhenAll(tasks);
await joinedTask;
// Execution of the rest of the method will continue
// asynchronously after joinedTask is resolve.
Propagação de erro
Quando um método de grão gera uma exceção, Orleans propaga essa exceção pela pilha de chamadas e através de hosts, conforme necessário. Para que isso funcione conforme o esperado, as exceções devem ser serializáveis Orleanse os hosts que lidam com a exceção devem ter o tipo de exceção disponível. Se um tipo de exceção não estiver disponível, Orleans gerará a exceção como uma instância de Orleans.Serialization.UnavailableExceptionFallbackException, preservando a mensagem, o tipo e o rastreamento de pilha da exceção original.
Exceções lançadas a partir de métodos de grain não fazem com que o grain seja desativado, a menos que a exceção derive de Orleans.Storage.InconsistentStateException. As operações de armazenamento lançam InconsistentStateException quando descobrem que o estado na memória do grão está inconsistente com o estado no banco de dados. Além da manipulação especial de InconsistentStateException, esse comportamento é semelhante a gerar uma exceção de qualquer objeto .NET: exceções não fazem com que um objeto seja destruído.
Métodos virtuais
Uma classe de grãos pode, opcionalmente, substituir os métodos virtuais OnActivateAsync e OnDeactivateAsync. O Orleans runtime invoca esses métodos após a ativação e a desativação de cada grão da classe. Isso dá ao seu código de grão a chance de executar operações adicionais de inicialização e limpeza. Uma exceção gerada por OnActivateAsync falha no processo de ativação.
Embora OnActivateAsync (se substituído) seja sempre chamado como parte do processo de ativação de grãos, OnDeactivateAsync não há garantia de ser chamado em todas as situações (por exemplo, em caso de falha de servidor ou outros eventos anormais). Por isso, seus aplicativos não devem depender de OnDeactivateAsync para executar operações críticas, como persistir alterações de estado. Use-o apenas para operações de esforço máximo.
Consulte também
- extensões Grain
- Identidade do grão
- referências de grãos
- persistência de grãos
- Visão geral do ciclo de vida do Grain
- Posicionamento do Grain