Kestrel是否像Node.js一样使用单线程处理请求?

78

KestrelNode.js 都基于 libuv

虽然Node.js明确表示其使用 事件循环,但我似乎找不到Kestrel是否也是这种情况,或者它是否像IIS一样利用线程池/请求队列?

Kestrel在Web服务器后面

Kestrel behind a web server

Node.js事件循环

    ┌───────────────────────┐
 ┌─>│        timers         │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 │  │     I/O callbacks     │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 │  │     idle, prepare     │
 │  └──────────┬────────────┘      ┌───────────────┐
 │  ┌──────────┴────────────┐      │   incoming:   │
 │  │         poll          │<─────┤  connections, │
 │  └──────────┬────────────┘      │   data, etc.  │
 │  ┌──────────┴────────────┐      └───────────────┘
 │  │        check          │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 └──┤    close callbacks    │
    └───────────────────────┘
2个回答

93
已更新至ASP.Net Core 2.0。正如poke所指出的,服务器已经分为托管和传输两个部分,其中libuv属于传输层。libuv的ThreadCount已经移动到自己的LibuvTransportOptions中,并且可以使用UseLibuv()扩展方法在web主机生成器中单独设置。
  • If you check the LibuvTransportOptions class in github, you will see a ThreadCount option:

    /// <summary>
    /// The number of libuv I/O threads used to process requests.
    /// </summary>
    /// <remarks>
    /// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16.
    /// </remarks>
    public int ThreadCount { get; set; } = ProcessorThreadCount;
    
  • The option can be set in the call to UseLibuv, in your web host builder. For example:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseLibuv(opts => opts.ThreadCount = 4)
            .UseStartup<Startup>()                
            .Build();
    
在ASP.NET Core 1.X中,Libuv配置是Kestrel服务器的一部分:
  • If you check the KestrelServerOptions class in its github repo, you will see there is a ThreadCount option:

    /// <summary>
    /// The number of libuv I/O threads used to process requests.
    /// </summary>
    /// <remarks>
    /// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16.
    /// </remarks>
    public int ThreadCount { get; set; } = ProcessorThreadCount;
    
  • The option can be set in the call to UseKestrel, for example in a new ASP.Net Core app:

    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel(opts => opts.ThreadCount = 4)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();
    
        host.Run();
    }
    
挖掘源代码:
  • 您可以在KestrelEngine中看到libuv监听线程(或KestrelThreads)的创建。
  • 有些地方会调用ThreadPool方法,以便在CLR线程池中运行代码,而不是在libuv线程中运行。(使用ThreadPool.QueueUserWorkItem)。该池似乎默认为32K个线程的最大值,可以通过配置进行修改。
  • Frame<TContext>委托实际应用程序(如ASP.Net Core应用程序)来处理请求。

因此,我们可以说它使用多个libuv事件循环来进行IO操作。实际工作是使用CLR线程池中的标准工作线程在托管代码上完成的。

我希望能找到更权威的文档(官方文档 没有提供太多细节)。我找到的最好的一个是Damian Edwards在channel 9上谈论Kestrel。大约在第12分钟,他解释了以下内容:

  • libuv使用单线程事件循环模型
  • Kestrel支持多个事件循环
  • Kestrel仅在libuv事件循环上执行IO工作
  • 所有非IO工作(包括与HTTP相关的任何内容,如解析、分帧等)都在托管代码上的标准.net工作线程上完成。

此外,快速搜索返回:

  • David Fowler在这里谈论了Kestrel中的线程池here。它还确认了请求可能仍然会在ASP.Net Core中在不同的线程之间跳转(就像在以前的版本中一样)。
  • 这篇博客文章介绍了Kestrel发布时的情况。
  • 这个问题涉及ASP.Net Core中如何管理线程。

1
在asp.net core 2.0中,KestrelServerOptions类没有ThreadCount属性,如何设置? - Mohammad Akbari
2
在2.0版本中,服务器被分成了服务器层和传输层。作为其中的一部分,线程计数配置移动到了传输层。因此,这个答案的等效解决方案是使用UseLibuv(...)配置LibuvTransportOptions - poke
谢谢@poke,我一有空就会尝试更新1.X与2.0差异的答案。 - Daniel J.G.
@DanielJ.G. 那真的是我能想到的唯一区别 :) - poke
4
现在没有使用UseLibuv()的必要。 - mugi
@mugi请检查 https://stackoverflow.com/questions/57708071/uselibuv-option-gone-after-changing-microsoft-aspnetcore-all-to-microsoft-netcor 的解决方案。 - Kamran Shahid

54

线程是与传输方式相关的。使用libuv传输(2.0中的默认选项),如Daniel J.G.所述,有多个基于机器逻辑处理器数量的事件循环,并且可以通过设置选项上的值进行覆盖。默认情况下,每个连接都绑定到特定的线程,并且所有IO操作都在该线程上执行。用户代码在线程池线程上执行,因为我们不信任用户不会阻塞IO线程。当您在这些线程池线程上进行IO调用时(例如HttpResponse.WriteAsync),Kestrel会将其工作转发回绑定到套接字的适当IO线程。典型的请求流程如下:

[从网络读取]分派到线程池 -> [http解析],[执行中间件管道]调用写入 -> 将用户工作排队到IO线程[写入到网络]

当然,您始终可以告诉kestrel您是专业人士,永远不会阻塞IO线程并在其上运行代码。但除非我知道自己在做什么(我不知道:D),否则我不会这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接