ASP.NET Core 中 AsyncLocal 的安全性 [英] Safety of AsyncLocal in ASP.NET Core

查看:18
本文介绍了ASP.NET Core 中 AsyncLocal 的安全性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于 .NET Core,AsyncLocalCallContext.但是,尚不清楚在 ASP.NET Core 中使用它的安全性"如何.

For .NET Core, AsyncLocal is the replacement for CallContext. However, it is unclear how "safe" it is to use in ASP.NET Core.

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

In ASP.NET 4 (MVC 5) and earlier, the thread-agility model of ASP.NET made CallContext unstable. Thus in ASP.NET the only safe way to achieve the behavior of a per-request logical context, was to use HttpContext.Current.Items. Under the covers, HttpContext.Current.Items is implemented with CallContext, but it is done in a way that is safe for ASP.NET.

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

In contrast, in the context of OWIN/Katana Web API, the thread-agility model was not an issue. I was able to use CallContext safely, after careful considerations of how correctly to dispose it.

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

But now I'm dealing with ASP.NET Core. I would like to use the following middleware:

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 视为必须在每次调用后释放的资源是至关重要的.

So far, the above code seems to be working just fine. But there is at least one red flag. In the past, it was critical to ensure that CallContext was treated like a resource that must be freed after each invocation.

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

Now I see there is no self-evident way to "clean up" AsyncLocal.

我包含了代码,注释掉了,展示了 ThreadLocal 是如何工作的.它是IDisposable,所以它有一个明显的清理机制.相比之下,AsyncLocal 不是 IDisposable.这令人不安.

I included code, commented out, showing how ThreadLocal<T> works. It is IDisposable, and so it has an obvious clean-up mechanism. In contrast, the AsyncLocal is not IDisposable. This is unnerving.

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

Is this because AsyncLocal is not yet in release-candidate condition? Or is this because it is truly no longer necessary to perform cleanup?

即使 AsyncLocal 在我上面的例子中被正确使用,ASP.NET Core 中是否有任何类型的老式线程敏捷性"问题会使这个中间件无法工作?

And even if AsyncLocal is being used properly in my above example, are there any kinds of old-school "thread agility" issues in ASP.NET Core that are going to make this middleware unworkable?

对于那些不熟悉 CallContext 在 ASP.NET 应用程序中的问题的人,在这个所以帖子,Jon Skeet 引用了关于问题(反过来引用了来自 Scott Hanselman 的评论).这个问题"不是错误——它只是一个必须仔细考虑的情况.

For those unfamiliar with the issues CallContext has within ASP.NET apps, in this SO post, Jon Skeet references an in-depth discussion about the problem (which in turn references commentary from Scott Hanselman). This "problem" is not a bug - it is just a circumstance that must be carefully accounted for.

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

Furthermore, I can personally attest to this unfortunate behavior. When I build ASP.NET applications, I normally include load-tests as part of my automation test infrastructure. It is during load tests that I can witness CallContext become unstable (where perhaps 2% to 4% of requests show CallContext being corrupted. I have also seen cases where a Web API GET has stable CallContext behavior, but the POST operations are all unstable. The only way to achieve total stability is to rely on HttpContext.Current.Items.

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

However, in the case of ASP.NET Core, I cannot rely on HttpContext.Items...there is no such static access point. I'm also not yet able to create load tests for the .NET Core apps I'm tinkering with, which is partly why I've not answered this question for myself. :)

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

Again: Please understand that the "instability" and "problem" I'm discussing is not a bug at all. CallContext is not somehow flawed. The issue is simply a consequence of the thread dispatch model employed by ASP.NET. The solution is simply to know the issue exists, and to code accordingly (e.g. use HttpContext.Current.Items instead of CallContext, when inside an ASP.NET app).

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

My goal with this question is to understand how this dynamic applies (or does not) in ASP.NET Core, so that I don't accidentally build unstable code when using the new AsyncLocal construct.

推荐答案

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

I'm just looking into the source code of the ExecutionContext class for CoreClr: https://github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/src/mscorlib/src/System/Threading/ExecutionContext.cs

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

Base on my understanding of the code, the async local values are fields/variables of each ExecutionContext instance. They are not based on ThreadLocal or any thread specific persisted data store.

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

To verify this, in my testing with thread pool threads, an instance left in async local value is not accessible when the same thread pool thread is reused, and the "left" instance's destructor for cleaning up itself got called on next GC cycle, meaning the instance is GCed as expected.

这篇关于ASP.NET Core 中 AsyncLocal 的安全性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