Использует ли Kestrel один поток для обработки запросов, например Node.js?

И Kestrel, и Node.js основаны на libuv.

Хотя Node.js точно заявляет, что он использует цикл обработки событий, я не могу найти, так ли это в случае с Kestrel, или он использует пул потоков / очередь запросов, как IIS?

Пустельга за веб-сервером

Пустельга за веб-сервером

Цикл событий Node.js

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

2 ответа

Решение

Обновлено для ASP.Net Core 2.0. Как указал poke, сервер был разделен между хостингом и транспортом, где libuv принадлежит транспортному уровню. Либув ThreadCount был перемещен в свой LibubTransportOptions и они устанавливаются отдельно в вашем веб-хосте с UseLibuv() метод ext:

  • Если вы проверите 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;
    
  • Опция может быть установлена ​​в вызове 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:

  • Если вы проверите 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;
    
  • Опция может быть установлена ​​в вызове 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 методы, чтобы они могли запускать код в пуле потоков CLR вместо потоков libuv. (С помощью ThreadPool.QueueUserWorkItem). Кажется, что пул по умолчанию имеет максимум 32K потоков, которые можно изменить через config.
  • Frame<TContext> делегирует фактическое приложение (например, приложение ASP.Net Core) для обработки запроса.

Таким образом, мы могли бы сказать, что он использует несколько циклов libuv для IO. Фактическая работа выполняется над управляемым кодом со стандартными рабочими потоками с использованием пула потоков CLR.

Я хотел бы найти более авторитетную документацию об этом ( официальные документы не дают подробностей). Лучшее, что я нашел, - это то, что Дамиан Эдвардс говорит о Кестрел на 9 канале Около 12 минут он объясняет:

  • libuv использует модель однопоточного цикла обработки событий
  • Kestrel поддерживает несколько циклов событий
  • Kestrel выполняет только операции ввода-вывода в циклах libuv
  • Вся работа, не связанная с вводом-выводом (включая все, что связано с HTTP, например анализ, кадрирование и т. Д.) Выполняется в управляемом коде в стандартных рабочих потоках.net.

Кроме того, быстрый поиск вернулся:

  • Дэвид Фаулер говорит о пуле потоков в Kestrel здесь. Это также подтверждает, что запрос может все еще перемещаться между потоками в ASP.Net Core. (как это было в предыдущих версиях)
  • Этот блог пост, глядя на Kestrel, когда он вышел
  • Этот вопрос о том, как потоки управляются в ASP.Net Core.

Threading зависит от транспорта. С транспортом libuv (по умолчанию в 2.0), как указано в ответе Даниэля Дж.Г., есть ряд циклов событий, основанных на количестве логических процессоров на машине, и это можно переопределить, установив значение в опциях. По умолчанию каждое соединение связано с конкретным потоком, и все операции ввода-вывода выполняются в этом потоке. Пользовательский код выполняется в потоках пула потоков, потому что мы не верим, что пользователи не будут блокировать потоки ввода-вывода. Когда вы делаете IO-вызовы в этих потоках пула потоков (т.е. HttpResponse.WriteAsync), kestrel выполняет работу, направленную на то, чтобы вернуться к соответствующему потоку ввода-вывода, к которому был привязан сокет. Типичный поток запросов выглядит следующим образом:

[чтение из сети] отправка в пул потоков -> [ http parsing ], [выполнить промежуточный программный конвейер] вызов для записи -> поставить пользователя в очередь на поток ввода-вывода [запись в сеть]

Конечно, вы всегда можете сказать kestrel, что вы профессионал и никогда не заблокируете поток ввода-вывода и не запустите на нем свой код. Но я бы не стал, если бы не знал, что я делаю (а я не знаю:D).

Другие вопросы по тегам