Partager via


Développer un grain

Avant d’écrire du code pour implémenter une classe de grain, créez un projet de bibliothèque de classes ciblant .NET Standard ou .NET Core (préféré) ou .NET Framework 4.6.1 ou version ultérieure (si vous ne pouvez pas utiliser .NET Standard ou .NET Core en raison de dépendances). Les interfaces grain et les classes de grain peuvent être définies dans le même projet de bibliothèque de classes, ou dans deux projets différents pour une meilleure séparation des interfaces de l’implémentation. Dans les deux cas, les projets doivent référencer le package NuGet Microsoft.Orleans.SDK.

Pour obtenir des instructions plus détaillées, consultez la section Paramétrage du projet du Tutoriel Un – Orleans Les bases.

Interfaces et classes de grain

Les grains interagissent les uns avec les autres et sont appelés de l’extérieur en appelant des méthodes déclarées dans le cadre des interfaces de grain respectives. Une classe de grain implémente une ou plusieurs interfaces de grain déclarées précédemment. Toutes les méthodes d’une interface de grain doivent retourner un Task (pour les méthodes void), un Task<TResult> ou un ValueTask<TResult> (pour les méthodes retournant des valeurs de type T).

Voici un extrait de l’exemple Orleans Presence Service :

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;
   }
}

Délai d’expiration de réponse pour les méthodes "grain"

Le runtime Orleans vous permet d’appliquer un délai d’expiration de réponse par méthode de grain. Si une méthode de grain ne se termine pas dans le délai d’expiration, le runtime lève le TimeoutException. Pour imposer un délai d’expiration de réponse, ajoutez la ResponseTimeoutAttribute à la définition de la méthode de grain de l’interface. Il est très important que l’attribut soit ajouté à la définition de méthode d’interface, et non à l’implémentation de la méthode dans la classe de grain, car le client et le silo doivent être conscients du délai d’expiration.

L’extension de l’implémentation PlayerGrain précédente montre comment imposer un délai d’attente de réponse à la méthode LeaveGame :

public interface IPlayerGrain : IGrainWithGuidKey
{
    Task<IGameGrain> GetCurrentGame();

    Task JoinGame(IGameGrain game);

    [ResponseTimeout("00:00:05")] // 5s timeout
    Task LeaveGame(IGameGrain game);
}

Le code précédent définit un délai d’expiration de réponse de cinq secondes sur la méthode LeaveGame. Lorsque vous quittez un jeu, s'il faut plus de cinq secondes, une TimeoutException est déclenchée.

Configurer le délai d’expiration de réponse

Tout comme les délais d’expiration de réponse des méthodes de grain individuels, vous pouvez configurer un délai d’expiration de réponse par défaut pour toutes les méthodes de grain. Les appels aux méthodes de grain atteindront un délai d'expiration si une réponse n’est pas reçue au cours d’une période spécifiée. Par défaut, cette période est 30 secondes. Vous pouvez configurer le délai d’expiration de réponse par défaut :

Pour plus d’informations sur la configuration de Orleans, consultez de configuration du client ou configuration du serveur.

Valeurs retournées par les méthodes de grain

Une méthode de grain qui retourne une valeur de type T est définie dans une interface de grain comme renvoyant un Task<T>. Pour les méthodes de grain non marquées avec le mot clé async, lorsque la valeur de retour est disponible, elle est généralement retournée via l’instruction suivante :

public Task<SomeType> GrainMethod1()
{
    return Task.FromResult(GetSomeType());
}

Une méthode de grain qui ne retourne aucune valeur, effectivement une méthode void, est définie dans une interface de grain comme renvoyant un Task. Le Task retourné indique l’exécution asynchrone et l’achèvement de la méthode. Pour les méthodes de grain non marquées avec le mot clé async, lorsqu’une méthode « void » termine son exécution, elle doit retourner la valeur spéciale de Task.CompletedTask:

public Task GrainMethod2()
{
    return Task.CompletedTask;
}

Une méthode de grain marquée comme async retourne directement la valeur :

public async Task<SomeType> GrainMethod3()
{
    return await GetSomeTypeAsync();
}

Une méthode de grain void marquée comme async ne retournant aucune valeur arrive simplement à la fin de son exécution.

public async Task GrainMethod4()
{
    return;
}

Si une méthode de grain reçoit la valeur de retour d’un autre appel de méthode asynchrone, que ce soit vers un grain ou non, et qu'elle n’a pas besoin d’effectuer la gestion des erreurs de cet appel, elle peut simplement renvoyer le Task qu’elle reçoit de cet appel asynchrone.

