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

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

问题描述

对于.NET Core, AsyncLocal CallContext 。但是,尚不清楚在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< T> 的工作方式。它是 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?

对于ASP.NET应用程序中 CallContext 中不熟悉的问题,请使用此SO帖子(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 API GET 具有稳定的 CallContext 行为,但 POST 操作都是不稳定的,唯一实现完全稳定的方法就是依赖HttpContext.Current.Items。

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使用的线程分派模型的结果。解决方案是简单地知道问题存在并进行相应的编码(例如,使用 HttpContext.Current.Items 代替 CallContext ,当在ASP.NET应用程序内部时)。

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).

我的目标是理解这种动态的应用方式(或不应用) ),以便在使用新的 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/Exce/ .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天全站免登陆