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

问题描述 投票:56回答:2

KestrelNode.js都基于libuv

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

红隼在Web服务器后面

Kestrel behind a web server

Node.js事件循环

    ┌───────────────────────┐
 ┌─>│        timers         │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 │  │     I/O callbacks     │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 │  │     idle, prepare     │
 │  └──────────┬────────────┘      ┌───────────────┐
 │  ┌──────────┴────────────┐      │   incoming:   │
 │  │         poll          │<─────┤  connections, │
 │  └──────────┬────────────┘      │   data, etc.  │
 │  ┌──────────┴────────────┐      └───────────────┘
 │  │        check          │
 │  └──────────┬────────────┘
 │  ┌──────────┴────────────┐
 └──┤    close callbacks    │
    └───────────────────────┘
asp.net-core kestrel-http-server
2个回答
74
投票

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

  • 如果你检查github中的LibuvTransportOptions类,你会看到一个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;
  • 可以在Web主机构建器中调用UseLibuv中设置该选项。例如: public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseLibuv(opts => opts.ThreadCount = 4) .UseStartup<Startup>() .Build();

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

  • 如果您在其github仓库中检查KestrelServerOptions类,您将看到有一个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;
  • 可以在调用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(); }

挖掘源代码:

  • 你可以看到在KestrelThreads中创建的libuv监听器线程(或KestrelEngine
  • 有些地方会调用ThreadPool方法,这样他们就可以在CLR线程池而不是libuv线程中运行代码。 (使用ThreadPool.QueueUserWorkItem)。游泳池似乎是默认的max of 32K threads,可以修改via config
  • Frame<TContext>委托实际应用程序(如ASP.Net Core应用程序)处理请求。

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

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

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

此外,快速搜索已返回:

  • David Fowler在Kestrel here谈论线程池。它还确认请求可能仍然在ASP.Net Core中的线程之间跳转。 (就像以前的版本一样)
  • 这个blogpost看着红隼出来的时候
  • 这篇关于如何在ASP.Net Core中管理线程的question

39
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.