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文件 }
疑问
- 这是
System.Text.Json或AsyncLocal<>的已知问题吗? - 我的代码用法是否存在错误?
- 有没有其他不破坏
AsyncLocal<>上下文的序列化/反序列化监控方式?
内容的提问来源于stack exchange,提问作者Mad hatter
相关产品推荐
相关产品推荐

