繁体   English   中英

ASP.NET Core 中 AsyncLocal 的安全性

[英]Safety of AsyncLocal in ASP.NET Core

对于 .NET Core, AsyncLocalCallContext的替代品。 但是,尚不清楚在 ASP.NET Core 中使用它的“安全性”如何。

在 ASP.NET 4 (MVC 5) 及更早版本中, ASP.NET线程敏捷模型使CallContext不稳定 因此,在 ASP.NET 中,实现每个请求逻辑上下文行为的唯一安全方法是使用 HttpContext.Current.Items。 CallContext , HttpContext.Current.Items 是用CallContext实现的,但它是以一种对 ASP.NET 安全的方式实现的。

相比之下,在 OWIN/Katana Web API 的上下文中,线程敏捷模型不是问题。 仔细考虑如何正确处理CallContext 之后,我能够安全地使用 CallContext。

但现在我正在处理 ASP.NET Core。 我想使用以下中间件:

public class MultiTenancyMiddleware
{
    private readonly RequestDelegate next;
    static int random;

    private static AsyncLocal<string> tenant = new AsyncLocal<string>();
    //This is the new form of "CallContext".
    public static AsyncLocal<string> Tenant
    {
        get { return tenant; }
        private set { tenant = value; }
    }

    //This is the new verion of [ThreadStatic].
    public static ThreadLocal<string> LocalTenant;

    public MultiTenancyMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        //Just some garbage test value...
        Tenant.Value = context.Request.Path + random++;
        await next.Invoke(context);

        //using (LocalTenant = new AsyncLocal<string>()) { 
        //    Tenant.Value = context.Request.Path + random++;
        //    await next.Invoke(context);
        //}
    }
}

到目前为止,上面的代码似乎工作得很好。 但至少有一个危险信号。 过去,确保将CallContext视为必须在每次调用后释放的资源至关重要。

现在我看到没有不言自明的方法来“清理” AsyncLocal

我包含了代码,注释掉了,展示了ThreadLocal<T>是如何工作的。 它是IDisposable ,因此它具有明显的清理机制。 相比之下, AsyncLocal不是IDisposable 这令人不安。

这是因为AsyncLocal尚未处于发布候选状态吗? 还是因为真的不再需要执行清理?

即使在我上面的示例中正确使用了AsyncLocal ,ASP.NET Core 中是否存在任何类型的老式“线AsyncLocal性”问题会使这个中间件无法使用?

特别说明

对于那些不熟悉 ASP.NET 应用程序中CallContext问题的人,在这篇 SO 文章中,Jon Skeet引用了有关该问题的深入讨论(反过来引用了 Scott Hanselman 的评论)。 这个“问题”不是错误——它只是一个必须仔细考虑的情况。

此外,我可以亲自证明这种不幸的行为。 当我构建 ASP.NET 应用程序时,我通常将负载测试作为自动化测试基础结构的一部分。 在负载测试期间,我可以看到CallContext变得不稳定(其中可能有 2% 到 4% 的请求显示CallContext已损坏。我还看到 Web API GET具有稳定的CallContext行为,但POST操作都不稳定的情况。实现完全稳定性的唯一方法是依赖 HttpContext.Current.Items。

但是,在 ASP.NET Core 的情况下,我不能依赖 HttpContext.Items...没有这样的静态访问点。 我还不能为我正在修补的 .NET Core 应用程序创建负载测试,这也是我没有为自己回答这个问题的部分原因。 :)

再次:请理解我正在讨论的“不稳定”和“问题”根本不是错误。 CallContext 并没有什么缺陷。 该问题只是 ASP.NET 采用的线程调度模型的结果。 解决方案只是知道问题存在,并进行相应的编码(例如,在 ASP.NET 应用程序内部时,使用HttpContext.Current.Items而不是CallContext )。

我对这个问题的目标是了解这种动态如何在 ASP.NET Core 中应用(或不应用),以便在使用新的AsyncLocal构造时不会意外地构建不稳定的代码。

我只是在查看 CoreClr 的 ExecutionContext 类的源代码: https : //github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/src/mscorlib/src/System/Threading/ExecutionContext.cs

根据我对代码的理解,异步本地值是每个 ExecutionContext 实例的字段/变量。 它们不基于 ThreadLocal 或任何线程特定的持久化数据存储。

为了验证这一点,在我对线程池线程的测试中,当同一线程池线程被重用时,无法访问留在异步本地值中的实例,并且在下一个 GC 周期调用了用于清理自身的“左”实例的析构函数,这意味着该实例按预期进行了 GC。

如果AsyncLocalASP.NET 经典(非核心)应用程序中“安全”,如果有人在谷歌搜索后登陆此页面(就像我所做的那样),则添加我的两美分(一些评论者一直在问这个问题,而且我看到一个已删除的答案询问一样)。

我写了一个模拟asp.net的ThreadPool行为的小测试

  1. 即使线程池重新使用现有线程, AsyncLocal始终在请求之间被清除 所以在这方面它是“安全的”,没有数据会泄漏到另一个线程。

  2. 但是,即使在相同的上下文中(例如在 global.asax 中运行的代码和在控制器中运行的代码之间),也可以清除AsyncLocal 因为 MVC 方法有时会在与非 MVC 代码分离的线程上运行,请参阅此问题,例如: asp.net mvc 4, thread changed by model binding?

  3. 使用ThreadLocal是不安全的 b/c 它在线程池中的线程被重新使用后保留该值。 切勿在 Web 应用程序中使用 ThreadLocal。 我知道问题与 ThreadLocal 无关,我只是向考虑使用它的人添加此警告,抱歉。

在 ASP.NET MVC 5 .NET 4.7.2 下测试。

总体而言,在您无法直接访问HttpContext.Current的情况下, AsyncLocal似乎是替代短期缓存内容的完美选择。 不过,您最终可能会更频繁地重新计算缓存值,但这不是大问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM