请求上下文
RequestContext 是 Orleans 的一项功能,它使应用程序元数据(例如跟踪 ID)能够与请求一起流动。 可以在客户端添加应用程序元数据;它们将与 Orleans 请求一起流向接收方 grain。 该功能由 Orleans 命名空间中的公共静态类 RequestContext
实现。 此类公开两个简单方法:
void Set(string key, object value)
上面的 API 用于在请求上下文中存储一个值。 该值可以是任何可序列化类型。
object Get(string key)
上面的 API 用于从当前请求上下文中检索一个值。
RequestContext
的后备存储是异步本地存储。 当某个调用方(不管是客户端还是 Orleans 内部)发送请求时,调用方的 RequestContext
的内容将包含在该请求的 Orleans 消息中;当 grain 代码接收请求时,可以从本地 RequestContext
访问该元数据。 如果 grain 代码未修改 RequestContext
,则它请求的任何 grain 都将收到相同的元数据,依此类推。
当使用 StartNew 或 ContinueWith 计划将来的计算时,也会保留应用程序元数据;在这两种情况下,延续将使用计划代码在计划计算时具有的相同元数据来执行(也就是说,系统会复制当前元数据并将其传递给延续,因此在调用 StartNew
或 ContinueWith
后,延续不会看到更改)。
重要
应用程序元数据不会随响应一起流回;也就是说,由于接收响应(在 ContinueWith
延续中或者在调用 Task.Wait() 或 GetValue
之后)而运行的代码仍将在原始请求设置的当前上下文中运行。
例如,若要将客户端中的跟踪 ID 设置为新 Guid
ID,请调用:
RequestContext.Set("TraceId", Guid.NewGuid());
在 grain 代码(或者在 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”。
示例客户端代码
在调用SayHello
HelloGrain
方法之前,客户端能够在请求上下文中设置跟踪 ID。 以下客户端代码演示如何在请求上下文中设置跟踪 ID 并调用 SayHello
方法 HelloGrain
:
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!
在此示例中,客户端在调用 SayHello
方法 HelloGrain
之前将跟踪 ID 设置为“example-id-set-by-client”。 粒度从请求上下文中检索跟踪 ID 并记录它。