О HTTP-proxy, C10K и софте

pilot

Сейчас в моде асинхронная обработка входящих соединений и высоконагруженные приложения.
То бишь сервер слушает какие происходят события на куче открытых сокетов и обрабатывает события по мере поступления, а не форкается или создает новый тред.
Есть системный вызов sendfile (= splice который замыкает открытый на чтение сокет на открытый на запись. Этот вызов хорошо использует HAProxy — прокси TCP.
Так вот, непонятно:
1) почему HTTP-proxy типа Nginx & Lighttpd используют его только изредка, для отправки статики.
Казалось бы хорошая схема: из открытого сокета прочитали HTTP-заголовки запроса, определились с тем куда запрос отправить, открыли сокет на запись, напихали в него заголовки, замкнули один на другой — и контент поехал самостоятельно на другой сервер.
2) Насколько серьезным будет overhead если дать при настройке proxy настройщику api (например, на lua — вроде быстрый и хорошо интегрируется то есть вызвать функцию:
на вход (сокет, HTTP-заголовки на выходе (сокет, HTTP-заголовки(по пути их можно изменить куда_посылать сервер открывает соединение к "куда посылать", пихает туда HTTP-заголовки, замыкает один сокет на другой и не парится больше.
Естественно, для полноценной работы нужна поддержка failover, балансировки нагрузки, подзапросов, но по такой же схеме эта проблема тоже решается.
Почему так не делают?

sergey_m

Дело в том, что
Есть системный вызов sendfile (= splice который замыкает открытый на чтение сокет на открытый на запись.
sendfile этого не делает. А что такое splice? Это из линукса?

Marinavo_0507

замкнули один на другой — и контент поехал самостоятельно на другой сервер
а разомкнуть потом?

pilot

sendfile этого не делает. А что такое splice? Это из линукса?
http://linux.die.net/man/2/splice :
Name
splice - splice data to/from a pipe
Synopsis
#define _GNU_SOURCE#include <fcntl.h>long splice(int fd_in, off_t *off_in,
int fd_out, off_t *off_out, size_t lenunsigned int " flags );
Description
splice moves data between two file descriptors without copying between kernel address space and user address space. It transfers up to len bytes of data from the file descriptor fd_in to the file descriptor fd_out, where one of the descriptors must refer to a pipe.
If fd_in refers to a pipe, then off_in must be NULL. If fd_in does not refer to a pipe and off_in is NULL, then bytes are read from fd_in starting from the current file offset, and the current file offset is adjusted appropriately. If fd_in does not refer to a pipe and off_in is not NULL, then off_in must point to a buffer which specifies the starting offset from which bytes will be read from fd_in; in this case, the current file offset of fd_in is not changed. Analogous statements apply for out_fd and off_out.
The flags argument is a bit mask that is composed by ORing together zero or more of the following values:

http://portal.acm.org/citation.cfm?id=511446.511449
http://haproxy.1wt.eu/download/1.3/doc/tcp-splicing.txt
И этот splicing встроен уже в HAProxy последних версий.

pilot

И этот splicing встроен уже в HAProxy последних версий.
Где-то вчера находил вопрос про splice, заданный Сысоеву в рассылке, который ответил что у нгинкса это sendfile.

sergey_m

Неправильно ответил, sendfile работает только с regular file.
Судя по мануалу этот линуксовый splice односторонний, что сильно накладывает ограничения на его использование в прокси.
То, что ты хочешь, умеют делать всякие "чёрные ящики" за кучу денег.

pilot

а разомкнуть потом?
А это вроде не нужно, "оно само".
Ну и я не специалист по низкоуровневым оптимизациям, я хочу аплоадить большие файлы, несколько Гб, на сервера, а все известные прокси почему-то сначала считывают весь запрос, а только потом обрабатывать начинают.
То есть в случае несколько Гб-ного файла нгинкс сначала сложит его себе на диск, только потом проксировать будет.

sergey_m

имхо, проще всего будет сделать так, чтобы аплоад-ссылка указывала сразу на бэкенд.

Marinavo_0507

А это вроде не нужно, "оно само".
Судя по ману, нужно указать длину, которая не всегда известна.
я хочу аплоадить большие файлы, несколько Гб
А ты должен этого хотеть? В HTTP докачка хотя бы предусмотрена для этого случая?
То есть в случае несколько Гб-ного файла нгинкс сначала сложит его себе на диск, только потом проксировать будет.
В принципе, идея для nginx правильная.
Потому что с одной стороны у него много медленных клиентов, а с другой - бекэнд, который ест сотни памяти на каждый запрос.
То есть надо, чтоб бекэнд быстро обработал запрос и освободил память для следующих, а клиент пусть не спеша скачивает/закачивает.

pilot

Судя по мануалу этот линуксовый splice односторонний, что сильно накладывает ограничения на его использование в прокси.
Хм, почитал про HAProxy, действительно, вроде он splice использует только для отправки ответа клиенту. Наверно я плохо понимаю как оно работает.
Тем не менее, непонятно почему прокси типа нгинкса не умеет проксировать запрос не получив его целиком. Пусть без splice. Этому что-то мешает? "Черные ящики" (что это?) для этого не нужны?
И вопрос про API ко всей этой штуке: пусть не splice, пусть перекладывалка контента из одного сокета в другом не в ядре, но почему все прокси в принципе устроены по типу "конфиг-файл", не дают API для нормальной манипуляции запросами?

pilot

Судя по ману, нужно указать длину, которая не всегда известна.
Написана в заголовке должна быть.
А ты должен этого хотеть? В HTTP докачка хотя бы предусмотрена для этого случая?

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

Нгинкс это не только вебсервер, это еще и прокси.

Marinavo_0507

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

pilot

Ну ты спрашиваешь, почему никто так не делает.
Да, мне непонятно есть ли принципиальные ограничения и насколько сложно реализуемо то что я хочу.
Вопрос про overhead на API все равно интересен:
В нгинксе есть embedded Perl & embedded Lua, и мне непонятно зачем нужны хитрые конфигурационные трюки с nginx, например, если неблокирующие быстрые операции легко выразимы на том же lua — поменять заголовки запроса, например, поменять url и тп. — просто никому не приходит в голову написать такой простой прокси или он будет медленный, опять же?

pilot

Судя по мануалу этот линуксовый splice односторонний, что сильно накладывает ограничения на его использование в прокси.
Какие все-таки ограничения?
нашел вот такие опции HAProxy:
option splice-auto
option splice-request
option splice-response
http://haproxy.1wt.eu/download/1.3/doc/configuration.txt

pilot

имхо, проще всего будет сделать так, чтобы аплоад-ссылка указывала сразу на бэкенд.
Бэкендов много, они стоят за прокси как раз.
PS: Я извращенец.

vall

у splice один из дескрипторов должен быть пайпом, чтоб передать на другой хост нужно два сплайса: в пайп и из пайпа. sendfile теперь так и работает со временным пайпом в ядре.

sergey_m

Бэкендов много, они стоят за прокси как раз.
Конечно ссылка должна генериться. Чтобы на разные бэкенды попадала.
Думаю, что такая конфигурация как у тебя действительно не задумывалась авторами проксей, а все кто с ней сталкиваются делают примерно так, как я предлагаю.

pilot

Можно пояснить для тупых типа меня? :crazy:
Я вижу прокси который splice умеет делать во все стороны — HAProxy.
Я вижу что Nginx & Lighttpd его вызывают только когда выдают статику.
Поэтому, вроде, требуемое поведение реализуемо в принципе. Так?

sergey_m

Суть в том, что sendfile отдаёт файл, а потом мы снова имеем дескриптор сокета из которого можно читать и писать. Если же бы существовал системный вызов, который в ядре свяжет два сокета, то потом мы уже не сможем попасть внутрь этого соединения, мы уже перестанем быть прокси. А современный HTTP подразумевает несколько запросов в одном соединении, то есть в большинстве сценариев после обработки отдельного запроса, нужно опять иметь контроль над сокетом, чтобы прочитать следующий запрос.

vall

splice позволяет переложить данные из сокета в сокет без копирования в юзерспейс.
но за два сискола через пайп который тут работает как ядерный буфер:
splice(in_sock, NULL, out_pipe, NULL, len, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
splice(in_pipe, NULL, out_sock, NULL, size, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
как-то так

tokuchu

А ты должен этого хотеть? В HTTP докачка хотя бы предусмотрена для этого случая?
Может какой-нибудь WebDAV такое описывает и автору проще им воспользоваться?

pilot

Понимаешь в чем дело, я не вижу зачем ты комментировал про splice. То есть где там противоречие — не вижу. Я не знаю как устроено ядро и сетевые соединения (shame on me как раз пытаюсь понять стоит ли копать в ту сторону — сколько надо копать чтобы решить такую проблему.
Конечно внутри какой-то буфер будет, но быстрый.
Или ты говоришь что 2 системных вызова это долго.
Непонятно.
Как я себе это дело представляю:
splice говорит "перекидывать отсюда туда байты", когда данные заканчиваются или байты идут в обратную сторону — splice прерывается и возвращает значение — кол-во переданных байтов. Сокеты при этом никуда не деваются, можно вызвать на них splice в обратную сторону.
При таком представлении не понимаю чем splice мешает прокси.
И вопрос про оверхед на апи все еще интересен :(

pilot

Может какой-нибудь WebDAV такое описывает и автору проще им воспользоваться?
Автору сложно воспользоваться WebDAV, так как он этот webdav реализует :crazy:

sergey_m

или байты идут в обратную сторону — splice прерывается
Разве такое в мане написано?

pilot

То есть тут 3 вопроса:
1. Почему не используют splice.
2. Почему так обращаются с контентом — не передают его сразу дальше. Даже опции такой нет.
3. Почему не делают нормальный API вместо конфигов.

kokoc88

как раз пытаюсь понять стоит ли копать в ту сторону — сколько надо копать чтобы решить такую проблему
А зачем тебе такое крутое вертикальное масштабирование? Мне кажется, что все вертикальные решения делаются дольше, устроены сложнее, содержат больше багов, и при этом всё равно ограничены.

pilot

Разве такое в мане написано?
Там написано что можно указать количество байтиков, которые этот splice работает. Так вот, мы же знаем количество байтиков которые мы хотим отдать бэкенду.
Что будет если хочется в другую сторону записывать — не сказано.

sergey_m

Теоретически будет работать. Но имхо проще отучить прокси класть запрос на диск, а начать его отдавать по мере поступления.

pilot

Теоретически будет работать. Но имхо проще отучить прокси класть запрос на диск, а начать его отдавать по мере поступления.
Про сплайс я спросил в связи с API:
Есть всякие асинхронные приблуды для Python, начиная со стандартного asyncore и до Tornado.
Там перекладывание данных реализуется как, грубо говоря, socket1.write(socket2.read(<кол-во байт>. Для меня пока загадка как быстро это работает для перелопачивания нескольких Гб по сравнению со splice и с буфером в том же nginx.

Marinavo_0507

Для меня пока загадка как быстро это работает для перелопачивания нескольких Гб по сравнению со splice и с буфером в том же nginx.
read+write - это одно лишнее копирование (или два? туплю)
скорость памяти представляешь - так что можешь понять, имеет ли смысл экономить на этом в твоей задаче

sergey_m

По идее аплоады 1 Гб это же тяжёлый, но редкий процесс. Обычно их не оптимизируют. Ну в смысле через диск конечно ахтунг, но через юзерленд память ничего страшного.

pilot

По идее аплоады 1 Гб это же тяжёлый, но редкий процесс. Обычно их не оптимизируют.
Как раз дело в том что надо сделать сервис для upload и download файлов, причем больших. В нем кроме этого ничего особо и не будет.
Ну в смысле через диск конечно ахтунг, но через юзерленд память ничего страшного.

Понял.

sergey_m

Как раз дело в том что надо сделать сервис для upload и download файлов, причем больших.
имхо, надо делать распределённую систему, а не вертикальную.

pilot

имхо, надо делать распределённую систему, а не вертикальную.
Да. Но ее делать сложнее в условиях сжатых сроков и непонятной квалификации смежников. На распределенную систему надо еще уговорить. Занимаюсь.
Но даже при распределенной системе в Интернет должно что-то торчать. И перед бэкендами (пусть даже они лопатят данные) должно что-то стоять — балансировать, резервировать, от DDoS защищать, скорость соединения ограничивать и т.п.

sergey_m

Но даже при распределенной системе в Интернет должно что-то торчать. И перед бэкендами (пусть даже они лопатят данные) должно что-то стоять — балансировать, резервировать, от DDoS защищать, скорость соединения ограничивать и т.п.
Ну так и должно быть. И только ссылка на сам аплоад должна идти прямо на бэкенд.
Оставить комментарий
Имя или ник:
Комментарий: