グレインを開発する
グレイン クラスを実装するコードを記述する前に、.NET Standard、.Net Core (推奨)、または .NET Framework 4.6.1 以降をターゲットとする新しいクラス ライブラリ プロジェクトを作成します (依存関係が原因で .NET Standard または .NET Core を使用できない場合)。 グレイン インターフェイスとグレイン クラスは、同じクラス ライブラリ プロジェクトまたは 2 つの異なるプロジェクトで定義して、インターフェイスの実装からより適切に分離できます。 どちらの場合も、プロジェクトは Microsoft.Orleans.Core.Abstractions と Microsoft.Orleans.CodeGenerator.MSBuild NuGet パッケージを参照する必要があります。
詳細な手順については、「チュートリアル 1 – Orleans の基本」の「プロジェクトのセットアップ」セクションを参照してください。
グレイン インターフェイスとクラス
グレインは相互に対話し、それぞれのグレイン インターフェイスの一部として宣言されたメソッドを呼び出すことによって外部から呼び出されます。 グレイン クラスは、以前に宣言された 1 つ以上のグレイン インターフェイスを実装します。 グレイン インターフェイスのすべてのメソッドは、Task (void
メソッドの場合)、Task<TResult>、または ValueTask<TResult> (T
型の値を返すメソッドの場合) を返す必要があります。
Orleans バージョン 1.5 Presence Service サンプルからの抜粋を次に示します。
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;
}
}
グレイン メソッドから値を返す
型 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>
の代わりに使用できます。
グレイン参照
グレイン参照は、対応するグレイン クラスと同じグレイン インターフェイスを実装するプロキシ オブジェクトです。 ターゲット グレインの論理 ID (型と一意のキー) をカプセル化します。 グレイン参照は、ターゲット グレインを呼び出すために使用されます。 各グレイン参照は 1 つのグレイン (グレイン クラスの 1 つのインスタンス) に対して行われますが、同じグレインに独立した複数の参照を作成できます。
グレイン参照はターゲット グレインの論理 ID を表すため、グレインの物理的な場所とは無関係であり、システムの完全な再起動後も有効なままです。 開発者は、他の .NET オブジェクトと同様にグレイン参照を使用できます。 また、メソッドに渡したり、メソッドの戻り値として使用したり、永続ストレージに保存したりすることもできます。
グレイン参照は、グレインの ID をT
がグレイン インターフェイスであり、key
が型内部のグレインの一意のキーである IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) メソッドに渡すことによって取得できます。
上で定義した 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);
2 つ以上の Tasks
の結合が可能です。結合操作では、Task
のすべての構成要素が完了したときに解決される新しい Task
が作成されます。 これは、グレインが複数の計算を開始し、それらのすべてが完了するのを待ってから続行する必要がある場合に便利なパターンです。 たとえば、多くのパーツで構成された Web ページを生成するフロントエンド グレインは、各パーツに 1 つずつ複数のバックエンド呼び出しを行い、結果ごとに Task
を受け取る可能性があります。 その後、グレインはこれらすべての Tasks
結合を待機します。結合 Task
が解決されると、個々の Task
が完了し、Web ページの書式設定に必要なすべてのデータを受信します。
例:
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.
仮想メソッド
グレイン クラスは、必要に応じて OnActivateAsync および OnDeactivateAsync 仮想メソッドをオーバーライドできます。これらは、クラスの各グレインのアクティブ化と非アクティブ化を行う場合に Orleans ランタイムで呼び出されます。 これにより、グレイン コードは、追加の初期化操作とクリーンアップ操作を実行できるようになります。 OnActivateAsync
によってスローされた例外は、アクティブ化プロセスに失敗します。
オーバーライドされた場合、OnActivateAsync
は常にグレイン アクティブ化プロセスの一部として呼び出されますが、サーバーの故障やその他の異常なイベントが発生した場合など、すべての状況で OnDeactivateAsync
が呼び出されることは保証されていません。 そのため、アプリケーションは、状態の変更の永続化などの重要な操作を実行するために OnDeactivateAsync
に依存しないようにする必要があります。 ベスト エフォート操作にのみ使用する必要があります。