開發粒紋
在您撰寫程式碼以實作粒紋類別之前,請建立以 .NET Standard 或 .Net Core (慣用) 或 .NET Framework 4.6.1 或更高版本為目標的新類別庫專案 (如果您因為相依性而無法使用 .NET Standard 或 .NET Core)。 您可在相同的類別庫專案中,或在兩個不同的專案中定義粒紋介面和粒紋類別,以便更妥善地區隔介面與實作。 不論是哪一種情況,專案都需要參考 Microsoft. Orleans.Core.Abstractions 和 Microsoft.Orleans.CodeGenerator.MSBuild NuGet 套件。
如需更徹底的指示,請參閱 教學課程一 –Orleans 基礎概念的專案設定一節。
粒紋介面和類別
粒紋會彼此互動,並透過叫用宣告為個別粒紋介面一部分的方法,從外部呼叫。 粒紋類別會實作一或多個先前宣告的粒紋介面。 粒紋介面的所有方法都必須傳回 Task (針對 void
方法)、Task<TResult> 或 ValueTask<TResult> (針對傳回型別 T
值的方法)。
以下是 Orleans 目前狀態服務範例的摘錄:
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
Task LeaveGame(IGameGrain game);
}
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 {GetPrimaryKey()} joined game {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 {GetPrimaryKey()} left game {game.GetPrimaryKey()}");
return Task.CompletedTask;
}
}
粒紋方法的回應逾時
Orleans 執行階段可讓您強制執行每個粒紋方法的回應逾時。 如果粒紋方法未在逾時內完成,則執行階段會擲回 TimeoutException。 若要強加回應逾時,請將 ResponseTimeoutAttribute
新增至介面的粒紋方法定義。 務必將屬性新增至介面方法定義,而不是新增至粒紋類別中的方法實作,因為用戶端和定址接收器都需要留意逾時。
擴充先前的 PlayerGrain
實作,下列範例示範如何在 LeaveGame
方法上強加回應逾時:
public interface IPlayerGrain : IGrainWithGuidKey
{
Task<IGameGrain> GetCurrentGame();
Task JoinGame(IGameGrain game);
[ResponseTimeout("00:00:05")] // 5s timeout
Task LeaveGame(IGameGrain game);
}
上述程式碼會在 LeaveGame
方法上設定五秒的回應逾時。 離開遊戲時,如果花費超過五秒的時間,就會擲回 TimeoutException。
設定回應逾時
與個別粒紋方法回應逾時非常類似,您可以為所有粒紋方法設定預設回應逾時。 如果未在指定的時段內收到回應,則對粒紋方法的呼叫將會逾時。 根據預設,此期間為 30 秒。 您可以設定預設回應逾時:
- 藉由在外部用戶端上,在 ClientMessagingOptions 上設定 ResponseTimeout。
- 藉由在伺服器上,在 SiloMessagingOptions 上設定 ResponseTimeout。
如需設定 Orleans 的詳細資訊,請參閱用戶端設定或伺服器設定。
從粒紋方法傳回值
會在粒紋介面中將傳回型別 T
值的粒紋方法定義為傳回 Task<T>
。
對於未以 async
關鍵字標示的粒紋方法,當傳回值可用時,通常會透過下列陳述式傳回:
public Task<SomeType> GrainMethod1()
{
return Task.FromResult(GetSomeType());
}
未傳回任何值且實際上為 void 方法的粒紋方法會在粒紋介面中定義為傳回 Task
。 傳回的 Task
表示方法的非同步執行和完成。 對於未以 async
關鍵字標示的粒紋方法,當「void」方法完成其執行時,其必須傳回 Task.CompletedTask 的特殊值:
public Task GrainMethod2()
{
return Task.CompletedTask;
}
標示為 async
的粒紋方法會直接傳回值:
public async Task<SomeType> GrainMethod3()
{
return await GetSomeTypeAsync();
}
標示為 async
的 void
粒紋方法不會傳回任何值,只會在其執行結束時傳回:
public async Task GrainMethod4()
{
return;
}
如果粒紋方法從另一個非同步方法呼叫接收傳回值,不論目標是否是粒紋,而且不需要執行該呼叫的錯誤處理,其可以直接傳回從該非同步呼叫接收的 Task
:
public Task<SomeType> GrainMethod5()
{
Task<SomeType> task = CallToAnotherGrain();
return task;
}
同樣地,void
粒紋方法可以傳回另一個呼叫傳回給自己的 Task
,而不是等候。
public Task GrainMethod6()
{
Task task = CallToAsyncAPI();
return task;
}
可以使用 ValueTask<T>
代替 Task<T>
。
粒紋參考
粒紋參考是 Proxy 物件,可實作與對應粒紋類別相同的粒紋介面。 其會封裝目標粒紋的邏輯身分識別 (型別和唯一索引鍵)。 粒紋參考用於對目標粒紋進行呼叫。 每個粒紋參考都是單一粒紋 (粒紋類別的單一執行個體),但使用者可以建立相同粒紋的多個獨立參考。
由於粒紋參考代表目標粒紋的邏輯身分識別,其與粒紋的實際位置無關,即使在系統完全重新開機之後仍有效。 開發人員可以使用類似任何其他 .NET 物件的粒紋參考。 可以將其傳遞至方法、用做為方法傳回值等等,甚至是儲存至持續性儲存體。
將粒紋的身分識別傳遞至 IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) 方法 (其中 T
是粒紋介面),而 key
是型別內粒紋的唯一索引鍵,即可取得粒紋參考。
以下是如何取得上述 IPlayerGrain
介面的粒紋參考範例。
從粒紋類別內部:
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
從 Orleans 用戶端程式碼。
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
如需粒紋參考的詳細資訊,請參閱粒紋參考文章。
粒紋方法叫用
Orleans 程式設計模型是以非同步程式設計為基礎。 使用上一個範例的粒紋參考,以下是如何執行粒紋方法叫用的方法:
// 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 has completed.
players.Add(playerId);
可以聯結兩個或多個 Tasks
;聯結作業會建立新的 Task
,當其所有組成的 Task
項目都完成時,就會加以解析。 當粒紋需要啟動多個計算,並等候所有計算完成再繼續之前,這是有用的模式。 例如,產生許多組件的網頁前端粒紋可能會進行多個後端呼叫、每個組件各呼叫一個,並接收每個結果的 Task
。 接著,粒紋會等候所有這些 Tasks
的聯結;當聯結 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.
錯誤傳播
當粒紋方法擲回例外狀況時,Orleans 會將該例外狀況傳播至呼叫堆疊 (視需要跨越主機)。 若要如預期般運作,例外狀況必須可由 Orleans 序列化,而處理例外狀況的主機必須具有可用的例外狀況類型。 如果無法取得例外狀況類型,則會擲回例外狀況作為 Orleans.Serialization.UnavailableExceptionFallbackException 的執行個體,並保留原始例外狀況的訊息、類型和堆疊追蹤。
除非例外狀況繼承自 Orleans.Storage.InconsistentStateException,否則從粒紋方法擲回的例外狀況不會造成粒紋停用。 發現粒紋的記憶體內部狀態與資料庫中的狀態不一致的儲存作業會擲回 InconsistentStateException
。 除了 InconsistentStateException 的特殊情況,此行為類似於從任何 .NET 物件擲回例外狀況:例外狀況不會造成物件終結。
虛擬方法
粒紋類別可以選擇性地覆寫 OnActivateAsync 和 OnDeactivateAsync 虛擬方法;這些會在啟用和停用類別的每個粒紋時由 Orleans 執行階段叫用。 這可讓粒紋程式碼有機會執行額外的初始化和清除作業。 OnActivateAsync
擲回的例外狀況會導致啟用處理序失敗。
如果遭覆寫,一律會以粒紋啟用處理序的一部分呼叫 OnActivateAsync
,但不保證會在所有情況 (例如,伺服器失敗或其他異常事件) 下呼叫 OnDeactivateAsync
。 因此,應用程式不應該依賴 OnDeactivateAsync
執行重要作業,例如狀態變更的持續性。 他們應該只在盡力而為的情況下使用。