次の方法で共有


要求コンテキスト

RequestContext は、トレース ID などのアプリケーション メタデータが要求と共に送信できるようにする Orleans の機能です。 アプリケーション メタデータはクライアントで追加でき、Orleans の要求と共に受信グレインに送信されます。 この機能は、Orleans 名前空間のパブリック静的クラス RequestContext によって実装されます。 このクラスでは、次の 2 つの簡単なメソッドが公開されています。

void Set(string key, object value)

上の API は、要求コンテキストに値を格納するために使われます。 値には、任意のシリアル化可能な型を指定できます。

object Get(string key)

上の API は、現在の要求コンテキストから値を取得するために使われます。

RequestContext 用のバッキング ストレージは非同期ローカルです。 呼び出し元 (クライアント側か Orleans 内かに関係なく) が要求を送信すると、呼び出し元の RequestContext の内容が要求の Orleans メッセージに加えられます。要求を受信したグレインのコードは、ローカルの RequestContext からそのメタデータにアクセスできます。 グレインのコードで RequestContext が変更されない場合、要求されているすべてのグレインが同じメタデータを受け取ります。

ユーザーが StartNew または ContinueWith を使って将来の計算をスケジュールすると、アプリケーション メタデータも保持されます。どちらの場合も、継続は、計算がスケジュールされた時点でスケジュール コードが持っていたのと同じメタデータを使って実行されます (つまり、システムは現在のメタデータのコピーを作成してそれを継続に渡すので、StartNew または ContinueWith の呼び出し後の変更は継続によって認識されません)。

重要

アプリケーション メタデータは、応答と共に返送されません。つまり、ContinueWith 継続内で、あるいは Task.Wait() または GetValue の呼び出しの後で、応答受信の結果として実行されるコードは、元の要求によって設定された現在のコンテキスト内で引き続き実行されます。

たとえば、クライアントのトレース ID を新しい Guidに設定するには、次を呼び出します。

RequestContext.Set("TraceId", Guid.NewGuid());

グレインのコード (またはスケジューラ スレッド上の Orleans 内で実行される他のコード) 内では、たとえばログを書き込むときに、元のクライアント要求のトレース ID を使用できます。

Logger.LogInformation(
    "Currently processing external request {TraceId}",
    RequestContext.Get("TraceId"));

シリアル化可能な object はアプリケーション メタデータとして送信される場合もありますが、大きいオブジェクトや複雑なオブジェクトがメッセージのシリアル化時間に顕著なオーバーヘッドを追加する可能性があることに注意してください。 このため、単純型 (文字列、GUID、または数値型) を使うことをお勧めします。

グレイン コードの例

要求コンテキストの使用方法を説明するために、次のグレイン コードの例を考えてみましょう。

using GrainInterfaces;
using Microsoft.Extensions.Logging;

namespace Grains;

public class HelloGrain(ILogger<HelloGrain> logger) : Grain, IHelloGrain
{
    ValueTask<string> IHelloGrain.SayHello(string greeting)
    {
        _logger.LogInformation("""
            SayHello message received: greeting = "{Greeting}"
            """,
            greeting);
        
        var traceId = RequestContext.Get("TraceId") as string 
            ?? "No trace ID";

        return ValueTask.FromResult($"""
            TraceID: {traceId}
            Client said: "{greeting}", so HelloGrain says: Hello!
            """);
    }
}

public interface IHelloGrain : IGrainWithStringKey
{
    ValueTask<string> SayHello(string greeting);
}

SayHello メソッドは、受信greeting パラメーターをログに記録し、要求コンテキストからトレース ID を取得します。 トレース ID が見つからない場合、グレインは "トレース ID なし" をログに記録します。

クライアント コードの例

クライアントは、HelloGrainSayHello メソッドを呼び出す前に、要求コンテキストでトレース ID を設定できます。 次のクライアント コードは、要求コンテキストでトレース ID を設定し、HelloGrainSayHello メソッドを呼び出す方法を示しています。

using GrainInterfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .UseOrleansClient(clientBuilder =>
        clientBuilder.UseLocalhostClustering())
    .Build();

await host.StartAsync();

var client = host.Services.GetRequiredService<IClusterClient>();

var grain = client.GetGrain<IHelloGrain>("friend");

var id = "example-id-set-by-client";

RequestContext.Set("TraceId", id);

var message = await friend.SayHello("Good morning!");

Console.WriteLine(message);
// Output:
//   TraceID: example-id-set-by-client
//   Client said: "Good morning!", so HelloGrain says: Hello!

この例では、クライアントは、HelloGrainSayHello メソッドを呼び出す前に、トレース ID を "example-id-set-by-client" に設定します。 グレインは、要求コンテキストからトレース ID を取得し、ログに記録します。