请求上下文

RequestContext这是一项Orleans功能,允许应用程序元数据(如跟踪 ID)随请求一起流动。 可以在客户端上添加应用程序元数据,并随 Orleans 请求一起流向接收端粒。 该功能由 RequestContext 命名空间中的公共静态类 Orleans 实现。 此类公开两个简单方法:

void Set(string key, object value)

使用前面的 API 在请求上下文中存储值。 该值可以是任何可序列化类型。

object Get(string key)

使用前面的 API 从当前请求上下文中检索值。

RequestContext 的后备存储是异步本地存储。 当调用方(客户端或内部 Orleans)发送请求时,调用方 RequestContext 的内容将包含在 Orleans 请求的消息中。 当粒度代码收到请求时,可以从本地 RequestContext访问该元数据。 如果粒度代码未修改 RequestContext,则它请求的任何粒度都会收到相同的元数据,依此等。

使用StartNewContinueWith计划将来的计算时,还会维护应用程序元数据。 在这两种情况下,延续操作都使用与调度代码在计算被调度时相同的元数据进行执行。 也就是说,系统会复制当前元数据并将其传递给续传,因此续传不会在调用StartNewContinueWith之后看到所做的更改。

重要

应用程序元数据不会随响应一起回流。 由于接收到响应而运行的代码(无论是在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”。

示例客户端代码

客户端可以在调用SayHelloHelloGrain方法之前,在请求上下文中设置跟踪 ID。 以下客户端代码演示如何在请求上下文中设置跟踪 ID,以及如何在HelloGrain对象上调用SayHello方法:

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!

在此示例中,客户端将跟踪 ID 设置为“example-id-set-by-client”后,再调用 SayHello 方法 HelloGrain。 Grain 从请求上下文中检索跟踪 ID 并将其记录下来。