Как максимизировать производительность загрузки файлов http.sys

Я создаю инструмент, который передает очень большие потоковые массивы данных (возможно, порядка терабайт в одном потоке, обычно в десятках гигабайт) с одного сервера на другой. Клиентская часть инструмента будет считывать блоки с исходного диска и отправлять их по сети. Серверная сторона будет считывать эти блоки из сети и записывать их в файл на диске сервера.

Сейчас я пытаюсь решить, какой транспорт использовать. Параметры - это raw TCP и HTTP.

Я действительно, ДЕЙСТВИТЕЛЬНО хочу иметь возможность использовать HTTP. HttpListener (или WCF, если я хочу идти по этому маршруту) упрощает подключение к API-интерфейсу HTTP (http.sys), и я могу получить такие вещи, как аутентификация и SSL, бесплатно. Проблема сейчас в производительности.

Я написал простую тестовую жгуту, которая отправляет 128 тыс. блоков из NULL-байтов, используя иксюкс ввода-вывода BeginWrite/EndWrite async, с асинхронным BeginRead/EndRead на стороне сервера. Я изменил этот тестовый жгут, поэтому я могу сделать это с помощью операций HTTP PUT с помощью HttpWebRequest/HttpListener или обычной старой записи сокета с помощью TcpClient/TcpListener. Чтобы исключить проблемы с сетевыми картами или сетевыми путями, клиент и сервер находятся на одной машине и обмениваются данными по локальному хосту.

На моем 12-ядерном тестовом сервере Windows 2008 R2 версия TCP этого тестового жгута может вызывать байты со скоростью 450 Мбайт/с при минимальном использовании ЦП. В том же окне версия HTTP тестовой жгуты работает от 130 МБ/с до 200 МБ/с в зависимости от того, как я ее настраиваю.

В обоих случаях использование ЦП низкое, и подавляющее большинство использования ЦП там - время ядра, поэтому я уверен, что мое использование С# и среды выполнения .NET не является узким местом. Коробка оснащена двумя 6-ядерными процессорами Xeon X5650, 24 ГБ одноразовой ОЗУ DDR3 и используется исключительно мной для моего собственного тестирования производительности.

Я уже знаю о настройках HTTP-клиента, таких как ServicePointManager.MaxServicePointIdleTime, ServicePointManager.DefaultConnectionLimit, ServicePointManager.Expect100Continue и HttpWebRequest.AllowWriteStreamBuffering.

Есть ли у кого-нибудь идеи, как я могу получить производительность HTTP.sys за пределами 200 МБ/с? Кто-нибудь видел, как он хорошо работает в любой среде?

ОБНОВЛЕНИЕ:

Здесь немного более подробно о производительности, которую я вижу с помощью TcpListener vs HttpListener:

Сначала я написал тест TcpClient/TcpListener. На моем тестовом поле, которое было способно вытолкнуть 450 МБ/с.

Затем, используя рефлектор, я понял, как получить исходный объект Socket, лежащий в основе HttpWebRequest, и изменил мой HTTP-клиентский тест, чтобы использовать его. Еще нет радости; всего 200 МБ/с.

Моя нынешняя теория заключается в том, что http.sys оптимизирован для типичного варианта использования IIS, который представляет собой множество параллельных небольших запросов и множество параллельных и, возможно, больших ответов. Я выдвигаю гипотезу, что для достижения этой оптимизации MSFT пришлось делать это за счет того, что я пытаюсь выполнить, что очень высокая пропускная способность по одному очень большому запросу с очень небольшим откликом.

Для чего это стоит, я также пробовал до 32 одновременных операций HTTP PUT, чтобы увидеть, может ли он масштабироваться, но радости все равно не было; около 200 МБ/с.

Интересно, что на моей рабочей станции разработки, которая представляет собой четырехъядерный Xeon Precision T7400 с 64-разрядной Windows 7, моя реализация TcpClient составляет около 200 МБ/с, а версия HTTP также составляет около 200 МБ/с. Как только я перейду на более высокоуровневую машину серверного класса с сервером Server 2008 R2, код TcpClient получит до 450 Мбайт/с, а код HTTP.sys останется около 200.

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

Ответы

Ответ 1

Я не вижу слишком большого интереса, кроме этого Tech Note. Возможно, стоит иметь скрипку с MaxBytesPerSend

Ответ 2

Если вы собираетесь отправлять файлы по локальной сети, тогда UDP - это путь, потому что TCP-ресурсы в этом случае являются отходами. TCP обеспечивает ограничение скорости, чтобы избежать слишком большого количества потерянных пакетов, тогда как с UDP приложение должно сортировать это самостоятельно. NFS выполнит эту работу, не правда ли, что вы застряли в окнах; но я уверен, что должны быть готовые вещи UDP. Также используйте инструмент "iperf" (доступный на linux, возможно, также окна) для сравнения сетевой ссылки независимо от протокола. Некоторые сетевые карты - это просто дерьмо и слишком много полагаются на процессор, что ограничит вашу скорость до 200 мбит. Вам нужна правильная сетевая карта с собственными процессорами (не знаю, какие именно термины ставить).