Nice programing

Kestrel은 Node.js와 같은 요청을 처리하기 위해 단일 스레드를 사용합니까?

nicepro 2020. 12. 11. 19:24
반응형

Kestrel은 Node.js와 같은 요청을 처리하기 위해 단일 스레드를 사용합니까?


KestrelNode.js모두 libuv를 기반으로 합니다 .

Node.js 는 이벤트 루프를 사용한다고 정확히 말하지만 Kestrel의 경우인지 아니면 IIS와 같은 스레드 풀링 / 요청 대기열을 사용하는지 찾을 수 없습니다.

웹 서버 뒤의 Kestrel

웹 서버 뒤의 Kestrel

Node.js 이벤트 루프

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

ASP.Net Core 2.0 용으로 업데이트되었습니다 . poke가 지적했듯이 서버는 호스팅과 전송으로 분할되었으며 libuv는 전송 계층에 속합니다. libuv ThreadCount는 자체로 이동되었으며 ext 메소드를 LibuvTransportOptions사용하여 웹 호스트 빌더에서 별도로 설정됩니다 UseLibuv().

  • LibuvTransportOptionsgithub 에서 클래스 를 확인하면 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;
    
  • 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 서버의 일부였습니다.

  • KestrelServerOptionsgithub 저장소에서 클래스 를 확인하면 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();
    }
    

소스 코드 살펴보기 :

  • libuv 리스너 스레드 (또는 KestrelThreads)가 생성되는 것을 볼 수 있습니다 .KestrelEngine
  • 일부 장소 ThreadPool에서는 libuv 스레드 대신 CLR 스레드 풀에서 코드를 실행할 수 있도록 메서드 를 호출합니다 . (사용 ThreadPool.QueueUserWorkItem). 풀은 config를 통해 수정할 수 있는 최대 32K 스레드 로 기본 설정되어있는 것 같습니다 .
  • The Frame<TContext> delegates to the actual application (like an ASP.Net Core application) for handling the request.

So we could say it uses multiple libuv eventloops for IO. The actual work is done on managed code with standard worker threads, using the CLR thread pool.

I would love to find more authoritative documentation about this (The official docs don't give much detail). The best one I have found is Damian Edwards talking about Kestrel on channel 9. Around minute 12 he explains:

  • libuv uses a single threaded event loop model
  • Kestrel supports multiple event loops
  • Kestrel does only IO work on the libuv event loops
  • All non IO work (including anything related with HTTP like parsing, framing, etc) is done in managed code on standard .net worker threads.

Additionally, a quick search has returned:

  • David Fowler talking about thread pooling in Kestrel here. It also confirms that a request might might still jump between threads in ASP.Net Core. (as it was in previous versions)
  • This blogpost looking at Kestrel when it came out
  • This question about how threads are managed in ASP.Net Core.

Threading is transport specific. With the libuv transport (the default in 2.0) as stated in Daniel J.G.'s answer there's a number of event loops based on the number of logical processors on the machine and that's overridable by setting the value on the options. By default each connection is bound to a particular thread and all IO operations take place on that thread. User code is executed on thread pool threads because we don't trust that users won't block IO threads. When you make IO calls on these thread pool threads (i.e. HttpResponse.WriteAsync), kestrel does the work to marshal that back to the appropriate IO thread the socket was bound to. A typical request flow looks like this:

[네트워킹에서 읽기] 스레드 풀로 디스패치-> [http 구문 분석], [미들웨어 파이프 라인 실행] 쓰기 호출-> 사용자 작업을 IO 스레드에 대기열에 추가 [네트워크에 쓰기]

물론 당신은 항상 kestrel에게 당신이 프로라고 말할 수 있으며 IO 스레드를 차단하고 코드를 실행하지 않을 것입니다. 그러나 나는 내가 무엇을하고 있는지 알지 못한다면하지 않을 것이다.

참고 URL : https://stackoverflow.com/questions/40948857/is-kestrel-using-a-single-thread-for-processing-requests-like-node-js

반응형