AsyncLocal<T>在链路追踪中的应用

AsyncLocal在多线程项目中的应用及实现原理 1、AsyncLocal<T>本身并不存储数据而是利用执行上下文来存储和管理数据,AsyncLocal<T>自身的作用更像是一个key,执行上下文使用这个key来存取数据,数据保存在 ExecutionContext 实例的 m_localValues 的私有字段上,字段类型定义是 IAsyncLocalMap ,且具体的实现类型随着元素数量的变化而变化。 阅读详情

🚀 优质资源分享 🚀

学习路线指引(点击解锁)知识定位人群定位
🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

前言

在项目生产中日志的记录是必不可少的,在.net项目中,要说日志组件,log4net绝对可有一席之地,随着公司业务的发展,微服务则必定无可避免。在跨服务中通过日志进行分析性能或者排查故障点,如何快速定位日志尤为关键。链路追踪技术的出现正是解决这些痛点的。
分布式链路追踪需要收集单次请求所经过的所有服务,而且为了知道请求细节,还需要将具体的业务日志进行串联,而这一切的基础就是要通过一个traceid从头传到尾,相当于将该次请求过程产生的所有日志都关联其traceid,事后排查问题只需要知道traceid,就可以在日志中拉出与之关联的所有日志。

当然不是所有的公司都需要链路追踪,对于一些小公司,就几个单体系统,压根不需要这些。比如我们使用log4net时,会在日志模板中加入ThreadId,例如这样的模板

"%date [%thread] %-5level - %message%newline"

虽然并发高时我们多个用户的请求日志都掺杂在一起,但是我们依然可以根据线程号将该次请求的日志进行串联。这在大多时候都很好的解决了我们的问题。

老传统做法

即使在体量不大的系统中上面的线程号很好用了,但是哪有一点不用多线程的业务场景呢,当一次请求进来后可能会开多个异步线程去执行,那上面的线程号就显得力不从心了,就是说没法一下将相干日志提取出来了。

但是这难不倒我们,我们可以在业务开始时自定义一个随便字符串作为该次请求的唯一标识,然后将该变量通过参数传给下游方法,下游方法也将其一层一层接力传下去,在打印日志时都将该字段进行输出,这个办法很多人都用过吧。

AspNetCore的TraceIdentifier

难道没有一种优雅的方式能将我们某次请求的过程(包括多线程)进行串联起来的唯一标识吗?
在ASPNetCore中其实一直有个不起眼的属性HttpContext.TraceIdentifier,可以说他就是框架给我们提供的traceid,我们可以在所需要的地方都注入HttpContext来获取该参数,当然不许那么麻烦,只需要给日志组件获取到该值,在任何leave的日志输出时日志组件将其输出即可,这个完全没问题,大家可以去深入研究,有些日志组件可以直接配置就可以输出该TraceIdentifier值到每一条日志中,也可以将其使用到跨应用调用时传递到下游服务,如http请求可以通过header携带该值,下游从header中获取并作为它自己的TraceIdentifier继续传递。

AsyncLocal在链路追踪的应用

ThreadLoacl倒是熟悉,是每个线程之间隔离的,每个线程操作的都是自己线程的对象,能做到各个线程或不影响。AsyncLocal并不是一个新特性,只是用的场景不多,很少被使用

定义

Represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method.

表示对于给定异步控制流(如异步方法)是本地数据的环境数据。

