Orleans 2.0官方文件(閆輝的個人翻譯)——4.1 開發一個Grain
設定
在編寫實現grain類的程式碼之前,請新建一個面向.NET Standard(首選)或.NET Framework 4.6.1或更高版本的類庫專案(如果由於依賴性而無法使用.NET Standard)。grain介面和grain類可以在同一個類庫專案中定義,也可以在兩個不同的專案中定義,以便更好地分離介面和實現。無論哪種情況,專案都需要引用Microsoft.Orleans.Core.Abstractions
和Microsoft.Orleans.CodeGenerator.MSBuild
NuGet包。
有關更詳細的說明,請參閱Tutorial One - Orleans Basics
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
執行關鍵操作,例如狀態更改的持久化。應用程式應只用它於力所能及的操作。