Pull to refresh

Comments 35

Не, вы уж извольте убрать gethostbyname в тесте TCP, а то нечестно как-то
Тем, что для unix вы ничего подобного не делаете. Может, для хоста в форме ip адреса разницы практически и не будет, но для чистоты эксперимента...
Нечестность тут заложена в самой разнице между TCP и UNIX сокетами.
В реальной ситуации в конфигах можно написать и ip, и имя хоста, и при подключении gethostbyname используется.
Поэтому, я думаю, автор топика просто воссоздал реальную ситуацию, а не синтетическую.
Реальных ситуаций бесконечно много, поэтому в синтетических тестах не надо пытаться их воссоздавать.
Я бы и connect с close вынес за цикл и гонял бы не десяток байт, а несколько мегабайт
А смысл в отдельном синтетическом тесте?
Разница по времени, по большей части, и получается изза того, что на установление соединения по TCP тратится больше времени, чем по UNIX.
Если использовать постоянные соединения, разницу можно уменьшить.
Я боюсь, что десяток мегабайт показал бы ещё и проблемы с TCP window. Хотя, могу ошибаться, конечно.
Нечестно тем что в нём нет необходимости внутри цикла — значение argv[1] не изменяется, а вероятность изменения адреса хоста сравнительно мала, к тому же, это довольно дорого — в зависимости от реализации, к примеру, он может каждый раз читать /etc/hosts — а это уже ужасно долго.
В случае же когда argv[1] уже является IP адресом (как в данном случае), вызов gethostbyname() — пустая трата времени.
Вы придираетесь к коду, который специально был написан, чтобы показать "среднюю температуру по больнице"? Дело же не в том, что "можно оптимизировать конкретно этот код". Пример просто наглядно показывает разницу во времени.
Представьте, что это не один клиент много раз подключается, а много клиентов подключаются один раз. В таком случае нельзя будет убрать gethostbyname из цикла. Просто много параллельных клиентов не смогут показать разницу во времени так явно.
Не, вы уж извольте убрать gethostbyname в тесте TCP, а то нечестно как-то

1) Это как раз честно. Представьте socket-клиент в реальных условиях. Будет ли он без gethostbyname?
2) На самом деле вес gethostbyname в массе(да пусть поперхнутся физики) ничтожен. Даже на НЕ-локальных вызовах он окажет куда меньшее влияние чем может оказать, например, размер пакета.
В реальных условиях, если вы заботитесь о производительности хоть немного, вы не будете на каждые 10 отправленных байт открывать сокет, вызывать gethostbyname и т.д.
Хотел вас поддержать (gethostbyname может легко отъесть много секунд при обращении к реальному днс), но здесь, при передаче 127.0.0.1 он вообще не влияет ни на что (как и все остальные манипуляции по заполнению структур — специально их вынес все за цикл, ничего не поменялось, вообще...)
gethostbyname — дешёвая операция, как в контексте данного теста
http://www.opennet.ru/cgi-bin/opennet/man.cgi?topic=gethostbyname
Если name является адресом IPv4 или IPv6, то поиск не производится и gethostbyname() просто копирует name в поле h_name, а его эквивалент для структуры struct in_addr копируется в поле h_addr_list[0] возвращаемой структуры hostent

так и при реальном разрешении имён, т.к. будет задействован механизм NSCD (если только не делать принудительный invalidate кеша).
Вообще, gethostbyname() уже давно даже не в стандарте POSIX, и нужно использовать getaddrinfo().
Так вот, в последней с кешированием не все так радужно (например, для имен с несколькими IP-адресами).
В реальных высоконагруженных проектах, в обработчиках запросов использовать getaddrinfo категорически не рекомендую. Даже если nscd кеширует все очень быстро и правильно, делать каждый раз запрос к nscd-демону для получения информации, которая может храниться в памяти, очень дорого. В реальном проекте пришлось использовать связку c-ares с собстенным велосипедом хранения IP-адресов в структурах памяти процесса.
А зачем?
С чем его сравнивать?
Говоря о UDP подразумевая SOCK_DGRAM, т.е. на доменах AF_UNIX/AF_INET имеем те же отличия что и при SOCK_STREAM.
Говоря о UDP подразумевая отличия в одном домене имён между SOCK_DGRAM и SOCK_STREAM имеем негарантированную доставку пакета ограниченного размера.
По наличию оверхеда понятно.
Непонятно другое — часто при больших нагрузках на файловый сокет, тот просто перестает отзываться.

Т.е. запускаем php-fpm с файловым сокетом, и имеем периодические ошибки в логе нжинкса по причине «11: Resource temporarily unavailable while connecting to upstream».

Переконфигурирем все на tcp, без каких либо еще изменнений, и тот же самый тазик держит нагрузку в разы больше.

Не думаю что это баг именно пхп, т.к. аналогичное наблюдение было с кастомным демоном на С, также с fastcgi режиме с нжинксом.
А у вас случаем не упиралось всё в лимит по дескрипторам? UNIX сокеты работают просто замечательно, но их, как и TCP, надо уметь настраивать.
Неа. Все что можно выкрутить в sysctl, выкручено на максимум.
Для tcp сокетов тоже надо с бубном плясать для нормальных нагрузок. Но они таки работают после этого.
Могу ошибаться, но у unix сокета кажется просто меньше размер backlog. В случае tcp можно настроить довольно большое значение, а у unix сокета есть ограничение.
Подтверждаю аналогичную проблему со связкой nginx — uwsgi. До некоторых пор все ок, а потом начинается свистопляска (у нас не прокатило более 500 запросов в сек, если память не изменяет...)
Всё это конечно прекрасно, но на уровне вопроса: что быстрее в php – echo или print?
Да unix сокеты "типа" быстрее, однако вся эта быстрота быстро нивелируется отсутствием нормальных инструментов дебага общения через него.
Да и мне лень смотреть в код, но если не изменяет память, там как-то всё очень плохо с backlog'ом. И бёрсты такая конструкция будет дропать.
Есть определённые плюшки от использования unix-сокетов. Например исчезает проблема с time-wait сокетами внутри машины. Текущие механизмы recycle и reuse могут давать всплески до сотен миллисекунд. Конечно эту проблему частично решает keep-alive, но когда локальный бекенд начинает таймаутить и какой-нить nginx начинает переустанавливать сокеты — превед time-wait.
Кроме того, если достаточно большой rate i/o, то unixsocket экономит процессор по sys time, они банально дешевле для системы.
Да и мне лень смотреть в код, но если не изменяет память, там как-то всё очень плохо с backlog'ом. И бёрсты такая конструкция будет дропать.

