AsyncLocal<T>在链路追踪中的应用
前言
在项目生产中日志的记录是必不可少的,在.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的一种思路,如有不正确之处欢迎指正,如果该思路对您有帮助,请点赞分享。
AsyncLocal<T>在链路追踪中的应用的更多相关文章
- Jaeger Client Go 链路追踪|入门详解
目录 从何说起 Jaeger 部署 Jaeger 从示例了解 Jaeger Client Go 了解 trace.span tracer 配置 Sampler 配置 Reporter 配置 分布式系统 ...
- 基于Dapper的分布式链路追踪入门——Opencensus+Zipkin+Jaeger
微信搜索公众号 「程序员白泽」,进入白泽的编程知识分享星球 最近做了一些分布式链路追踪有关的东西,写篇文章来梳理一下思路,或许可以帮到想入门的同学.下面我将从原理到demo为大家一一进行讲解,欢迎评论 ...
- 原理分析dubbo分布式应用中使用zipkin做链路追踪
zipkin是什么 Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开 ...
- 在spring boot中三分钟上手apache顶级分布式链路追踪系统skywalking
原文:https://juejin.im/post/5cd10e81e51d453b560f2d53 skywalking在apache里全票通过成为了apache顶级链路追踪系统 项目地址:gith ...
- 原理分析dubbo分布式应用中使用zipkin做链路追踪(转)
作者:@nele本文为作者原创,转载请注明出处:https://www.cnblogs.com/nele/p/10171794.html 目录 zipkin是什么为什么使用Zipkinzipkin架构 ...
- .NET Core 中的日志与分布式链路追踪
目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...
- 基于zipkin分布式链路追踪系统预研第一篇
本文为博主原创文章,未经博主允许不得转载. 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Infras ...
- zipkin分布式链路追踪系统
基于zipkin分布式链路追踪系统预研第一篇 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Inf ...
- .NET Core----zipkin链路追踪使用
本文主要是说明core怎么使用链路追踪 一.添加nuget包 二.在Startup中添加配置 /// <summary> /// 注册zipkinTrace /// </summar ...
随机推荐
- OpenSSL实现了5种信息摘要算法有哪些?
OpenSSL实现了5种信息摘要算法,分别是MD2.MD5.MDC2.SHA(SHA1)和RIPEMD.SHA算法事实上包括了SHA和SHA1两种信息摘要算法.此外,OpenSSL还实现了DSS标准中 ...
- 构造器注入和 setter 依赖注入,那种方式更好?
每种方式都有它的缺点和优点.构造器注入保证所有的注入都被初始化,但是 setter 注入提供更好的灵活性来设置可选依赖.如果使用 XML 来描述依赖, Setter 注入的可读写会更强.经验法则是强制 ...
- Visual Studio App Center 中的 Bug 跟踪服务
我在之前的一篇文章 <使用 Visual Studio App Center 持续监视应用使用情况和问题> 中介绍了 App Center 的基本功能及使用入门,其中 诊断 可以自动手机用 ...
- 如何在Ubuntu 18.04 LTS上安装和配置MongoDB
MongoDB是一款非关系型数据库,提供高性能,高可用性和自动扩展企业数据库. MongoDB是一个非关系型数据库,因此您不能使用SQL(结构化查询语言)插入和检索数据,也不会将数据存储在MySQL或 ...
- PID参数整定
PID参数整定方法很多,常见的工程整定方法有临界比例度法.衰减曲线法和经验法.云南昌晖仪表制造有限公司以图文形式介绍以临界比例度法和衰减曲线法整定调节器PID参数方法.临界比例度法一个调节系统,在阶跃 ...
- HTML5打造原生应用——Ionic框架简介与Ionic Hello World
试了试用Ionic框架打造了两个应用,然后在Google Play上架了. 程序语言答人 教你设计物联网 更有意思的是这是在一周的业余时间内完成的三个应用中的两个,接着让我们看看这个框架如何实现高效地 ...
- JavaScript正则进阶之路——活学妙用奇淫正则表达式
原文收录在我的 GitHub博客 (https://github.com/jawil/blog) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步,以学习者的身份写博客,记录点滴. 有些童鞋肯定 ...
- SQL语句中关于日期的操作(非常的有用)
在SQL Server 里的日期数据,我们经常可以用 字段<='2008-5-20'这样的表达式,但在oracle却不可以,因为数据类型不一样 字段是date型,'2008-5-20'是字符型, ...
- vue和mint-ui loadMore 实现上拉加载和下拉刷新
首先安装mint-ui组件库 npm install mint-ui 在main.js中引入mint-ui和样式 import 'mint-ui/lib/style.css' import MintU ...
- 你可以说出export export default || model.exports exports 的区别吗(一)
一.前言: 用模块写代码,为什么要用模块来写代码:ES6之前,在js中定义的一切,都是共享一个全局作用域的,随着web应用变得复杂,这样做会引起如:命名冲突和安全问题.于是引入了模块. 二.清楚一个概 ...