ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Orleans RequestContext 实战指南:在 grain 调用链中传递关联 ID、租户与授权元数据

Orleans RequestContext 实战指南:在 grain 调用链中传递关联 ID、租户与授权元数据 后端微服务【免费下载链接】orleansCloud Native application framework for .NET项目地址https://gitcode.com/gh_mirrors/or/orleans点击查看免费下载RequestContext是 Orleans 运行时提供的一块随请求流动的元数据载体调用方在发起 grain 调用前写入条目运行时自动将其复制进请求消息接收方 grain 与下游调用都能读取同一份上下文。本文基于官方文档与仓库源码完整讲解其 API、传播机制、安全边界、与放置placement及调用链可重入性的交互帮助你用它承载关联 IDcorrelation ID、租户 ID 与授权上下文等应用元数据。RequestContext 是什么在 Orleans.Runtime.RequestContext 的源码注释中它被定义为当前正在处理请求的相关信息集合且明确设计为可供应用代码直接使用。它是一个属性包property bag某些值由运行时默认提供例如内部用于调度控制的重入标识其余值由应用代码写入随请求自动传播。典型用途包括关联 IDcorrelation ID把一次用户操作在客户端、多个 grain、多次调用间的日志串联起来租户 IDtenant ID多租户应用在 grain 内快速识别当前租户授权上下文authorization context由可信应用代码建立的调用方身份信息。源码中还保留了两个内部保留键应用代码不应直接操作常量值用途CALL_CHAIN_REENTRANCY_HEADER#CCR携带当前调用链的重入标识Guid见调用链可重入性一节PING_APPLICATION_HEADERPing用于运行时内部的 Ping 请求标记设置与读取最小可用示例完整的示例代码位于 RequestsAndVersioningSnippets.csDocumentation.Grains.RequestContextExamples命名空间。调用方写入上下文再发起调用RequestContext.Set(trace-id, Guid.NewGuid().ToString(N)); IOrderGrain order grainFactory.GetGrainIOrderGrain(order-42); await order.Submit();接收方 grain 读取该值public sealed class OrderGrain( ILoggerOrderGrain logger) : Grain, IOrderGrain { public Task Submit() { string? traceId RequestContext.Get(trace-id) as string; logger.LogInformation( Submitting order with trace ID {TraceId}, traceId); return Task.CompletedTask; } }两个使用要点文档明确强调值必须可被 Orleans 序列化。因为请求上下文会被放进请求消息跨 silo 传输任何不可序列化的对象都会在调用链路上失败值应尽量小。上下文随每个请求消息携带条目越多、值越大网络开销越高。Get返回object?因此示例中读取后需要as string进行类型转换从源码 RequestContext.Get 看它只是从当前AsyncLocal中的字典取出值找不到键时返回null。静态 API 全览Get / Set / Remove / ClearRequestContext是一个静态类全部通过静态方法操作管理示例同样来自文档片段object? value RequestContext.Get(tenant-id); RequestContext.Set(tenant-id, tenant-17); bool removed RequestContext.Remove(tenant-id); RequestContext.Clear();完整 API 及语义如下对应源码 RequestContext.csAPI签名说明Get(string key)object?读取指定键的值不存在时返回nullSet(string key, object value)void写入或更新指定键Remove(string key)bool移除指定键若该键原本存在则返回trueClear()void清空当前请求上下文KeysIEnumerablestring当前上下文中所有键EntriesIEnumerableKeyValuePairstring, object当前上下文中所有条目AllowCallChainReentrancy()ReentrancySection打开调用链可重入作用域见后文SuppressCallChainReentrancy()ReentrancySection抑制调用链可重入作用域ReentrancyIdGuid获取或设置当前调用链重入标识底层实现AsyncLocal 与写时复制从源码看RequestContext的存储核心是一行internal static readonly AsyncLocalContextProperties CallContextData new();ContextProperties内部持有一个Dictionarystring, object?。值得注意的实现细节是 Set 采用写时复制copy-on-write每次写入都会基于旧字典复制出一份新字典再替换回去。源码注释解释了原因——AsyncLocal的复制语义是共享同一个字典对象引用如果不复制字典一个执行流中的修改就会泄漏影响其他线程。Remove也遵循同样的复制策略。由此可以推断两个行为特性每个异步执行流对上下文的修改是隔离的不会相互污染频繁调用Set/Remove会带来字典复制的分配成本因此应当在需要它的操作附近尽早设置用完即清理而不是在长生命周期流程中反复增删。仓库中的 RequestContextTestsNonSiloRequired.cs 用 1000 个并行Task.Run任务验证了这种隔离性每个子任务对同名键的修改互不影响主执行流的值始终保持最初设置的结果RequestContext_CrossThread测试。传播机制从 async-local 到请求消息再到下游调用文档对传播机制的定义是请求上下文使用异步局部存储async-local storage。当代码发送 grain 调用时Orleans 会把当前条目复制进发出的请求接收方 grain 能看到这些条目而它发起的调用会把自身当前上下文继续向下游传播。这条链路的源码级证据清晰可循导出ExportRequestContextExtensions.Export 使用DeepCopier对当前字典做深拷贝返回一份独立副本装入消息MessageFactory.cs 在创建请求消息时调用RequestContextExtensions.Export(_deepCopier)并把结果赋给Message.RequestContextData序列化传输MessageSerializer.cs 在消息头带HasRequestContextData标志时将请求上下文作为消息的最后一个字段写入并读取接收端导入接收方运行时调用RequestContextExtensions.ImportRequestContextExtensions.cs清空当前上下文并载入消息中携带的条目下游继续传播grain 内再发起的调用会重复上述导出→装入→传输→导入过程因此上下文沿整条调用链逐跳向下游流动。两条重要语义调用方对上下文的修改不会回流文档明确被调用方做的修改不会随响应返回请求上下文是下游元数据downstream metadata不要把它当作返回通道。想回传数据请使用方法返回值或Response上下文是调用链视角的接收方 grain 看到的是上游传入的快照如果接收方在同一异步流里先处理了一个上游请求的上下文又处理另一个无关操作就需要主动Clear()或恢复避免把上一个请求的上下文泄漏给无关的下游调用。测试 RequestContext_ActivityId_ExportImport 直接验证了导出到消息 → 清空当前上下文 → 从消息导入 → 重新读到相同值的完整往返过程可作为理解该机制的行为基准。安全边界请求上下文是调用方提供的数据文档用专门一节强调安全问题请求上下文是调用方提供的数据。不要仅仅因为某个角色、用户 ID 或租户 ID 出现在RequestContext中就信任它。应当在可信边界建立认证并使用调用过滤器call filters或应用授权逻辑来校验访问。这意味着认证authentication必须发生在可信边界例如客户端连接层、网关或专门的认证服务而不是信任上游 grain 塞进上下文的字符串授权authorization应当在每次需要时重新校验可以用 Orleans 的调用过滤器IIncomingGrainCallFilter/IOutgoingGrainCallFilter统一检查也可以在每个 grain 方法内做应用级校验上下文里的身份信息只应作为便捷的请求属性而非安全凭证。关于身份传播与授权的完整指引见仓库文档 authentication-authorization.md。Placement 与迁移激活创建前的特殊读取路径放置placement发生在新激活activation被创建之前此时还没有任何 grain 激活在运行因此静态RequestContext在放置判定器placement directors和过滤器内部是空的。文档给出的正确读取方式是读取Orleans.Runtime.Placement.PlacementTarget.RequestContextData。源码印证了这一设计PlacementTarget 在构造时接收Dictionarystring, object requestContextData并暴露为RequestContextData属性而 PlacementService.cs 正是用触发放置的第一条消息firstMessage携带的RequestContextData来构造PlacementTargetvar target new PlacementTarget( firstMessage.TargetGrain, firstMessage.RequestContextData!, firstMessage.InterfaceType, firstMessage.InterfaceVersion);所以自定义放置逻辑若要感知请求元数据例如根据租户 ID 选择 silo应当从PlacementTarget.RequestContextData读取而不是RequestContext.Get。MigrateOnIdle 与放置提示当 grain 通过MigrateOnIdle()请求迁移时Orleans 会捕获当前请求上下文并使其对放置逻辑可用见文档Placement and migration一节。这允许应用借助请求上下文提供放置提示placement hints例如这个 grain 应迁移到租户所在的区域。文档同时提醒自定义放置逻辑属于高级运行时扩展需要谨慎使用。调用链可重入性与保留键调用链可重入call-chain reentrancy用于解决一类经典死锁grain A 调用 grain BB 又回调 A而 A 默认不可重入于是 B 的调用一直排队直到 A 完成但 A 又在等 B——形成循环死锁。RequestContext.AllowCallChainReentrancy()允许某个明确的调用路径回调到发起该链的激活中public async Task JoinRoom(string roomName) { using var scope RequestContext.AllowCallChainReentrancy(); IChatRoomGrain room GrainFactory.GetGrainIChatRoomGrain(roomName); await room.Join(this.AsReferenceIUserGrain()); }从源码看其实现正是借助请求上下文内部的#CCR键CALL_CHAIN_REENTRANCY_HEADERAllowCallChainReentrancy()RequestContext.cs若当前没有重入标识则生成一个新的Guid构造一个ReentrancySection作用域SuppressCallChainReentrancy()RequestContext.cs把重入标识临时置为Guid.EmptyReentrancySection.Dispose()作用域结束时恢复进入前的原始ReentrancyId并通知激活离开重入区段。文档对使用方式给出两条硬性约束必须使用using作用域消费返回的ReentrancySection确保在异步流程的合适位置自动恢复状态不要直接设置 Orleans 保留的上下文键即#CCR等内部机制依赖这些键的语义直接写入会破坏运行时调度。该机制的完整调度语义回合制执行、交错与可重入见 request-scheduling.md 的 Call-chain reentrancy 一节文档还提示它比给整个 grain 打[Reentrant]更窄、更可控——只放行属于该调用链的回调而不是所有请求。最佳实践小结综合官方文档与源码实现将请求上下文用好可以总结为几条实践准则尽早设置、用后即清在需要上下文的那次操作附近写入并在同一异步流执行无关操作前Clear()或恢复原值防止上下文泄漏保持小而可序列化只放必要元数据关联 ID、租户 ID、身份标识等避免放入大对象或不可序列化类型因为它会随每个请求消息传输只作下游传播不作回传通道被调用方的修改不会回流跨 grain 回传数据请走返回值不信任上下文中的身份认证在可信边界完成授权用调用过滤器或应用逻辑校验放置逻辑读PlacementTarget.RequestContextData激活尚未创建时静态RequestContext不可用可重入用作用域 API用using var scope RequestContext.AllowCallChainReentrancy();解决明确的循环回调死锁不要直接写内部保留键。掌握这些约定后你可以在 Orleans 应用中用一套统一的机制串联日志、隔离租户、传递授权属性并让自定义放置逻辑感知请求元数据——而这一切都建立在运行时对 async-local 存储、消息导出/导入与序列化的完整支持之上。赞分享后端微服务【免费下载链接】orleansCloud Native application framework for .NET项目地址https://gitcode.com/gh_mirrors/or/orleans点击查看免费下载相关推荐Orleans 客户端与 Grain 调用安全身份认证、授权边界与纵深防御实践Orleans 客户端与 Grain 调用安全身份认证、授权边界与纵深防御实践 本文聚焦 .NET 云原生框架 Orleans Orleans.slnx h后端微服务gorush中的上下文传递在协程间传递请求ID与元数据gorush中的上下文传递在协程间传递请求ID与元数据 在分布式系统中追踪请求流转路径是排查问题的关键。当一个推送请求从API网关进入gorush一个用G后端Permify 多租户架构实战用 tenant_id 隔离 Schema 与授权数据的完整指南Permify 多租户架构实战用 tenant_id 隔离 Schema 与授权数据的完整指南 本文以 Permify 官方文档 docs/use cases认证鉴权后端上一篇在 Haystack 中集成 Eden AI统一接入多供应商 Embedding 与 Chat 生成模型下一篇Lucide Solid 图标描边宽度完全指南strokeWidth 与 nonScalingStroke 用法解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表