Какие есть советы по использованию буфера и настройке пользовательских служб TCP?

В последнее время я исследовал ряд сетевых библиотек и фреймворков, таких как libevent, libev, Facebook Tornado и Concurrence (Python).

Одна вещь, которую я заметил в их реализациях, - это использование буферов чтения / записи на уровне приложения для каждого клиента (например, IOStream в Tornado) - даже HAProxy имеет такие буферы.

В дополнение к этим буферам уровня приложения существуют буферы реализации TCP ядра ОС для каждого сокета.

Я могу понять, как app / lib использует буфер чтения, я думаю: app / lib читает из буфера ядра в буфер приложения, и приложение что-то делает с данными (например, десериализует сообщение в нем).

Однако я запутался в необходимости / использовании буфера записи. Почему бы просто не записать в буфер отправки / записи ядра? Чтобы избежать накладных расходов на системные вызовы (запись)? Я полагаю, дело в том, чтобы быть готовым с большим количеством данных, которые нужно отправить в буфер записи ядра, когда ядро ​​уведомляет приложение / библиотеку о том, что сокет "доступен для записи" (например, EPOLLOUT). Но почему бы просто не избавиться от буфера записи приложения и не настроить буфер записи TCP ядра таким же большим?

Также рассмотрите сервис, для которого имеет смысл отключение алгоритма Нэгла (например, игровой сервер). В такой конфигурации, я полагаю, мне хотелось бы обратного: не было буфера записи ядра, но был буфер записи приложения, да? Когда приложение готово к отправке полного сообщения, оно записывает буфер приложения через send () и т. Д., А ядро ​​передает его.

Помогите мне прояснить мне это понимание, если хотите. Спасибо!


person z8000    schedule 22.01.2010    source источник


Ответы (2)


Что ж, говоря о haproxy, он не делает различий между буферами чтения и записи, для обеих целей используется буфер, который сохраняет копию. Однако вносить какие-то изменения действительно болезненно. Например, иногда вам нужно переписать заголовок HTTP, и вам нужно правильно переместить данные для вашей перезаписи и сохранить некоторое состояние о значении предыдущего заголовка. В haproxy заголовок соединения можно переписать, а его предыдущее и новое состояния сохраняются, потому что они понадобятся позже, после перезаписи. Используя буфер чтения и записи, у вас нет этой сложности, так как вы всегда можете вернуться в свой буфер чтения, если вам нужны какие-либо исходные данные.

Haproxy также может использовать соединение сокетов в Linux. Это означает, что он не читает и не записывает данные, он просто сообщает ядру, что куда брать и куда переместить. Затем ядро ​​автоматически перемещает указатели без копирования данных для передачи сегментов TCP с сетевой карты на другую (когда это возможно), но при этом данные никогда не передаются в пространство пользователя, что позволяет избежать двойной копии.

Вы совершенно правы в том, что в общем случае вам не нужно копировать данные между буферами. Это пустая трата пропускной способности памяти. Haproxy работает со скоростью 10 Гбит / с с 20% ЦП со сращиванием, но без сращивания (еще 2 копии), это близко к 100%. Но затем рассмотрите сложность альтернатив и сделайте свой выбор.

Надеюсь, это поможет.

person Willy Tarreau    schedule 22.01.2010
comment
Спасибо за информацию о HAProxy. Вызов splice () интересен: kerneltrap.org/node/6505 - person z8000; 23.01.2010

Когда вы используете асинхронную операцию ввода-вывода сокета, асинхронная операция чтения / записи возвращается немедленно, поскольку асинхронная операция не гарантирует обработки всех данных (т.е. помещает все необходимые данные в буфер сокета TCP или получает все необходимые данные из него) успешно с один вызов, частичные данные должны пережить несколько операций. Затем вам потребуется буферное пространство приложения для хранения данных до тех пор, пока длятся операции ввода-вывода.

person syffinx    schedule 02.12.2013