示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
    static AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();

    static ThreadLocal<string> _threadLocalString = new ThreadLocal<string>();

    static async Task AsyncMethodA()
    {
        // Start multiple async method calls, with different AsyncLocal values.
        // We also set ThreadLocal values, to demonstrate how the two mechanisms differ.
        _asyncLocalString.Value = "Value 1";
        _threadLocalString.Value = "Value 1";
        var t1 = AsyncMethodB("Value 1");

        _asyncLocalString.Value = "Value 2";
        _threadLocalString.Value = "Value 2";
        var t2 = AsyncMethodB("Value 2");

        // Await both calls
        await t1;
        await t2;
     }

    static async Task AsyncMethodB(string expectedValue)
    {
        Console.WriteLine("Entering AsyncMethodB.");
        Console.WriteLine(" Expected '{0}', AsyncLocal value is '{1}', ThreadLocal value is '{2}'", 
                          expectedValue, _asyncLocalString.Value, _threadLocalString.Value);
        await Task.Delay(100);
        Console.WriteLine("Exiting AsyncMethodB.");
        Console.WriteLine(" Expected '{0}', got '{1}', ThreadLocal value is '{2}'", 
                          expectedValue, _asyncLocalString.Value, _threadLocalString.Value);
    }

    static async Task Main(string[] args)
    {
        await AsyncMethodA();
    }
}
// The example displays the following output:
// Entering AsyncMethodB.
// Expected 'Value 1', AsyncLocal value is 'Value 1', ThreadLocal value is 'Value 1'
// Entering AsyncMethodB.
// Expected 'Value 2', AsyncLocal value is 'Value 2', ThreadLocal value is 'Value 2'
// Exiting AsyncMethodB.
// Expected 'Value 2', got 'Value 2', ThreadLocal value is ''
// Exiting AsyncMethodB.
// Expected 'Value 1', got 'Value 1', ThreadLocal value is ''

简单理解,就是对该变量赋值后,之影响自己个自己的子线程,即当前线程发起的其他线程,包括线程池中的线程,都能获取到该值,而子线程修改该值,对父线程来说是无影响的。而这种特性貌似就是我们寻找那种能够优雅标记出同一次请求的特性。定义一个全局变量,在每次请求的起点对该变量赋值一个随机字符串,然后本次请求涉及到的所有线程访问该值,都是我们在入口赋的值。

项目应用

我们可以在任意地方定义一个全局变量,最好是放到LogHelper之中

AspNet4

public static class LogHelper{
    public static AsyncLocal<string> Traceid = new AsyncLocal<string>();
    ...
}

在授权过滤器中对该值进行赋值,一般授权过滤最先执行,可作为请求的入口点

LogHelper.TraceId.Value = Guid.NewGuid().ToString();

在log4net的LogHelper中使用,日志模板为

"%date [%property{trace}] [%thread] %-5level - %message%newline"

public static void Info(object message)
{
    ThreadContext.Properties["trace"] = TraceId.Value;
    Loger.Info(message);
}
...

AspNetCore

注册中间件进行设置值,将自己的中间件注册靠前点

app.Use(delegate (HttpContext ctx, RequestDelegate next)
{
    LogHelper.TraceId.Value = ctx.TraceIdentifier;
    return next(ctx);
});