Если это так, то это не минус, это нюанс. Для некоторых частей системы лучше быстро дропать запросы, чем зря складывать их в беклог. Если эта компонента ненапрямую отвечает пользователю (мы ведь врятли через unixsocket напрямую отвечаем тысячам пользователей?), то это даже огромная плюшка для балансера, который быстрее сможет поретраиться в другой бекенд.
Немного оффтоп, но меня всегда очень интересовало — почему нет именованных портов у сетевых сокетов? Например "{ip}:web_server"?
Кстати, вообще есть. В unix системах есть файл /etc/services (в нагрузку к /etc/hosts и /etc/protocols).
Большинство утилит умеют работать с этим файлом и если вы напишете "www", они "разрезолвят" его в 80.
Спасибо, не знал. Но почему нет первокласнных именованных портов как часть стека? Просто "так вышло", или на это были и есть какие-то причины?
Что значит "первоклассный именованый порт"? Вы не про DNS случаем?
Вообще порт — часть стека TCP/IP. Этот стек был разработан в 1977 и внедрен в 1983.
А DNS был разработан через несколько лет. Хочу напомнить, чтобы работа глобального DNS обеспечивается нехилой такой прослойкой "управляющая организация + корневые сервера + регистраторы + доменные хостеры + кеширующие серверы + 100500 продавцов и покупателей доменов + куча бумаг и денег на все это дело".
Вы же не хотите регистрировать свое название порта, с заверением всех документов?
А вообще при разработке TCP/IP думали о скорости и эффективности для компьютера, а не об удобстве пользователя. Хранить порт в виде числа куда удобнее и практичнее, чем строку.
Во первых в каком формате хранить строку?
Это 7 битный ascii, или 8 битный, или 8 битный utf, или 16/32 битный? Ну ладно, тогда ещё не было UTF.
А какой длинны должна быть строка?
А где хранить все возможные известные строки?
Что делать, если запрос придет на порт web_server, а система знает только про www?
А если и www ещё не изобрели, так как он появился только в 1991 году?
А как добавить новый порт?
В текущей системе вы, кстати, можете сами редактировать /etc/services файл и система тут же начнет понимать новые названия портов.
Если вы сами попробуете реализовать порт в виде строки, то скоро увидите, что разработчики выбрали самую эффективную реализацию:
Порт в виде числа + текстовый файл с названиями.
P.S.
Кстати, вы в своем вопросе про "{ip}:web_server" уже наступили на грабли названий — правильно писать "www".
Нет, я совсем не имел в виду DNS.
Про формат хранения строки — да, пришлось бы выбрать формат, скажем 7-bit ASCII, с ограничением длины строки.
Зачем хранить все возможные строки? Я, наверное, что-то глубинное здесь не понимаю, я представляю себе это так — сейчас скажем сервер слушает на портах 1111 и 2222. Если клиент пытается подсоединиться к server_ip:1111 или server_ip:2222, у него получается. На все остальные порты — не получается, так как там никто не слушает. Я просто говорил о возможности идентифицировать эти порты строками, что сервер мог (вдобавок к нумерическим портам) сказать "я слушаю на порте 'lexore'", а клиент подключался бы к server_ip:lexore.
Тут и плюсы кстати есть — например, сканировать порты стало бы сильно сложнее.
Ну, стоит вспомнить, что порты источника и получателя пишутся в каждый TCP пакет. Сейчас это 16 бит на порт "откуда" и 16 бит на порт "куда", т.е. 32 бита, или 4 байта. А в случае с текстовыми портами, скажем по 8 символов, это уже 16 байт. И 20 байтный пакет вырастает на 60%
И остается вопрос "зачем?". Зачем увеличивать заголовок на 60%, когда можно всего один раз "разрезолвить" номер порта по имени из файла /etc/services.
Лично я даже в 2016 году на гигабитных каналах не вижу объективных преимуществ.
А в 70-80е годы XX века никто даже не думал так делать. Если вы заметите, все делалось на числах. причем самой маленькой длины — порту даже не 4 байта отвели, а 2.
формат tcp-заголовка (как и udp) позволяет идентифицировать порт 16 битами. Поменять число на текст — значит существенно раздуть формат заголовка без какого либо профита и выкинуть на свалку все имеющееся сетевое оборудование.
Тогда пришлось бы городить ещё один DNS для резолва портов. Или я что-то не понял?
UFO just landed and posted this here
У меня тоже самое бывало с питоновским приложением и использованием сокета в гуникорне для nginx.
Линуксы (и другие ос) отлично умеют оптимизировать коннекты на lo. Для них не высчитывается чексуммы и не выполняется маршрутизация. Объем работы с файловыми советами сравним с сетью (vfs тоже не Самая быстрая штука, если что).
Sign up to leave a comment.

Articles