Orlens入門概念(3)客戶端
什麼是Grain客戶端?
術語“客戶”或有時“GrainClient”用於與其互動Grain
但本身不是Grain
邏輯的一部分的應用程式程式碼。客戶端程式碼在Orleans
稱為託管Silo
位置的伺服器叢集之外執行Grain
。因此,客戶端充當叢集和所有Grain
應用程式的聯結器或通道。
通常,客戶端用於前端Web伺服器以連線到Orleans
作為Grain
執行業務邏輯的中間層的叢集。在典型的設定中,前端Web伺服器:
- 接收網路請求
- 執行必要的認證和授權驗證
- 決定哪些穀物應該處理請求
- 使用Grain Client對穀物進行一次或多次方法呼叫
- 處理穀物呼叫和任何返回值的成功完成或失敗
- 傳送網路請求的響應
Grain Client的初始化
在Grain
客戶端可用於呼叫Grain
託管在Orleans
叢集中之前,需要對客戶端進行配置,初始化並連線到叢集。
通過一個ClientConfiguration
物件提供配置,該物件包含用於以程式設計方式配置客戶端的配置屬性層次結構。還有一種通過XML檔案配置客戶端的方法,但該選項將來會被棄用。更多資訊在客戶端配置指南中。在這裡,我們將簡單地使用一個助手方法來建立一個硬編碼的配置物件,用於連線到本地Silo
執行localhost
。
ClientConfiguration clientConfig = ClientConfiguration.LocalhostSilo();
一旦我們有了一個配置物件,我們就可以通過這個
ClientBuilder
類建立一個客戶端。IClusterClient client = new ClientBuilder().UseConfiguration(clientConfig).Build();
最後,我們需要呼叫Connect()
構造的客戶端物件上的方法,使其連線到Orleans
叢集。這是一個返回a的非同步方法Task
。所以我們需要等一個await
or 完成它.Wait()
。
await client.Connect();
呼叫Grain
從客戶端呼叫Grain方法沒啥特殊的地方,從內作出這樣的呼叫Grain
程式碼。在這兩種情況下使用同樣的GetGrain<T>(key)
T
目標Grain
介面在哪裡)來獲取Grain
引用。細微的區別在於我們呼叫的工廠物件GetGrain
。在客戶端程式碼中,我們通過連線的客戶端物件來實現。IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
Task t = player.JoinGame(game)
await t;
對一個Grain
方法的呼叫返回一個Task
或一個Task<T>
按照糧食介面規則的要求。客戶端可以使用await
關鍵字非同步等待返回Task
而不阻塞執行緒,或者在某些情況下Wait()
阻止當前執行執行緒的方法。
Grain
從客戶端程式碼和另一個客戶端程式碼進行呼叫的主要區別在於Grain
單執行緒執行模型Grain
。穀物被Orleans
執行時限制為單執行緒,而客戶端可能是多執行緒的。 Orleans
在客戶端沒有提供任何這樣的保證,因此使用任何適合其環境的同步構造 - 鎖,事件Tasks
等,由客戶來管理自己的併發。
接收通知
有些情況下,簡單的請求 - 響應模式不夠,客戶端需要接收非同步通知。例如,使用者可能希望在她正在關注的某人釋出新訊息時收到通知。
觀察者就是這樣一種機制,可以使客戶端物件暴露在類似於Grain
目標的物件中以被呼叫Grain
。呼叫觀察員不會提供成功或失敗的任何指示,因為它們是作為單向最佳努力訊息傳送的。因此,必要時,應用程式程式碼負責在觀察者之上構建更高級別的可靠性機制。
另一種可用於向客戶端傳遞非同步訊息的機制是<a href="http://dotnet.github.io/orleans/Documentation/%3Ccode%20class=" defaulcode"="" style="background-color: transparent; color: rgb(51, 122, 183); cursor: pointer;">Orleans-Streams / index.html“> Streams。Streams揭示單個訊息傳遞成功或失敗的跡象,並因此啟用可靠的通訊回到客戶端。
例子
下面是一個客戶端應用程式的示例的擴充套件版本,該應用程式連線到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)
{
RunWatcher().Wait();
// Block main thread so that the process doesn't exit.
// Updates arrive on thread pool threads.
Console.ReadLine();
}
static async Task RunWatcher()
{
try
{
// Connect to local silo
var config = ClientConfiguration.LocalhostSilo();
var client = new ClientBuilder().UseConfiguration(config).Build();
await client.Connect();
// Hardcoded player ID
Guid playerId = new Guid("{2349992C-860A-4EDA-9590-000000000006}");
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
IGameGrain game = null;
while (game == null)
{
Console.WriteLine("Getting current game for player {0}...", playerId);
try
{
game = await player.GetCurrentGame();
if (game == null) // Wait until the player joins a game
{
await Task.Delay(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();
await game.SubscribeForGameUpdates(
await client.CreateObjectReference<IGameObserver>(watcher));
Console.WriteLine("Subscribed successfully. Press <Enter> to stop.");
}
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>
class GameObserver : IGameObserver
{
// Receive updates
public void UpdateGameScore(string score)
{
Console.WriteLine("New game score: {0}", score);
}
}
}
}