You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kestrel中JSON序列化/反序列化时AsyncLocal<>上下文丢失问题

问题背景与现象

我使用NuGet包Microsoft.ServiceFabric.AspNetCore.Kestrel 6.1.1799和System.Text.Json进行序列化,目标框架为.NET 6.0。
我实现了一个中间件,通过AsyncLocal<>状态对请求进行计时监控以排查性能瓶颈。该机制在控制器、ActionFilter特性、JWT授权策略事件等模块中均正常工作,但在序列化/反序列化阶段出现异常:

  • 当业务对象实现IJsonOnDeserialized、IJsonOnDeserializing、IJsonOnSerialized、IJsonOnSerializing接口时,中间件初始化的AsyncLocal<>状态经常无法被获取到。
  • 单请求测试(如Postman)时无问题,但高并发负载测试时必现问题。

基础业务对象代码

public abstract class CMonitor : IJsonOnDeserialized, IJsonOnDeserializing, IJsonOnSerialized, IJsonOnSerializing
{
    #region Properties

    [JsonIgnore]
    private Watch Serialization { get; set; }

    [JsonIgnore]
    private Watch Deserialization { get; set; }

    #endregion

    #region Serialization

    public virtual void OnDeserialized()
    {
        this.Deserialization?.Dispose();
    }

    public void OnDeserializing()
    {
        this.Deserialization = new Watch("{0} >>", this.GetType().Name);
    }

    public void OnSerialized()
    {
        this.Serialization?.Dispose();
    }

    public void OnSerializing()
    {
        this.Serialization = new Watch("<< {0}", this.GetType().Name);
    }

    #endregion
}

Watch类代码(序列化/反序列化时会触发构造函数中的Debug.WriteLine错误)

public class Watch : Elapsed, IDisposable
{
    #region Properties

    private static AsyncLocal<WatchContainer?> _CurrentWatcher;
    public static WatchContainer? CurrentWatcher
    {
        get
        {
            return _CurrentWatcher.Value;
        }
        set
        {
            _CurrentWatcher.Value = value;
        }
    }

    public string Name { get; private set; }

    #endregion

    #region Constructor

    public Watch(string pName, params object[] pArgs)
    {
        if (pArgs != null && pArgs.Length > 0)
        {
            pName = string.Format(pName, pArgs);
        }

        this.Name = pName;
        this.StartTimestamp = Stopwatch.GetTimestamp();

        if (Watch.CurrentWatcher == null)
        {
            Debug.WriteLine("WATCH CONTAINER NULL for WATCH={0}", this.Name);
        }
        else
        {
            Watch.CurrentWatcher?.Add(this);
        }
    }

    static Watch()
    {
        _CurrentWatcher = new AsyncLocal<WatchContainer?>();
    }

    #endregion

    #region Methods

    public void Dispose()
    {
        if (this.EndTimestamp == 0)
        {
            this.EndTimestamp = Stopwatch.GetTimestamp();
        }
    }

    #endregion
}

中间件代码

public class LogMiddleware
{
    #region Properties

    private RequestDelegate Next { get; set; }

    #endregion

    public LogMiddleware(RequestDelegate pRequestDelegate)
    {
        this.Next = pRequestDelegate;
    }

    public async Task Invoke(HttpContext context)
    {
        Watch.CurrentWatcher = new WatchContainer();

        using (new Watch("OuterTreatment"))
        {
            await this.Next(context);
        }
    }
}

补充:WatchContainer类代码

public class WatchContainer : Elapsed
{
    #region Properties

    public List<Watch> Watches { get; private set; }

    #endregion

    #region Constructor

    public WatchContainer()
    {
        this.StartTimestamp = Stopwatch.GetTimestamp();
        this.Watches = new List<Watch>();
    }

    #endregion

    #region Methods

    public void Add(Watch pWatch)
    {
        lock (this.Watches)
        {
            this.Watches.Add(pWatch);
        }
    }

    #endregion
}

补充:Elapsed基类代码

public abstract class Elapsed
{
    #region Properties

    public long StartTimestamp { get; protected set; }

    public long EndTimestamp { get; protected set; }

    public double Delay
    {
        get
        {
            return Math.Round(1000.0 * (this.EndTimestamp - this.StartTimestamp) / Stopwatch.Frequency, 4);
        }
    }

    #endregion
}

Kestrel中间件管道配置

builder
    .UseMiddleware<LogMiddleware>()
    .UseRouting()
    .UseAuthentication()
    .UseAuthorization()
    .Use(async (context, next) =>
    {
        using (new Watch("Treatment"))
        {
            await next();
        }
    })
    .UseEndpoints(endpoints =>
    {
       endpoints
            .MapControllers()
            .RequireAuthorization();
    });

补充:WatchContainer的Trace方法(在LogMiddleware.Invoke()末尾调用)

public void Trace(string pIdentifier, bool pStats)
{
    Watch.CurrentWatcher = null;
    this.EndTimestamp = Stopwatch.GetTimestamp();

    // 日志跟踪算法
    // 将容器发送到队列写入CSV文件
}

疑问

  1. 这是System.Text.Json或AsyncLocal<>的已知问题吗?
  2. 我的代码用法是否存在错误?
  3. 有没有其他不破坏AsyncLocal<>上下文的序列化/反序列化监控方式?

内容的提问来源于stack exchange,提问作者Mad hatter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:34:55