Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Antes de escribir código para implementar una clase específica, cree un nuevo proyecto de biblioteca de clases destinado a .NET Standard o .NET Core (preferido) o .NET Framework 4.6.1 o superior (si no puede usar .NET Standard o .NET Core debido a dependencias). Puede definir interfaces de grano y clases de grano en la misma biblioteca de clases o en dos proyectos separados para mejorar la separación de las interfaces de la implementación. En cualquier caso, los proyectos deben hacer referencia al Microsoft.Orleans. Sdk paquete NuGet.
Para obtener instrucciones más detalladas, consulte la sección Configuración del Proyecto de Tutorial Uno – Orleans Conceptos básicos.
Interfaces y clases granulares
Los granos interactúan entre ellos y son llamados desde el exterior mediante la invocación de métodos declarados como parte de sus respectivas interfaces de grano. Una clase de grano implementa una o varias interfaces de grano declaradas anteriormente. Todos los métodos de una interfaz de grano deben devolver un Task (para void
los métodos), un Task<TResult>, o un ValueTask<TResult> (para los métodos que devuelven valores de tipo T
).
A continuación se muestra un extracto del ejemplo de servicio de presencia de 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;
}
}
Tiempo de espera de respuesta para métodos de grano
El runtime de Orleans permite aplicar un tiempo de espera de respuesta por método de grano. Si un método de grano no se completa dentro del tiempo de espera, el tiempo de ejecución lanza una TimeoutException excepción. Para imponer un tiempo de espera de respuesta, agregue el ResponseTimeoutAttribute
a la definición del método de grano de la interfaz. Es fundamental agregar el atributo a la definición del método de la interfaz, no a la implementación del método en la clase grain, ya que tanto el cliente como el silo deben tener en cuenta el tiempo de espera.
Al extender la implementación de PlayerGrain
anterior, en el ejemplo siguiente se muestra cómo imponer un tiempo de espera de respuesta en el método LeaveGame
:
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
[ResponseTimeout("00:00:05")] // 5s timeout
Task LeaveGame(IGameGrain game);
}
El código anterior establece un tiempo de espera de respuesta de cinco segundos en el método LeaveGame
. Al salir de un juego, si la salida tarda más de cinco segundos, se lanza un TimeoutException.
Configuración del tiempo de espera de respuesta
De forma similar a los tiempos de espera de respuesta de métodos específicos individuales, puede configurar un tiempo de espera de respuesta predeterminado para todos los métodos de grano. Las llamadas a métodos específicos agota el tiempo de espera si no se recibe una respuesta dentro del período especificado. De forma predeterminada, este período se 30 segundos. Puede configurar el tiempo de espera de respuesta predeterminado:
- Mediante la configuración de ResponseTimeout en ClientMessagingOptions, en un cliente externo.
- Mediante la configuración de ResponseTimeout en SiloMessagingOptionsen un servidor.
Para obtener más información sobre cómo configurar Orleans, vea Configuración del cliente o Configuración del servidor .
Devolver valores de métodos de grano
Defina un método de grano que devuelva un valor de tipo T
en una interfaz de grano como devolver un Task<T>
.
Para los métodos de grano no marcados con la async
palabra clave , cuando el valor devuelto está disponible, normalmente se devuelve mediante la instrucción siguiente:
public Task<SomeType> GrainMethod1()
{
return Task.FromResult(GetSomeType());
}
Defina un método de grain que no devuelva ningún valor (efectivamente, un método void) en una interfaz de grain, devolviendo un Task
. El devuelto Task
indica la ejecución asincrónica y la finalización del método . Para los métodos de grain no marcados con la palabra clave async
, cuando un método "void" completa su ejecución, debe devolver el valor especial Task.CompletedTask.
public Task GrainMethod2()
{
return Task.CompletedTask;
}
Un método de grano marcado como async
devuelve el valor directamente:
public async Task<SomeType> GrainMethod3()
{
return await GetSomeTypeAsync();
}
Un void
método de grano marcado como async
que no devuelve ningún valor simplemente devuelve al final de su ejecución:
public async Task GrainMethod4()
{
return;
}
Si un método de grano recibe el valor devuelto de otra llamada de método asincrónico (a un grano o no) y no necesita realizar el control de errores para esa llamada, simplemente puede devolver lo Task
que recibe de esa llamada asincrónica:
public Task<SomeType> GrainMethod5()
{
Task<SomeType> task = CallToAnotherGrain();
return task;
}
Del mismo modo, un método de void
grano puede devolver un Task
devuelto por otra llamada en lugar de esperarlo.
public Task GrainMethod6()
{
Task task = CallToAsyncAPI();
return task;
}
ValueTask<T>
se puede usar en lugar de Task<T>
.
Referencias de grano
Una referencia de grano es un objeto proxy que implementa la misma interfaz de grano que la clase de grano correspondiente. Encapsula la identidad lógica (tipo y clave única) del grano de destino. Las referencias de grano se usan para realizar llamadas al grano de destino. Cada referencia de grano apunta a un único grano (una sola instancia de la clase de grano), pero puede crear varias referencias independientes al mismo grano.
Dado que una referencia de grano representa la identidad lógica del grano de destino, es independiente de la ubicación física del grano y sigue siendo válida incluso después de reiniciar el sistema completo. Puede usar referencias de grano como cualquier otro objeto .NET. Puede pasarlo a un método, usarlo como un valor devuelto de método, etc., e incluso guardarlo en el almacenamiento persistente.
Puede obtener una referencia de grano pasando la identidad de un grano al IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) método , donde T
es la interfaz de grano y key
es la clave única del grano dentro de su tipo.
En los ejemplos siguientes se muestra cómo obtener una referencia de grano para la IPlayerGrain
interfaz definida anteriormente.
Desde dentro de una clase de grano:
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
Código de cliente Orleans.
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
Para obtener más información sobre las referencias de grano, consulte el artículo de referencia de grano .
Invocación del método Grain
El modelo de programación de Orleans se basa en programación asincrónica. Partiendo de la referencia de grano del ejemplo anterior, aquí se muestra cómo efectuar una invocación del método de grano.
// 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);
Puede combinar dos o más Tasks
. La operación de unión crea un nuevo objeto Task
que se resuelve cuando se completan todos sus componentes Tasks
. Este patrón es útil cuando un grano necesita iniciar varios cálculos y esperar a que se completen todos antes de continuar. Por ejemplo, un grano de front-end que genera una página web hecha de muchos elementos podría realizar varias llamadas de back-end (una para cada parte) y recibir un Task
para cada resultado. El grano estaría entonces a la espera de la combinación de todos estos Tasks
. Cuando el conjunto Task
se resuelve, los individuales Tasks
se han completado, y se han recibido todos los datos necesarios para dar formato a la página web.
Ejemplo:
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.
Propagación de errores
Cuando un método de grano lanza una excepción, Orleans propaga esa excepción a lo largo de la pila de llamadas y entre diferentes hosts cuando sea necesario. Para que funcione según lo previsto, las excepciones deben ser serializables por Orleansy los hosts que controlan la excepción deben tener el tipo de excepción disponible. Si un tipo de excepción no está disponible, Orleans inicia la excepción como una instancia de Orleans.Serialization.UnavailableExceptionFallbackException, conservando el mensaje, el tipo y el seguimiento de pila de la excepción original.
Las excepciones producidas desde métodos de grano no hacen que el grano se desactive a menos que la excepción herede de Orleans.Storage.InconsistentStateException. Las operaciones de almacenamiento lanzan InconsistentStateException
cuando detectan que el estado en memoria del grano es incoherente con el estado de la base de datos. Aparte del control especial de InconsistentStateException
, este comportamiento es similar a iniciar una excepción de cualquier objeto .NET: las excepciones no hacen que se destruya un objeto.
Métodos virtuales
Una clase de grano puede sobrescribir opcionalmente los métodos virtuales OnActivateAsync y OnDeactivateAsync. El Orleans tiempo de ejecución invoca estos métodos tras la activación y desactivación de cada grano de la clase. Esto proporciona a su código "grain" una oportunidad para realizar operaciones de inicialización y limpieza adicionales. Una excepción producida por OnActivateAsync
produce un error en el proceso de activación.
Aunque OnActivateAsync
(si se sobrescribe) siempre se llama como parte del proceso de activación de la granulación, no se garantiza que OnDeactivateAsync
se llame en todas las situaciones (por ejemplo, en caso de fallo del servidor u otros eventos excepcionales). Por este motivo, las aplicaciones no deben confiar en OnDeactivateAsync
para realizar operaciones críticas, como conservar los cambios de estado. Úselo solo para las operaciones de mejor esfuerzo.
Consulte también
- extensiones de Grain
- de identidad de Grain
- referencias de Grain
- Persistencia de grano
- Visión general del ciclo de vida de Grain
- Colocación de granos