Комментарии 35
Не, вы уж извольте убрать gethostbyname в тесте TCP, а то нечестно как-то
0
Чем нечестно?
0
Тем, что для unix вы ничего подобного не делаете. Может, для хоста в форме ip адреса разницы практически и не будет, но для чистоты эксперимента...
0
Нечестность тут заложена в самой разнице между TCP и UNIX сокетами.
В реальной ситуации в конфигах можно написать и ip, и имя хоста, и при подключении gethostbyname используется.
Поэтому, я думаю, автор топика просто воссоздал реальную ситуацию, а не синтетическую.
В реальной ситуации в конфигах можно написать и ip, и имя хоста, и при подключении gethostbyname используется.
Поэтому, я думаю, автор топика просто воссоздал реальную ситуацию, а не синтетическую.
+4
Реальных ситуаций бесконечно много, поэтому в синтетических тестах не надо пытаться их воссоздавать.
Я бы и connect с close вынес за цикл и гонял бы не десяток байт, а несколько мегабайт
Я бы и connect с close вынес за цикл и гонял бы не десяток байт, а несколько мегабайт
0
А смысл в отдельном синтетическом тесте?
Разница по времени, по большей части, и получается изза того, что на установление соединения по TCP тратится больше времени, чем по UNIX.
Если использовать постоянные соединения, разницу можно уменьшить.
Разница по времени, по большей части, и получается изза того, что на установление соединения по TCP тратится больше времени, чем по UNIX.
Если использовать постоянные соединения, разницу можно уменьшить.
+1
Я боюсь, что десяток мегабайт показал бы ещё и проблемы с TCP window. Хотя, могу ошибаться, конечно.
+2
Нечестно тем что в нём нет необходимости внутри цикла — значение argv[1] не изменяется, а вероятность изменения адреса хоста сравнительно мала, к тому же, это довольно дорого — в зависимости от реализации, к примеру, он может каждый раз читать /etc/hosts — а это уже ужасно долго.
В случае же когда argv[1] уже является IP адресом (как в данном случае), вызов gethostbyname() — пустая трата времени.
В случае же когда argv[1] уже является IP адресом (как в данном случае), вызов gethostbyname() — пустая трата времени.
0
Вы придираетесь к коду, который специально был написан, чтобы показать "среднюю температуру по больнице"? Дело же не в том, что "можно оптимизировать конкретно этот код". Пример просто наглядно показывает разницу во времени.
Представьте, что это не один клиент много раз подключается, а много клиентов подключаются один раз. В таком случае нельзя будет убрать gethostbyname из цикла. Просто много параллельных клиентов не смогут показать разницу во времени так явно.
Представьте, что это не один клиент много раз подключается, а много клиентов подключаются один раз. В таком случае нельзя будет убрать gethostbyname из цикла. Просто много параллельных клиентов не смогут показать разницу во времени так явно.
+1
Не, вы уж извольте убрать gethostbyname в тесте TCP, а то нечестно как-то
1) Это как раз честно. Представьте socket-клиент в реальных условиях. Будет ли он без gethostbyname?
2) На самом деле вес gethostbyname в массе(да пусть поперхнутся физики) ничтожен. Даже на НЕ-локальных вызовах он окажет куда меньшее влияние чем может оказать, например, размер пакета.
-1
Хотел вас поддержать (gethostbyname может легко отъесть много секунд при обращении к реальному днс), но здесь, при передаче 127.0.0.1 он вообще не влияет ни на что (как и все остальные манипуляции по заполнению структур — специально их вынес все за цикл, ничего не поменялось, вообще...)
0
gethostbyname — дешёвая операция, как в контексте данного теста
http://www.opennet.ru/cgi-bin/opennet/man.cgi?topic=gethostbyname
так и при реальном разрешении имён, т.к. будет задействован механизм NSCD (если только не делать принудительный invalidate кеша).
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 кеша).
+1
Вообще, gethostbyname() уже давно даже не в стандарте POSIX, и нужно использовать getaddrinfo().
Так вот, в последней с кешированием не все так радужно (например, для имен с несколькими IP-адресами).
В реальных высоконагруженных проектах, в обработчиках запросов использовать getaddrinfo категорически не рекомендую. Даже если nscd кеширует все очень быстро и правильно, делать каждый раз запрос к nscd-демону для получения информации, которая может храниться в памяти, очень дорого. В реальном проекте пришлось использовать связку c-ares с собстенным велосипедом хранения IP-адресов в структурах памяти процесса.
Так вот, в последней с кешированием не все так радужно (например, для имен с несколькими IP-адресами).
В реальных высоконагруженных проектах, в обработчиках запросов использовать getaddrinfo категорически не рекомендую. Даже если nscd кеширует все очень быстро и правильно, делать каждый раз запрос к nscd-демону для получения информации, которая может храниться в памяти, очень дорого. В реальном проекте пришлось использовать связку c-ares с собстенным велосипедом хранения IP-адресов в структурах памяти процесса.
0
Если кто пропустил, то в статье речь об этой заметке: https://habrahabr.ru/company/pushall/blog/280218/
Автору браво за развернутое объяснение.
Автору браво за развернутое объяснение.
+4
А про UDP будет подобный обзор?
0
По наличию оверхеда понятно.
Непонятно другое — часто при больших нагрузках на файловый сокет, тот просто перестает отзываться.
Т.е. запускаем php-fpm с файловым сокетом, и имеем периодические ошибки в логе нжинкса по причине «11: Resource temporarily unavailable while connecting to upstream».
Переконфигурирем все на tcp, без каких либо еще изменнений, и тот же самый тазик держит нагрузку в разы больше.
Не думаю что это баг именно пхп, т.к. аналогичное наблюдение было с кастомным демоном на С, также с fastcgi режиме с нжинксом.
Непонятно другое — часто при больших нагрузках на файловый сокет, тот просто перестает отзываться.
Т.е. запускаем php-fpm с файловым сокетом, и имеем периодические ошибки в логе нжинкса по причине «11: Resource temporarily unavailable while connecting to upstream».
Переконфигурирем все на tcp, без каких либо еще изменнений, и тот же самый тазик держит нагрузку в разы больше.
Не думаю что это баг именно пхп, т.к. аналогичное наблюдение было с кастомным демоном на С, также с fastcgi режиме с нжинксом.
+1
А у вас случаем не упиралось всё в лимит по дескрипторам? UNIX сокеты работают просто замечательно, но их, как и TCP, надо уметь настраивать.
+1
Неа. Все что можно выкрутить в sysctl, выкручено на максимум.
Для tcp сокетов тоже надо с бубном плясать для нормальных нагрузок. Но они таки работают после этого.
Для tcp сокетов тоже надо с бубном плясать для нормальных нагрузок. Но они таки работают после этого.
0
Подтверждаю аналогичную проблему со связкой nginx — uwsgi. До некоторых пор все ок, а потом начинается свистопляска (у нас не прокатило более 500 запросов в сек, если память не изменяет...)
0
Всё это конечно прекрасно, но на уровне вопроса: что быстрее в php – echo или print?
Да unix сокеты "типа" быстрее, однако вся эта быстрота быстро нивелируется отсутствием нормальных инструментов дебага общения через него.
Да и мне лень смотреть в код, но если не изменяет память, там как-то всё очень плохо с backlog'ом. И бёрсты такая конструкция будет дропать.
Да unix сокеты "типа" быстрее, однако вся эта быстрота быстро нивелируется отсутствием нормальных инструментов дебага общения через него.
Да и мне лень смотреть в код, но если не изменяет память, там как-то всё очень плохо с backlog'ом. И бёрсты такая конструкция будет дропать.
0
Есть определённые плюшки от использования unix-сокетов. Например исчезает проблема с time-wait сокетами внутри машины. Текущие механизмы recycle и reuse могут давать всплески до сотен миллисекунд. Конечно эту проблему частично решает keep-alive, но когда локальный бекенд начинает таймаутить и какой-нить nginx начинает переустанавливать сокеты — превед time-wait.
Кроме того, если достаточно большой rate i/o, то unixsocket экономит процессор по sys time, они банально дешевле для системы.
Если это так, то это не минус, это нюанс. Для некоторых частей системы лучше быстро дропать запросы, чем зря складывать их в беклог. Если эта компонента ненапрямую отвечает пользователю (мы ведь врятли через unixsocket напрямую отвечаем тысячам пользователей?), то это даже огромная плюшка для балансера, который быстрее сможет поретраиться в другой бекенд.
Кроме того, если достаточно большой rate i/o, то unixsocket экономит процессор по sys time, они банально дешевле для системы.
Да и мне лень смотреть в код, но если не изменяет память, там как-то всё очень плохо с backlog'ом. И бёрсты такая конструкция будет дропать.
Если это так, то это не минус, это нюанс. Для некоторых частей системы лучше быстро дропать запросы, чем зря складывать их в беклог. Если эта компонента ненапрямую отвечает пользователю (мы ведь врятли через unixsocket напрямую отвечаем тысячам пользователей?), то это даже огромная плюшка для балансера, который быстрее сможет поретраиться в другой бекенд.
+2
Немного оффтоп, но меня всегда очень интересовало — почему нет именованных портов у сетевых сокетов? Например "{ip}:web_server"?
0
Кстати, вообще есть. В unix системах есть файл /etc/services (в нагрузку к /etc/hosts и /etc/protocols).
Большинство утилит умеют работать с этим файлом и если вы напишете "www", они "разрезолвят" его в 80.
Большинство утилит умеют работать с этим файлом и если вы напишете "www", они "разрезолвят" его в 80.
+4
Спасибо, не знал. Но почему нет первокласнных именованных портов как часть стека? Просто "так вышло", или на это были и есть какие-то причины?
0
Что значит "первоклассный именованый порт"? Вы не про 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".
Вообще порт — часть стека 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".
0
Нет, я совсем не имел в виду DNS.
Про формат хранения строки — да, пришлось бы выбрать формат, скажем 7-bit ASCII, с ограничением длины строки.
Зачем хранить все возможные строки? Я, наверное, что-то глубинное здесь не понимаю, я представляю себе это так — сейчас скажем сервер слушает на портах 1111 и 2222. Если клиент пытается подсоединиться к server_ip:1111 или server_ip:2222, у него получается. На все остальные порты — не получается, так как там никто не слушает. Я просто говорил о возможности идентифицировать эти порты строками, что сервер мог (вдобавок к нумерическим портам) сказать "я слушаю на порте 'lexore'", а клиент подключался бы к server_ip:lexore.
Тут и плюсы кстати есть — например, сканировать порты стало бы сильно сложнее.
Про формат хранения строки — да, пришлось бы выбрать формат, скажем 7-bit ASCII, с ограничением длины строки.
Зачем хранить все возможные строки? Я, наверное, что-то глубинное здесь не понимаю, я представляю себе это так — сейчас скажем сервер слушает на портах 1111 и 2222. Если клиент пытается подсоединиться к server_ip:1111 или server_ip:2222, у него получается. На все остальные порты — не получается, так как там никто не слушает. Я просто говорил о возможности идентифицировать эти порты строками, что сервер мог (вдобавок к нумерическим портам) сказать "я слушаю на порте 'lexore'", а клиент подключался бы к server_ip:lexore.
Тут и плюсы кстати есть — например, сканировать порты стало бы сильно сложнее.
0
Ну, стоит вспомнить, что порты источника и получателя пишутся в каждый TCP пакет. Сейчас это 16 бит на порт "откуда" и 16 бит на порт "куда", т.е. 32 бита, или 4 байта. А в случае с текстовыми портами, скажем по 8 символов, это уже 16 байт. И 20 байтный пакет вырастает на 60%
И остается вопрос "зачем?". Зачем увеличивать заголовок на 60%, когда можно всего один раз "разрезолвить" номер порта по имени из файла /etc/services.
Лично я даже в 2016 году на гигабитных каналах не вижу объективных преимуществ.
А в 70-80е годы XX века никто даже не думал так делать. Если вы заметите, все делалось на числах. причем самой маленькой длины — порту даже не 4 байта отвели, а 2.
И остается вопрос "зачем?". Зачем увеличивать заголовок на 60%, когда можно всего один раз "разрезолвить" номер порта по имени из файла /etc/services.
Лично я даже в 2016 году на гигабитных каналах не вижу объективных преимуществ.
А в 70-80е годы XX века никто даже не думал так делать. Если вы заметите, все делалось на числах. причем самой маленькой длины — порту даже не 4 байта отвели, а 2.
0
формат tcp-заголовка (как и udp) позволяет идентифицировать порт 16 битами. Поменять число на текст — значит существенно раздуть формат заголовка без какого либо профита и выкинуть на свалку все имеющееся сетевое оборудование.
0
Тогда пришлось бы городить ещё один DNS для резолва портов. Или я что-то не понял?
0
НЛО прилетело и опубликовало эту надпись здесь
У меня тоже самое бывало с питоновским приложением и использованием сокета в гуникорне для nginx.
0
Линуксы (и другие ос) отлично умеют оптимизировать коннекты на lo. Для них не высчитывается чексуммы и не выполняется маршрутизация. Объем работы с файловыми советами сравним с сетью (vfs тоже не Самая быстрая штука, если что).
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Немного о сокетах, redis и битых яйцах