Как настроить пустельгу asp.net для низкой задержки?

Я пытаюсь реализовать приложение asp.net 2.2 для обслуживания HTTP-запросов с минимально возможной задержкой (не пропускная способность, это не для производства, а своего рода соревнование). Приложение должно работать в среде контейнера докеров Linux с 4 ядрами, а мои обработчики привязаны к процессору со скоростью 0,2..3 мс каждый. Соединения предварительно создаются и поддерживаются, но в настоящее время я получаю время обработки около 0,6..0,8 мс для пустых обработчиков (отвечая 200 OK), с заметным дрожанием и случайными всплесками до 20-50 мс, которые я не могу объяснять.

Существуют ли какие-либо особые настройки Kestrel/Sockets/Threads/CLR, которые могут помочь минимизировать время ответа на каждый запрос? Или путь C/C++ с EPOLL — мой единственный вариант, если я хочу снизить его до 0,1..0,2 мс?


person Yuriy L    schedule 09.01.2019    source источник
comment
Вы находитесь на .net - CLR работает медленно. случайные всплески до 20–50 мс, возможно, являются циклами GC. Если вам нужен чрезвычайно быстрый код, используйте C++ и boost.   -  person vasily.sib    schedule 09.01.2019
comment
Я специально сделал так, чтобы GC здесь не играл роли: буферы предварительно выделяются и объединяются в пул, а при обработке запроса не создаются новые объекты. Любопытно, что подобные шипы я наблюдал и без Kestrel, на простых сокетах.   -  person Yuriy L    schedule 09.01.2019
comment
Вы не можете полностью контролировать сборщик мусора из своего приложения (поскольку сборщик мусора не является частью вашего приложения), поэтому его циклы по-прежнему влияют на ваше приложение.   -  person vasily.sib    schedule 09.01.2019
comment
Как это не вопрос программирования? Возможно, слова настроить сбивали с толку, изменили на реализацию.   -  person Yuriy L    schedule 09.01.2019
comment
Обязательно ли использовать Kestrel и Web.API? В конвейере обработки запросов есть много (полезного) промежуточного программного обеспечения. Если вы реализуете простой прослушиватель HTTP с помощью TcpListener, вы, вероятно, сможете добиться большего успеха (конечно, вы никогда не сделаете этого в рабочей среде).   -  person David Browne - Microsoft    schedule 09.01.2019
comment
Kestrel был моим первым вариантом (удаленный из всего ненужного промежуточного программного обеспечения), но я также экспериментировал с TcpListener с ожидаемыми расширениями, анализируя HTTP-заголовок вручную. Это было немного лучше, чем Kestrel, но ненамного (спасибо разработчикам Kestrel). Задержка была по-прежнему высокой и составляла [min=600 мкс, max=4000 мкс], измеренная в течение каждой секунды. Что меня озадачивает, так это то, что оба числа уменьшились до [min=400 мкс, max=2500 мкс] при увеличении нагрузки с 1 до 1000 запросов в секунду!   -  person Yuriy L    schedule 09.01.2019
comment
Как написано, вопрос не о программировании и разработке. Как написано, похоже, это еще один вопрос о том, как мне настроить свой сервер. Возможно, вам следует предоставить минимальный, полный и проверяемый пример и некоторые данные профилирования.   -  person jww    schedule 10.01.2019
comment
Я чувствую, что нужно больше информации, чтобы делать подобные заявления, например, какие настройки вы используете и что вы используете для проверки производительности. Не говорю, что это неправда, просто многое может повлиять на результаты.   -  person Filip Cordas    schedule 29.06.2019


Ответы (2)


Низкая задержка, безусловно, возможна с ASP.NET Core/Kestrel.

Вот крошечное веб-приложение, чтобы продемонстрировать это...

using System.Net;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

public static void Main(string[] args)
{
    IWebHost host = new WebHostBuilder()
        .UseKestrel()
        .Configure(app =>
        {
            // notice how we don't have app.UseMvc()?
            app.Map("/hello", SayHello);  // <-- ex: "http://localhost/hello"
        })
        .Build();

    host.Run();
}

private static void SayHello(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        // implement your own response
        await context.Response.WriteAsync("Hello World!");
    });
}

Я много раз отвечал на подобный вопрос раньше здесь и здесь.

Если вы хотите сравнить платформу ASP.NET Core с другими, это отличное наглядное пособие https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext. Как видите, ASP.NET Core дает исключительные результаты и является ведущей платформой для C#.

В моем блоке кода выше я отметил отсутствие app.UseMvc(). Если вам это нужно, я дал очень подробный ответ об уменьшении задержки в этом ответе: В чем разница между AddMvc() и ДобавитьMvcCore()?


Среда выполнения .NET Core (CoreRT)

Если вам по-прежнему требуется более высокая производительность, я рекомендую вам ознакомиться с .Net Core Runtime (CoreRT). .

Обратите внимание, что на момент написания этой статьи этот вариант, вероятно, необходимо рассмотреть более подробно, прежде чем использовать его для производственной системы.

CoreRT обеспечивает большую часть производительности и все преимущества развертывания собственной компиляции, сохраняя при этом возможность писать на вашем любимом языке программирования .NET.

