欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET?core項(xiàng)目AsyncLocal在鏈路追蹤中的應(yīng)用

 更新時(shí)間:2022年05月14日 11:44:05   作者:gui.h  
這篇文章主要為大家介紹了.NET?core項(xiàng)目zhong?AsyncLocal在鏈路追蹤中的應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在項(xiàng)目生產(chǎn)中日志的記錄是必不可少的,在.net項(xiàng)目中,要說日志組件,log4net絕對(duì)可有一席之地,隨著公司業(yè)務(wù)的發(fā)展,微服務(wù)則必定無可避免。在跨服務(wù)中通過日志進(jìn)行分析性能或者排查故障點(diǎn),如何快速定位日志尤為關(guān)鍵。鏈路追蹤技術(shù)的出現(xiàn)正是解決這些痛點(diǎn)的。

分布式鏈路追蹤需要收集單次請(qǐng)求所經(jīng)過的所有服務(wù),而且為了知道請(qǐng)求細(xì)節(jié),還需要將具體的業(yè)務(wù)日志進(jìn)行串聯(lián),而這一切的基礎(chǔ)就是要通過一個(gè)traceid從頭傳到尾,相當(dāng)于將該次請(qǐng)求過程產(chǎn)生的所有日志都關(guān)聯(lián)其traceid,事后排查問題只需要知道traceid,就可以在日志中拉出與之關(guān)聯(lián)的所有日志。

當(dāng)然不是所有的公司都需要鏈路追蹤,對(duì)于一些小公司,就幾個(gè)單體系統(tǒng),壓根不需要這些。比如我們使用log4net時(shí),會(huì)在日志模板中加入ThreadId,例如這樣的模板

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

雖然并發(fā)高時(shí)我們多個(gè)用戶的請(qǐng)求日志都摻雜在一起,但是我們依然可以根據(jù)線程號(hào)將該次請(qǐng)求的日志進(jìn)行串聯(lián)。這在大多時(shí)候都很好的解決了我們的問題。

老傳統(tǒng)做法

即使在體量不大的系統(tǒng)中上面的線程號(hào)很好用了,但是哪有一點(diǎn)不用多線程的業(yè)務(wù)場景呢,當(dāng)一次請(qǐng)求進(jìn)來后可能會(huì)開多個(gè)異步線程去執(zhí)行,那上面的線程號(hào)就顯得力不從心了,就是說沒法一下將相干日志提取出來了。

但是這難不倒我們,我們可以在業(yè)務(wù)開始時(shí)自定義一個(gè)隨便字符串作為該次請(qǐng)求的唯一標(biāo)識(shí),然后將該變量通過參數(shù)傳給下游方法,下游方法也將其一層一層接力傳下去,在打印日志時(shí)都將該字段進(jìn)行輸出,這個(gè)辦法很多人都用過吧。

AspNetCore的TraceIdentifier

難道沒有一種優(yōu)雅的方式能將我們某次請(qǐng)求的過程(包括多線程)進(jìn)行串聯(lián)起來的唯一標(biāo)識(shí)嗎?

ASPNetCore中其實(shí)一直有個(gè)不起眼的屬性HttpContext.TraceIdentifier,可以說他就是框架給我們提供的traceid,我們可以在所需要的地方都注入HttpContext來獲取該參數(shù),當(dāng)然不許那么麻煩,只需要給日志組件獲取到該值,在任何leave的日志輸出時(shí)日志組件將其輸出即可,這個(gè)完全沒問題,大家可以去深入研究,有些日志組件可以直接配置就可以輸出該TraceIdentifier值到每一條日志中,也可以將其使用到跨應(yīng)用調(diào)用時(shí)傳遞到下游服務(wù),如http請(qǐng)求可以通過header攜帶該值,下游從header中獲取并作為它自己的TraceIdentifier繼續(xù)傳遞。

AsyncLocal在鏈路追蹤的應(yīng)用

ThreadLoacl倒是熟悉,是每個(gè)線程之間隔離的,每個(gè)線程操作的都是自己線程的對(duì)象,能做到各個(gè)線程或不影響。AsyncLocal并不是一個(gè)新特性,只是用的場景不多,很少被使用

定義

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

表示對(duì)于給定異步控制流(如異步方法)是本地?cái)?shù)據(jù)的環(huán)境數(shù)據(jù)。

示例

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 ''

簡單理解,就是對(duì)該變量賦值后,之影響自己個(gè)自己的子線程,即當(dāng)前線程發(fā)起的其他線程,包括線程池中的線程,都能獲取到該值,而子線程修改該值,對(duì)父線程來說是無影響的。

而這種特性貌似就是我們尋找那種能夠優(yōu)雅標(biāo)記出同一次請(qǐng)求的特性。定義一個(gè)全局變量,在每次請(qǐng)求的起點(diǎn)對(duì)該變量賦值一個(gè)隨機(jī)字符串,然后本次請(qǐng)求涉及到的所有線程訪問該值,都是我們?cè)谌肟谫x的值。

項(xiàng)目應(yīng)用

我們可以在任意地方定義一個(gè)全局變量,最好是放到LogHelper之中

AspNet4

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

在授權(quán)過濾器中對(duì)該值進(jìn)行賦值,一般授權(quán)過濾最先執(zhí)行,可作為請(qǐng)求的入口點(diǎn)

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

注冊(cè)中間件進(jìn)行設(shè)置值,將自己的中間件注冊(cè)靠前點(diǎn)

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

經(jīng)驗(yàn)證與預(yù)期符合,該實(shí)現(xiàn)方式不依賴AspnetCore框架HttpContext.TraceIdentifier,提供一種實(shí)現(xiàn)鏈路追蹤中傳遞TraceId的一種思路,如有不正確之處歡迎指正,如果該思路對(duì)您有幫助,請(qǐng)點(diǎn)贊分享。

以上就是.NET core項(xiàng)目AsyncLocal在鏈路追蹤中的應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于AsyncLocal鏈路追蹤的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論