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(CancellationToken cancellationToken = default);
Task JoinGame(IGameGrain game, CancellationToken cancellationToken = default);
Task LeaveGame(IGameGrain game, CancellationToken cancellationToken = default);
}
public class PlayerGrain : Grain, IPlayerGrain
{
private IGameGrain _currentGame;
// Game the player is currently in. May be null.
public Task<IGameGrain> GetCurrentGame(CancellationToken cancellationToken = default)
{
return Task.FromResult(_currentGame);
}
// Game grain calls this method to notify that the player has joined the game.
public Task JoinGame(IGameGrain game, CancellationToken cancellationToken = default)
{
_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, CancellationToken cancellationToken = default)
{
_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(CancellationToken cancellationToken = default);
Task JoinGame(IGameGrain game, CancellationToken cancellationToken = default);
[ResponseTimeout("00:00:05")] // 5s timeout
Task LeaveGame(IGameGrain game, CancellationToken cancellationToken = default);
}
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.
También puede especificar el tiempo de espera mediante TimeSpan parámetros de constructor:
public interface IDataProcessingGrain : IGrainWithGuidKey
{
// 2 minute timeout using hours, minutes, seconds
[ResponseTimeout(0, 2, 0)]
Task<ProcessingResult> ProcessLargeDatasetAsync(Dataset data, CancellationToken cancellationToken = default);
// 500ms timeout using TimeSpan.FromMilliseconds equivalent
[ResponseTimeout("00:00:00.500")]
Task<HealthStatus> GetHealthAsync(CancellationToken cancellationToken = default);
}
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 .
Mejores prácticas de tiempo de espera
Tenga en cuenta lo siguiente al configurar tiempos de espera:
- Los tiempos de espera por método invalidan la configuración global: un ResponseTimeoutAttribute en un método específico tiene prioridad sobre la configuración global ResponseTimeoutAttribute .
- Establecer tiempos de espera realistas: tiempos de espera base en el tiempo de ejecución esperado más una latencia de red razonable. Los tiempos de espera que son demasiado cortos provocan errores innecesarios; los tiempos de espera que son demasiado largos retrasan la detección de errores.
- Operaciones de larga duración: en el caso de las operaciones que pueden tardar mucho tiempo, considere la posibilidad de usar Orleansrecordatorios en lugar de extender los tiempos de espera indefinidamente.
- Pruebas: Pruebe el comportamiento del tiempo de espera en las pruebas de integración para asegurarse de que la aplicación gestiona TimeoutException correctamente.
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<TResult>.
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<TResult> se puede usar en lugar de Task<TResult>.
Valores devueltos de IAsyncEnumerable
Orleans admite la devolución de IAsyncEnumerable<T> desde métodos de grain, permitiendo un streaming eficiente de datos de un grano a un llamador sin cargar todo el conjunto de resultados en la memoria. Esto es útil para escenarios como:
- Devolver grandes colecciones de datos progresivamente
- Streaming de actualizaciones en tiempo real
- Procesamiento de los resultados a medida que están disponibles
Definir una interfaz de transmisión de grano
public interface IDataGrain : IGrainWithStringKey
{
// Returns a streaming sequence of items
IAsyncEnumerable<DataItem> GetAllItemsAsync();
// Can also include CancellationToken for cancellation support
IAsyncEnumerable<DataItem> GetItemsAsync(CancellationToken cancellationToken = default);
}
Implementación del método de streaming
Use la yield return instrucción o devuelva directamente una IAsyncEnumerable<T>.
public class DataGrain : Grain, IDataGrain
{
public async IAsyncEnumerable<DataItem> GetAllItemsAsync()
{
for (int i = 0; i < 1000; i++)
{
// Simulate async data retrieval
var item = await FetchItemAsync(i);
yield return item;
}
}
public async IAsyncEnumerable<DataItem> GetItemsAsync(
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
int id = 0;
while (!cancellationToken.IsCancellationRequested)
{
var item = await FetchItemAsync(id++);
yield return item;
}
}
private Task<DataItem> FetchItemAsync(int id) =>
Task.FromResult(new DataItem { Id = id });
}
[GenerateSerializer]
public class DataItem
{
[Id(0)]
public int Id { get; set; }
}
Consuma el método de streaming
var grain = client.GetGrain<IDataGrain>("mydata");
await foreach (var item in grain.GetAllItemsAsync())
{
Console.WriteLine($"Received item: {item.Id}");
// Process each item as it arrives
await ProcessItemAsync(item);
}
Configuración del tamaño del lote
Orleans procesa por lotes múltiples elementos para reducir los viajes de ida y vuelta en la red. Puede configurar el tamaño del lote mediante el método de extensión WithBatchSize.
// Request up to 50 elements per batch instead of the default 100
await foreach (var item in grain.GetAllItemsAsync().WithBatchSize(50))
{
await ProcessItemAsync(item);
}
IAsyncEnumerable frente a Orleans Streams
| Característica | IAsyncEnumerable | Orleans Flujos de datos |
|---|---|---|
| Caso de uso | Streaming de solicitud-respuesta | Mensajería pub-sub (publicación-suscripción) |
| Vida | Limitado a una sola llamada | Suscripciones persistentes |
| Direction | Solo grano al autor de la llamada | Cualquier productor a cualquier suscriptor |
| Backpressure | Integrada | Dependiente del proveedor |
| Persistencia | No | Opcional (dependiente del proveedor) |
Utiliza IAsyncEnumerable<T> si:
- Necesita un patrón de solicitud-respuesta simple con resultados de streaming.
- El autor de la llamada inicia el flujo de datos y consume todos los resultados.
- Desea soporte automático de control de flujo y cancelación.
Use Orleans Secuencias cuando:
- Necesitas mensajería pub-sub con varios suscriptores.
- Las suscripciones deben sobrevivir a la desactivación de granos
- Necesita flujos persistentes o duraderos.
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, GrainCancellationToken);
// 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