CoreRT предлагает большие преимущества, которые имеют решающее значение для многих приложений.

  • Собственный компилятор создает ОДИН ФАЙЛ, включая приложение, управляемые зависимости и CoreRT.
  • Нативные скомпилированные приложения запускаются быстрее, поскольку они выполняют уже скомпилированный код. Им не нужно генерировать машинный код во время выполнения или загружать JIT-компилятор.
  • Скомпилированные в собственном коде приложения могут использовать оптимизирующий компилятор, что приводит к увеличению пропускной способности за счет более высокого качества кода (оптимизация компилятора C++). Компиляторы LLILLC и IL в CPP полагаются на оптимизирующие компиляторы.

Эти преимущества открывают новые возможности для разработчиков .NET.

  • Скопируйте один исполняемый файл с одного компьютера и запустите его на другом (того же типа) без установки среды выполнения .NET.
  • Создайте и запустите образ докера, содержащий один исполняемый файл (например, один файл в дополнение к Ubuntu 14.04).

Оптимизация для Linux

Существует хорошая библиотека, которая пытается справиться с очень специфическими случаями. В частности для Linux (но этот код безопасен для других операционных систем). Принцип этой оптимизации заключается в замене транспортной библиотеки libuv (которую использует ASP.NET Core) другой оптимизацией для Linux.

Он напрямую использует примитивы ядра для реализации Transport API. Это уменьшает количество объектов, выделенных в куче (например, uv_buf_t, SocketAsyncEventArgs), что означает меньше нагрузки на сборщик мусора. Реализации, построенные на основе xplat API, будут объединять объекты для достижения этой цели.

using RedHat.AspNetCore.Server.Kestrel.Transport.Linux; // <--- note this !

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseLinuxTransport()     // <--- and note this !!!
        .UseStartup()
        .Build();

// note: It's safe to call UseLinuxTransport on non-Linux platforms, it will no-op

Вы можете посмотреть репозиторий этого промежуточного программного обеспечения на GitHub здесь https://github.com/redhat-developer/kestrel-linux-transport

введите здесь описание изображения

введите здесь описание изображения

Источник: https://developers.redhat.com/blog/2018/07/24/improv-net-core-kestrel-performance-linux/

person Svek    schedule 09.01.2019
comment
Спасибо, с этого я и начал. Затем я даже удалил app.Map() и заменил его на app.Run(), чтобы избавиться от кода синтаксического анализа пути, но задержка все еще в несколько раз выше, чем при использовании C++/epoll(). Теперь теряю надежду - если нет какой-либо настройки уровня сокета, о которой я не знаю, мне придется прибегнуть к переключению языка для задачи. - person Yuriy L; 10.01.2019
comment
Вы пробовали CoreRT? Он скомпилирует все решение, и вы получите лучшую производительность. - person Svek; 10.01.2019
comment
Тоже пробовал: создал нативное приложение для Linux-x64, но это существенно не изменило результаты. Дело в том, что все участки горячего кода проходят JIT-оптимизацию после определенного количества итераций. На самом деле, в некоторых случаях JIT-компилятор времени выполнения может работать даже лучше, чем статический компилятор AOT, поскольку он может собирать реальную статистику выполнения перед оптимизацией вместо статического анализа и предположений, поэтому хороший прогревочный прогон после старта сработает. JIT-оптимизация. - person Yuriy L; 10.01.2019
comment
Для своего собственного аналога C/C++, который, как вы утверждаете, работает быстрее, вы используете libuv/kestrel? Проблемы с производительностью могут быть связаны не с языком, а с основной технологией обработки запросов. - person Svek; 10.01.2019
comment
Я добавил дополнительный контент для оптимизации Linux - person Svek; 10.01.2019
comment
Я пробовал и транспорт сокетов, и транспорт libuv, libuv продемонстрировал еще более высокую задержку. Re: linux transport: по словам разработчика, нет данных, свидетельствующих о том, что этот транспорт предлагает значительные преимущества по сравнению с транспортом Libuv/Sockets для реальных рабочих нагрузок. По этой причине мы не будем публиковать поддерживаемую версию на nuget.org. github.com/redhat-developer/kestrel-linux-transport/issues/ 61 - но я все равно попробую - person Yuriy L; 10.01.2019
comment
@YuriyL - Возможно, вы недостаточно моделируете нагрузку / стресс в своем сценарии. Я хочу избежать диалога на этом пути, поскольку он обязательно пометит ваш вопрос как неподходящий для StackOverflow. - person Svek; 10.01.2019
comment
Надеюсь, это ответит на ваш вопрос. - person Svek; 10.01.2019
comment
Всего одно примечание к примеру Run. Поскольку мы говорим о микрооптимизациях, основная команда .net на самом деле использует трюк, чтобы получить производительность для тестов techenpower. github.com/aspnet/Benchmarks/blob/ - person Filip Cordas; 29.06.2019

Спасибо всем, кто ответил. В итоге я реализовал свой собственный HTTP-сервер с системными вызовами epoll_wait(), это был единственный способ снизить задержку до нужного мне уровня. Kestrel предлагал примерно в 2-2,5 раза большую задержку.

Пожалуйста, имейте в виду, что Kestrel по-прежнему является отличным выбором для большинства производственных нужд, он оптимизирован для максимальной пропускной способности с разумной задержкой.

person Yuriy L    schedule 19.01.2019