public Task<SomeType> GrainMethod5()
{
    Task<SomeType> task = CallToAnotherGrain();

    return task;
}

De même, une méthode de grain void peut retourner une Task retournée par un autre appel au lieu de l’attendre.

public Task GrainMethod6()
{
    Task task = CallToAsyncAPI();
    return task;
}

ValueTask<T> pouvez être utilisé au lieu de Task<T>.

Références de céréale

Une référence grain est un objet proxy qui implémente la même interface de grain que la classe de grain correspondante. Il encapsule l’identité logique (type et clé unique) du grain cible. Les références de grain sont utilisées pour effectuer des appels au grain cible. Chaque référence de grain est à un seul grain (une seule instance de la classe de grain), mais on peut créer plusieurs références indépendantes au même grain.

Étant donné qu’une référence de grain représente l’identité logique du grain cible, elle est indépendante de l’emplacement physique du grain et reste valide même après un redémarrage complet du système. Les développeurs peuvent utiliser des références de grain comme n’importe quel autre objet .NET. Elle peut être passée à une méthode, utilisée comme valeur de retour de méthode, etc., et même enregistrée dans un stockage persistant.

Une référence de grain peut être obtenue en passant l’identité d’un grain à la méthode IGrainFactory.GetGrain<TGrainInterface>(Type, Guid), où T est l’interface de grain et key est la clé unique du grain dans le type.

Voici des exemples d’obtention d’une référence de grain de l’interface IPlayerGrain définie ci-dessus.

Depuis l'intérieur d'une classe de grain :

IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);

Du code client Orleans.

IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);

Pour plus d’informations sur les références de grain, consultez l’article de référence sur les grains .

Appel de méthode grain

Le modèle de programmation Orleans est basé sur de programmation asynchrone. En utilisant la référence de grain de l'exemple précédent, voici comment effectuer un appel de méthode pour le grain :

// 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);

Il est possible de joindre deux ou plusieurs Tasks; l’opération de jointure crée une nouvelle Task résolue lorsque tous ses Taskconstituants sont terminés. Il s’agit d’un modèle utile lorsqu’un grain doit démarrer plusieurs calculs et attendre qu’ils se terminent avant de continuer. Par exemple, un grain frontal qui génère une page web composée de plusieurs composants peut effectuer plusieurs appels principaux, un pour chaque composant et recevoir une Task pour chaque résultat. Le grain attendrait alors que tous ces Taskssoient réunis ; lorsque la jonction Task est résolue, les Taskindividuelles ont été complétées et toutes les données nécessaires pour formater la page Web ont été reçues.

Exemple:

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.

Propagation d’erreurs

Lorsqu’une méthode de grain lève une exception, Orleans propage cette exception dans la pile appelante, à travers les hôtes si nécessaire. Pour que cela fonctionne comme prévu, les exceptions doivent être sérialisables par Orleans et les hôtes qui gèrent l’exception doivent avoir le type d’exception disponible. Si aucun type d’exception n’est disponible, l’exception est levée en tant qu’instance de Orleans.Serialization.UnavailableExceptionFallbackException, en conservant le message, le type et la trace de pile de l’exception d’origine.

Les exceptions levées à partir de méthodes de grain ne provoquent pas la désactivation du grain, sauf si l’exception hérite de Orleans.Storage.InconsistentStateException. InconsistentStateException est levée par les opérations de stockage qui découvrent que l’état en mémoire du grain est incohérent avec l’état de la base de données. Outre le traitement spécial de InconsistentStateException, ce comportement est similaire à celui de lever une exception à partir d’un objet .NET : les exceptions ne provoquent pas la destruction d’un objet.

Méthodes virtuelles

Une classe de grain peut éventuellement remplacer les méthodes virtuelles OnActivateAsync et OnDeactivateAsync ; ces méthodes sont appelées par le runtime Orleans lors de l’activation et de la désactivation de chaque grain de la classe. Cela permet au code grain d’effectuer des opérations d’initialisation et de nettoyage supplémentaires. Une exception levée par OnActivateAsync empêche le processus d’activation.

Bien que OnActivateAsync, lorsqu'il est surchargé, soit toujours appelé dans le cadre du processus d’activation du grain, il n'est pas garanti que OnDeactivateAsync soit appelé dans toutes les situations, par exemple en cas de défaillance du serveur ou d’un autre événement anormal. En raison de cela, les applications ne doivent pas s’appuyer sur OnDeactivateAsync pour effectuer des opérations critiques telles que la persistance des modifications d’état. Ils doivent l’utiliser uniquement pour les opérations optimales.

Voir aussi