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

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

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

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

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

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

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

Я уже знаю про твики клиента HTTP вроде ServicePointManager.MaxServicePointIdleTime, ServicePointManager.DefaultConnectionLimit, ServicePointManager.Expect100Continue, а также HttpWebRequest.AllowWriteStreamBuffering,

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

ОБНОВЛЕНИЕ:

Вот немного подробнее о производительности, которую я вижу с TcpListener против 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 не является подходящим инструментом для работы, которую мне нужно сделать, и должен будет продолжать использовать протокол сокетов, свернутый вручную, который мы использовали все это время.

2 ответа

Я не вижу слишком много интереса, за исключением этой технической заметки. Возможно, стоит поиграть MaxBytesPerSend

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

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