Kestrel是否使用单个线程来处理Node.js等请求?

Nor*_*rny 59 kestrel-http-server asp.net-core

无论红隼Node.js的基于libuv.

虽然Node.js确切地说它使用了一个事件循环,但我似乎无法找到Kestrel的情况,或者它是否利用像IIS这样的线程池/请求队列?

红隼在Web服务器后面

红隼在Web服务器后面

Node.js事件循环

    ?????????????????????????
 ??>?        timers         ?
 ?  ?????????????????????????
 ?  ?????????????????????????
 ?  ?     I/O callbacks     ?
 ?  ?????????????????????????
 ?  ?????????????????????????
 ?  ?     idle, prepare     ?
 ?  ?????????????????????????      ?????????????????
 ?  ?????????????????????????      ?   incoming:   ?
 ?  ?         poll          ?<??????  connections, ?
 ?  ?????????????????????????      ?   data, etc.  ?
 ?  ?????????????????????????      ?????????????????
 ?  ?        check          ?
 ?  ?????????????????????????
 ?  ?????????????????????????
 ????    close callbacks    ?
    ?????????????????????????
Run Code Online (Sandbox Code Playgroud)

Dan*_*.G. 77

针对ASP.Net Core 2.0进行了更新.正如poke所指出的,服务器已经在托管和传输之间分开,其中libuv属于传输层.libuv ThreadCount已移至自己的位置,LibuvTransportOptions并使用UseLibuv()ext方法在Web主机构建器中单独设置:

  • 如果你LibuvTransportOptions在github中检查类,你会看到一个ThreadCount选项:

    /// <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;
    
    Run Code Online (Sandbox Code Playgroud)
  • 可以UseLibuv在Web主机构建器的调用中设置该选项.例如:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseLibuv(opts => opts.ThreadCount = 4)
            .UseStartup<Startup>()                
            .Build();
    
    Run Code Online (Sandbox Code Playgroud)

在ASP.NET Core 1.X中,Libuv配置是kestrel服务器的一部分:

  • 如果您KestrelServerOptions在其github仓库中检查该类,您将看到有一个ThreadCount选项:

    /// <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;
    
    Run Code Online (Sandbox Code Playgroud)
  • 该选项可以在调用中设置UseKestrel,例如在新的ASP.Net Core应用程序中:

    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel(opts => opts.ThreadCount = 4)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();
    
        host.Run();
    }
    
    Run Code Online (Sandbox Code Playgroud)

挖掘源代码:

  • 您可以看到KestrelThreads正在创建的libuv侦听器线程(或)KestrelEngine
  • 有些地方会调用这些ThreadPool方法,以便它们可以在CLR线程池而不是libuv线程中运行代码.(使用ThreadPool.QueueUserWorkItem).该池似乎默认最多32K线程,可以通过配置修改.
  • 所述Frame<TContext>用于处理的请求委托给实际应用(像一个ASP.Net核心应用程序).

所以我们可以说它为IO使用了多个libuv eventloops.实际工作是使用CLR线程池在具有标准工作线程的托管代码上完成的.

我很想找到更多关于此的权威文档(官方文档没有提供太多细节).我发现的最好的是Damian Edwards在第9频道谈论Kestrel .大约12分钟,他解释说:

  • libuv使用单线程事件循环模型
  • Kestrel支持多个事件循环
  • Kestrel只对IO工作在libuv事件循环上
  • 所有非IO工作(包括与HTTP相关的任何事情,如解析,框架等)都是在标准的.net工作线程上的托管代码中完成的.

此外,快速搜索已返回:

  • David Fowler在这里谈论Kestrel中的线程池.它还确认请求可能仍然在ASP.Net Core中的线程之间跳转.(就像以前的版本一样)
  • 这篇博文在它出来时看着红隼
  • 这个问题有关线程是如何在ASP.Net核心管理.

  • 在2.0中,服务器被拆分为服务器和传输层.作为其中一部分,线程计数配置移动到传输层.所以这个答案的等效解决方案是配置[`LibuvTransportOptions`](https://github.com/aspnet/KestrelHttpServer/blob/rel/2.0.0/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/ LibuvTransportOptions.cs)使用[`UseLibuv(...)`](https://github.com/aspnet/KestrelHttpServer/blob/rel/2.0.0/samples/SampleApp/Startup.cs#L83). (2认同)
  • 现在没有UseLibuv()了 (2认同)

dav*_*owl 41

线程是特定于传输的.使用Daniel JG的答案中所述的libuv传输(默认值为2.0),有许多基于机器上逻辑处理器数量的事件循环,并且可以通过设置选项上的值来覆盖它们.默认情况下,每个连接都绑定到特定线程,并且所有IO操作都在该线程上进行.用户代码在线程池线程上执行,因为我们不相信用户不会阻止IO线程.当您在这些线程池线程(即HttpResponse.WriteAsync)上进行IO调用时,kestrel会将该工作编组回到套接字绑定的相应IO线程.典型的请求流程如下所示:

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

当然,你总是可以告诉红隼你是专业人士,永远不会阻止IO线程并在其上运行你的代码.但我不会,除非我知道我在做什么(我不知道:D).