经验证与预期符合,该实现方式不依赖AspnetCore框架HttpContext.TraceIdentifier,提供一种实现链路追踪中传递TraceId的一种思路,如有不正确之处欢迎指正,如果该思路对您有帮助,请点赞分享。

  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角**【[推荐](javascript:void(0)😉】**一下。
C# AsyncLocal原理与实战:异步上下文传递与线程安全实践 在多线程与异步编程中,线程间数据传递是常见挑战。传统静态变量存在线程安全问题,而参数层层传递在异步场景下变得笨拙。其核心原理基于执行上下文流动机制,确保数据在异步控制流中保持逻辑隔离。这一技术为Web请求上下文管理、分布式追踪等场景提供了优雅解决方案。通过AsyncLocal,开发者可以安全传递TraceId、用户身份等横切关注点,避免上下文污染。本文结合实战案例,深入解析其与ThreadLocal的本质区别,并探讨值类型陷阱、ConfigureAwait影响等高级主题,帮助构建健壮的异步应用架构。 阅读详情

相关推荐

AspNetCore服务框架扩展:精准切片式服务解析与上下文治理

在.NET微服务架构中,依赖注入容器(IServiceProvider)是服务生命周期管理的核心机制,但其默认行为难以满足灰度路由、链路追踪透传、动态服务解析等高级治理需求。本文深入剖析AspNetCore服务解析原理,揭示HttpContext初始化时机、AsyncLocal线程本地存储边界、以及编译期服务依赖图生成等底层机制。通过构建轻量级服务运行时环境(Service Runtime Environment),实现不侵入Kestrel、不重写中间件管道的‘精准切片’扩展,支持基于请求上下文的服务实例动

diedangxiang4092的博客 462

浅谈AsyncLocal,我们应该知道的那些事儿

【导读】最近查看有关框架源码,发现AsyncLocal这玩意水还挺深,于是花了一点功夫去研究,同时对比ThreadLocal说明二者区别以及在何时场景下使用AsyncLocal或Thre...

dotNET跨平台 2581

手写.NET服务框架:从Kestrel底层到零拷贝请求处理

服务框架是构建高可靠、高性能后端系统的基石,其核心在于对生命周期管理、依赖注入、HTTP通信与可观测性的深度掌控。传统ASP.NET Core框架虽功能完备,但在等保合规、边缘计算、物联网长连接等场景下,常因抽象层级过高而难以满足安全链路不可侵入、调度粒度不可控等硬性要求。本文基于.NET 6 LTS底层API,聚焦Kestrel通信内核与Span<T>内存模型,详解如何绕过HostBuilder、重构去反射DI容器、实现零拷贝HTTP请求解析,并集成动态配置热更新、无侵入日志埋点与分层健康检查。关键技术点

baochuanying9531的博客 305

c#: AsyncLocal的使用

AsyncLocal介绍和使用实例

火焰 3347

.NET Core异步链路追踪实践与OpenTelemetry集成

分布式系统中的链路追踪是确保系统可观测性的关键技术,其核心原理是通过唯一标识符串联跨服务调用。在.NET Core的异步编程模型中,传统同步日志会因线程切换丢失上下文,此时需要借助OpenTelemetry等工具实现跨线程追踪。通过Activity与AsyncLocal的配合,可以构建完整的调用链,这对诊断微服务场景下的超时、异常等问题具有重要价值。本文以电商支付失败案例为切入点,详细演示了如何通过OpenTelemetry SDK实现数据库查询、HTTP调用、消息队列等场景的全链路监控,并特别针对EF C

weixin_30596165的博客 326

大型ASP.NET系统架构选型实战指南:3 Tier、SOA与消息总线落地要点

在企业级Web应用开发中,分层架构(Layered Architecture)是理解系统组织方式的基础概念;而当业务规模扩大、并发增长、集成增多时,必须升级到物理隔离的多层架构(N-Tier),其核心在于通过Presentation/Application/Data三层的独立部署与标准化通信实现弹性伸缩。该架构天然支撑MVC模式对UI层的工程化治理,并为面向服务架构(SOA)提供服务边界基础——SOA将业务能力封装为可发现、可治理、契约驱动的服务单元,配合消息总线保障跨系统最终一致性。本文聚焦.NET技术栈

baochuanying9531的博客 476

.NET Core对接用友T+ OpenAPI实战:从鉴权到业务集成的避坑指南

在企业系统集成领域,API(应用程序编程接口)是实现不同软件间数据互通的核心技术。其原理是通过预定义的协议和格式,允许一个系统安全地调用另一个系统的功能或访问数据。这种技术对于打破信息孤岛、实现业务流程自动化具有重要价值,尤其在制造业中,MES(制造执行系统)与ERP(企业资源计划)系统的对接是典型应用场景。本文以用友T+这一广泛使用的ERP系统为例,深入剖析其OpenAPI的对接实战。内容涵盖OAuth 2.0鉴权流程的细节处理、业务单据的字段映射与数据转换,并针对接口调用中常见的超时、重试等稳定性问题,

weixin_30740295的博客 345

老赵点滴:面向.NET开发者的工程化成长指南

编程之美并非抽象概念,而是可测量、可验证、可传承的工程实践;.NET开发者成长路径不应止步于语法掌握或框架调用,而需深入运行时机制、内存模型与云原生约束。本文从CLR底层原理出发,解析JIT优化、GC代际策略、Span<T>栈语义等关键技术本质,揭示async/await上下文传播、HttpClient连接泄漏、EF Core状态机等高频生产问题的根因。结合真实电商、金融、工业HMI等多行业案例,提供带数据验证的性能调优方案、故障诊断命令链与AOT反射配置范式,助力开发者构建跨层级穿透力——从读懂错误日志,

dejz8829的专栏 536

云原生可观测性盲区突破:用OpenTelemetry .NET 9 SDK实现分布式追踪精度达99.99%,附Prometheus告警阈值公式

突破云原生可观测性盲区,基于.NET 9云原生容器化环境,集成OpenTelemetry .NET 9 SDK实现分布式追踪精度达99.99%,支持K8s微服务场景,附Prometheus动态告警阈值公式。精准定位跨服务性能瓶颈,值得收藏。

IterStream的博客 201

Java与.NET技术栈深度对比:运行时、构建、框架与可观测性差异

Java和.NET作为企业级后端开发两大主流技术栈,其本质差异远超语法层面——核心在于JVM与CLR的运行时哲学、Maven与NuGet的依赖治理逻辑、Spring与ASP.NET Core的架构抽象范式,以及Micrometer与OpenTelemetry所代表的可观测性演进路径。JVM强调可调性与社区驱动的渐进优化,CLR则依托微软统一规划实现跨平台收敛;Maven以‘契约暴力’保障依赖确定性,NuGet借语义化版本与绑定重定向提升协作鲁棒性;Spring Boot封装复杂为约定,ASP.NET Cor

SailingAptech的专栏 493

10分钟搞懂 C# 中 AsyncLocal 的底层原理,你也能成为异步编程高手

AsyncLocal是 C# 中的一个类,它允许你在异步方法调用之间保持数据的一致性。简单来说,就是你可以在一个异步操作中设置某个值,然后在后续的异步操作中仍然能够访问到这个值,即使这些操作可能运行在不同的线程上。这听起来是不是很神奇?别急,我们马上来看看它是如何做到这一点的。通过本文的详细介绍,相信你已经对AsyncLocal的底层原理有了深入的理解。AsyncLocal是一个非常有用的工具,它可以帮助你在复杂的异步调用链中保持数据的一致性,而无需通过参数传递的方式。

java专栏 1904

.NET的AsyncLocal用法指南

通过 AsyncLocal 我们可以在一个逻辑上下文中维护一份私有数据,该上下文后续代码中都可以访问和修改这份数据,但另一个无关的上下文是无法访问的。无论是在新创建的 Task 中还是 await 关键词之后,我们都能够访问前面设置的 AsyncLocal 的数据。

xiaocai 1595

深入揭秘C# AsyncLocal:异步上下文中的秘密武器

AsyncLocal。

墨夶的博客 891

一文读懂 .NET 中的 ThreadLocal 和 AsyncLocal

欢迎来到 Dotnet 工具箱!在这里,你可以发现各种令人惊喜的开源项目!本篇文章,我们主要介绍一下 C# 中关于异步操作的一个重要知识点,ThreadLocal 和 AsyncLocal。基本概念ThreadLocal 用于在多线程环境中创建线程局部变量,可以让每个线程独立地访问自己的变量副本,互不影响。而 AsyncLocal 是 ThreadLocal 的异步版本,专门用于异步编程场景,在异...

dotNET跨平台 1182

微信H5网页授权通用方案:中转页破解域名限制,适配Hash/History路由

在Web开发中,OAuth 2.0授权码模式是实现第三方登录的通用标准,其核心原理是通过授权服务器颁发临时代码,再由客户端交换访问令牌。这一机制在微信公众号H5开发中尤为重要,用于安全获取用户身份。然而,微信平台对网页授权回调域名有严格的数量限制,这成为多子域名、多环境及微前端架构下业务扩展的主要瓶颈。其技术价值在于通过设计一个统一的中转授权页,将授权逻辑与业务逻辑解耦,让所有业务页面共享一个已配置的授权入口,从而突破域名配额限制。该方案的关键在于适配不同的前端路由模式,需精准处理Hash模式与Histor

weixin_42533120的博客 324

第二章 零差云控关节模组内部构造

对零差云控的eRob70I进行了拆解

SFY__优快云的博客 5994

一篇关于.NET AsyncLocal 的避坑指南

通过 AsyncLocal 我们可以在一个逻辑上下文中维护一份私有数据,该上下文后续代码中都可以访问和修改这份数据,但另一个无关的上下文是无法访问的。无论是在新创建的 Task 中还是 await 关键词之后,我们都能够访问前面设置的 AsyncLocal 的数据。

fefdfg的博客 494

基于灰度信息的图像配准方法

图像配准方法分为三种:基于灰度信息方法、基于变换域方法和基于特征方法。基于灰度信息方法是直接根据图像或图像块灰度信息进行像素上的对齐,该方法主要思想是直接最小化图像信息差异。然而,图像信息差异的判断准则衍生出不同的配准方法。常见的基于灰度信息的图像配准方法包括互相关法、互信息法和误差平方和法。本节将分别对互相关法、互信息法和误差平方和算法展开介绍,并通过MATLAB进行演示。基于灰度的图像方法容易实现,但同样存在不足。该方法对成像条件(光照,遮挡)、图像形变旋转以及噪声极其敏感,同时具有较高的计算复杂度。

weixin_42068054的博客 3778

多线程共享变量和 AsyncLocal

>>返回《C# 并发编程》 1. 简介 2. 异步下的共享变量 3. 解析 AsyncLocal 3.1. IAsyncLocalValueMap 的实现 3.2. 结论 1. 简介 普通共享变量: 在某个类上用静态属性的方式即可。 多线程共享变量 希望能将这个变量的共享范围缩小到单个线程内 无关系的B线程无法访问到A线程的值; [ThreadStatic]特性、Th...

Unknowncheats的博客 855

Unity最强动作战斗框架?Combat System Framework 深度解析

Combat System Framework 并不是一个简单的角色控制器,而是一套完整的动作游戏战斗开发框架。它借鉴了现代 AAA 游戏常见的架构思想,以 Gameplay Tag 为核心,结合 ScriptableObject 数据驱动设计,将技能、属性、Buff、伤害、武器、动画通知等系统有机整合在一起。同时,配套的可视化动画时间轴、Root Motion 工具和实时预览窗口,大幅优化了动作开发流程。当然,这套框架也有一定的学习门槛。

2403_88403568的博客 684

YOLOv11 TensorRT极致加速:C++部署性能优化完全指南

购买即可解锁300+YOLO优化文章,并且还有海量深度学习复现项目,价格仅需两杯奶茶的钱,别人有的本专栏也有!

LXY_LOVE36的博客 118

浅析 .NET 中 AsyncLocal 的实现原理

对于写过 ASP.NET Core 的童鞋来说,可以通过在 Controller 之外的地方获取到HttpContext,而它实现的关键其实是在于一个AsyncLocal 类型的静态字段。接下来就和大家来一起探讨下这个 AsyncLocal 的具体实现原理。如果有讲得不清晰或不准确的地方,还望指出。// 其他代码这里不展示本文源码参考为发文时间点为止最新的 github 开源代码,和之前实现有些许不同,但设计思想基本一致。

ts16dmy的博客 383

从 AccessControl.authorizationCheck 读懂 ABAP CDS 访问控制:默认策略、取舍边界与一线项目实战

摘要: 本文深入解析ABAP CDS视图中@AccessControl.authorizationCheck注解的核心功能与工程实践。该注解通过DCL角色实现行级数据过滤,提供三类能力:控制访问生效条件、约束语义一致性及管理特权访问。文章详述四种注解取值(#CHECK、#NOT_REQUIRED、#NOT_ALLOWED、#PRIVILEGED_ONLY)的适用场景与默认行为,并结合采购订单案例演示DCL与PFCG授权的落地步骤。同时指出访问控制的边界问题(如JOIN场景可能绕过过滤),并给出视图层级决策矩

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 265
上一篇: 代码管理工具-Git基础介绍及常用技巧
下一篇: Go Context 原理详解
pythonxxoo
博客等级 码龄5年 337粉丝 670原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值