阅读本文之前,请阅读Grains。
Grain接口
Grain之间交互是通过调用声明成相应grain接口的一部分的方法来实现。
一个grain类实现一个或多个之前声明过的grain接口。
所有grain接口的方法都必须返回一个Task
(对于void
的方法)或者一个Task<T>
(对于返回值类型为T
的方法)。
下面是一段来自在线服务示例的节选:
//一个Grain接口的例子
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
Task LeaveGame(IGameGrain game);
}
//一个实现了Grain接口的Grain类的例子
public class PlayerGrain : Grain, IPlayerGrain
{
private IGameGrain currentGame;
// 目前在在线的游戏玩家。可能为null。
public Task<IGameGrain> GetCurrentGame()
{
return Task.FromResult(currentGame);
}
// 游戏grain调用这个方法,来通知有玩家加入了游戏。
public Task JoinGame(IGameGrain game)
{
currentGame = game;
Console.WriteLine("Player {0} joined game {1}", this.GetPrimaryKey(), game.GetPrimaryKey());
return TaskDone.Done;
}
// 游戏grain调用这个方法,来通知有玩家离开了游戏。
public Task LeaveGame(IGameGrain game)
{
currentGame = null;
Console.WriteLine("Player {0} left game {1}", this.GetPrimaryKey(), game.GetPrimaryKey());
return TaskDone.Done;
}
}
Grain引用
一个Grain的引用是一个代理对象,实现了与相应grain类同样的grain接口。它使用异步消息提供与其他grain的全双工通信,就像Orleans客户端。
一个grain引用能通过传递一个grain的标识给GrainFactory.GetGrain<T>()
方法来创建,T是grain接口。开发者能够想使用其他.NET对象那样使用grain引用。它能够传递给一个方法、用作方法的返回值等等。
下面的例子是如何创建一个之前定义过的IPlayerGrain
接口的grain引用。
Orleans客户端代码:
//创建特定玩家的grain引用
IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId);
一个Grain类的内部:
//创建特定玩家的grain引用
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
Grain方法的调用
Orleans编程模型是基于async和await的异步编程。关于这个主题的详细文章在此。
使用之前例子中的grain引用,下面是一个grain方法调用的例子:
//异步调用一个grain方法
Task joinGameTask = player.JoinGame(this);
//`await`关键字有效地把方法其余的部分转变成一个闭包,在这个正在等待的Task完成后,这个闭包将会异步执行,这样不会阻塞正在执行的线程。
await joinGameTask;
//C#编译器会将下面这一行转换成一个闭包。
players.Add(playerId);
可以将两个或者多个Task
联合起来;这种联合会创建一个新的Task
,这个新的Task
将在所有子Task
完成后被完成。对于一个grain需要启动多个计算并且等待他们全都完成处理的时候这是一个非常有用的模式。
例如,一个生成由多个部分组成的web页面的前端grain可能进行多次后端调用,每个部分一次,并且每个结果收到一个Task
。
grain将会等在所有这些Task
的组合;当这个组合Task
完成,每一个独立的Task
就完成了,并且所有用来组成web页的数据都已经收到了。
例子:
List<Task> tasks = new List<Task>();
ChirperMessage chirp = CreateNewChirpMessage(text);
foreach (IChirperSubscriber subscriber in Followers.Values)
{
tasks.Add(subscriber.NewChirpAsync(chirp));
}
Task joinedTask = Task.WhenAll(tasks);
await joinedTask;
TaskDone.Done实用属性
没有方便返回一个已经完成的"void"Task
的“标准”方法,所以Orleans示例代码使用TaskDone.Done
来达到这个目的。