Orleans Documentation

Developing a Client

一旦我们实现了grain类型,我们就可以写一个客户端应用来说使用这个类型。

下面的来自[SDK-ROOT]\Binaries\PresenceClient_ or _[SDK-ROOT]\Samples\References目录的Orleans DLL需要引入到客户端的应用工程中:

  • Orleans.dll
  • OrleansRuntimeInterfaces.dll

几乎任何客户端都会用到grain的工厂类。 GetGrain()用来得到一个特定ID的grain引用。 就像之前所说的,grain不能够显式地创建和销毁。

GrainClient.Initialize();

// 硬编码了玩家ID
Guid playerId = new Guid("{2349992C-860A-4EDA-9590-000000000006}");
IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId);

IGameGrain game = player.CurrentGame.Result;
var watcher = new GameObserver();
var observer = GrainClient.GrainFactory.CreateObjectReference<IGameObserver>(watcher);
await game.SubscribeForGameUpdates();

如果这段代码用在控制台应用的主线程,你需要在game.SubscribeForGameUpdates()返回后调用Wait(),因为await不会组织 Main()函数返回,这样会使进程退出。

阅读关键概念部分来获取更多Task执行调度和异常流的不同用法的细节。

找到或者创建grain

在通过调用GrainClient.Initialize()创建链接后,泛型工厂类中的静态方法可以用来获取一个grain的引用,例如GrainClient.GrainFactory.GetGrain<IPlayerGrain>()来获取PlayerGrain。grain接口作为类参数传递给GrainFactory.GetGrain<T>()

向grain发送消息

客户端与grain的通信编程模型跟grain之间的通信编程模型一样。 client持有一个实现了grain接口IPlayerGrain的grain引用。 它调用那个grain引用或者其他继承自IGrain的grain接口的方法,并且这些方法有异步返回值Task/Task<T> 当这些异步返回值求值得时候,客户端可以使用await关键字或者ContinueWith()方法来伫列被运行的延续体,或者使用Wait()方法阻塞当前线程。

一个客户端与grain的通信和grain与grain的通信关键的不同是单线程执行模型。 grain被Orleans调度器线支撑单线程的,但是客户端可能是多线程的。 客户端库使用TPL线程池来管理延续提或者毁掉,并且客户端根据自身环境适用什么同步机制(锁、事件、TPL任务、等等)来自由决定使用什么方法管理它自己的并发。

收到通知

有些情况下简单的消息/响应模式是不够的,并且客户端需要收到异步通知。 例如,一个用户可能想要在他粉的某人发布新消息的时候收到通知。

观察者是一个单向实现了IGrainObserver的单向异步接口,并且它的所有的方法都必须是void的。 grain通过像调用grain接口的方法一样调用观察者的方法来发送一个通知给观察者,不同是观察者方法没有返回值并且grain不会以来调用结果。 Orleans运行时将会确保单向传递通知的成功。 发布通知的grain应该提供API来添加或者删除观察者。

想要订阅通知,客户端必须首先创建一个本地的实现了观察者接口的C#对象。 然后调用grain工厂的CreateObjectReference()将C#对象转换成一个grain引用,之后会被传递给通知grain上的订阅方法。

这个模型也可以被其他的grain使用来接收异步通知。 不像客户端订阅的情况,订阅的grain只要实现观察者接口,并且把自身作为一个引用传递给自己(例如this.AsReference<IChirperViewer>)。

举例

这里是一个上面给出的客户端应用连接Orleans的扩展版本,找到玩家账号,订阅玩家所在的游戏的会话的更新,并且打印出通知,直到程序被手动关闭。

namespace PlayerWatcher
{
    class Program
    {
        /// <summary>
        /// Simulates a companion application that connects to the game
        /// that a particular player is currently part of, and subscribes
        /// to receive live notifications about its progress.
        /// </summary>
        static void Main(string[] args)
        {
            try
            {
                GrainClient.Initialize();

                // Hardcoded player ID
                Guid playerId = new Guid("{2349992C-860A-4EDA-9590-000000000006}");
                IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId);
                IGameGrain game = null;

                while (game == null)
                {
                    Console.WriteLine("Getting current game for player {0}...", playerId);

                    try
                    {
                        game = player.CurrentGame.Result;
                        if (game == null) // Wait until the player joins a game
                            Thread.Sleep(5000);
                    }
                    catch (Exception exc)
                    {
                        Console.WriteLine("Exception: ", exc.GetBaseException());
                    }
                }

                Console.WriteLine("Subscribing to updates for game {0}...", game.GetPrimaryKey());

                // Subscribe for updates
                var watcher = new GameObserver();
                game.SubscribeForGameUpdates(GrainClient.GrainFactory.CreateObjectReference<IGameObserver>(watcher)).Wait();

                // .Wait will block main thread so that the process doesn't exit.
                // Updates arrive on thread pool threads.
                Console.WriteLine("Subscribed successfully. Press <Enter> to stop.");
                Console.ReadLine();
            }
            catch (Exception exc)
            {
                Console.WriteLine("Unexpected Error: {0}", exc.GetBaseException());
            }
        }

        /// <summary>
        /// Observer class that implements the observer interface.
        /// Need to pass a grain reference to an instance of this class to subscribe for updates.
        /// </summary>
        private class GameObserver : IGameObserver
        {
            // Receive updates
            public void UpdateGameScore(string score)
            {
                Console.WriteLine("New game score: {0}", score);
            }
        }
    }
}

Next

运行一个应用