当前位置: 首页 > 工具软件 > grain > 使用案例 >

Orleans 2.0 官方文档 —— 4.1 Grains -> 开发一个Grain

白志勇
2023-12-01

设置

在编写实现grain类的代码之前,请新建一个面向.NET Standard(首选)、或.NET Framework 4.6.1或更高版本的类库项目(如果由于依赖性而无法使用.NET Standard)。grain接口和grain类可以在同一个类库项目中定义,也可以在两个不同的项目中定义,以便更好地分离接口和实现。无论哪种情况,项目都需要引用Microsoft.Orleans.Core.AbstractionsMicrosoft.Orleans.CodeGenerator.MSBuildNuGet包。

有关更详细的说明,请参阅Tutorial One - Orleans BasicsProject Setup部分。

grain接口和类

grain之间能彼此交互,它能被外部调用其方法,这些方法是grain接口声明中的一部分。grain类实现一个或多个先前声明的grain接口。grain接口的所有方法都必须返回一个 Task(对于void方法)、一个 Task<T>或一个 ValueTask<T>(对于返回类型值的类型为T的方法)。

以下是Orleans 1.5版的Presence Service示例的摘录:

//an example of a Grain Interface
public interface IPlayerGrain : IGrainWithGuidKey
{
  Task<IGameGrain> GetCurrentGame();
  Task JoinGame(IGameGrain game);
  Task LeaveGame(IGameGrain game);
}

//an example of a Grain class implementing a Grain Interface
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 {0} joined game {1}",
           this.GetPrimaryKey(),
           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 {0} left game {1}",
           this.GetPrimaryKey(),
           game.GetPrimaryKey());

       return Task.CompletedTask;
   }
}

grain方法的返回值

返回值类型为T的grain方法,在grain接口中定义为返回一个 Task<T>。对于未标记async关键字的grain的方法,当返回值可用时,通常通过以下语句返回:

public Task<SomeType> GrainMethod1()
{
    ...
    return Task.FromResult(<variable or constant with result>);
}

一个不返回值的grain方法,实际上是一个void方法,在grain接口中定义为返回一个Task。返回Task表示异步执行和方法的完成。对于未标记async关键字的grain方法,当“void”方法完成其执行时,需要返回以下特殊值Task.CompletedTask

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

标记为async的grain的方法,直接返回值:

public async Task<SomeType> GrainMethod3()
{
    ...
    return <variable or constant with result>;
}

一个“void”的grain的方法,若被标记为async,则不返回任何值,只是在执行结束时返回:

public async Task GrainMethod4()
{
    ...
    return;
}

如果一个grain方法,接收从另一个异步方法调用的返回值,无论此异步调用是不是grain的方法,都不需要对该调用执行错误处理,它只需简单地将从该异步调用接收的Task,作为其返回值:

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

类似地,一个“void”的grain的方法,可以返回另一个调用返回给它的Task,而不是await它。

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

ValueTask<T> 可以用来代替 Task<T>。

grain引用

一个grain引用是一个代理对象,它实现了与对应的grain类相同的grain接口。它封装了目标grain的逻辑标识(类型和唯一键)。使用grain引用,来调用目标grain。每个grain引用只能用于单个grain(grain类的单个实例),但是可以为同一grain创建多个独立的引用。

由于grain引用表示目标grain的逻辑标识,因此它与grain的物理位置无关,即使在系统彻底重新启动后,也保持有效。开发人员可以像任何其他.NET对象一样使用grain引用。它可以传递给一个方法,用作方法的返回值等,甚至可以保存到持久存储中。

可以通过将grain的标识,传递给GrainFactory.GetGrain<T>(key)方法,来获得grain引用,其中,T是grain的接口,而key是该类型中grain的唯一键。

以下演示了如何获得上文中定义的IPlayerGrain接口的grain引用。

从grain类的内部:

    //construct the grain reference of a specific player
    IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);

从Orleans的client:

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

grain的方法调用

Orleans编程模型基于异步编程

使用前一个示例中的grain引用,以下是如何执行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 is completed.
players.Add(playerId);

可以join两个或更多个Tasks;join操作创建一个Task,在其所有组成任务都完成之后,该Task完成。当一个grain需要启动多个计算,并等待这些计算完成才能继续时,这种模式很有用。例如,生成网页(由多个部分组成)的前端grain,可能会产生多个后端调用,一个部分一个调用,并为每个结果接收一个Task。然后,grain将await这些Tasks的join; 当join的Task完成,则各个Task已经完成,且已经收到格式化网页所需的所有数据。

例:

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.

虚方法

grain类可以选择性地重写OnActivateAsync和OnDeactivateAsync虚方法,这些方法在类的每个grain激活和停用时,由Orleans运行时调用。这使grain代码有机会执行额外的初始化和清理操作。若OnActivateAsync抛出异常,则激活失败。虽然OnActivateAsync(如果被重写)总是作为grain激活过程的一部分被调用,但OnDeactivateAsync不保证在所有情况下都被调用,例如,在服务器故障或其他异常事件的情况下。因此,应用程序不应依赖OnDeactivateAsync来执行关键操作,例如状态更改的持久化。应用程序应只用它于力所能及的操作。

 类似资料: