LINUX.ORG.RU

Асинхронная обработка tcp запросов

 ,


0

3

Всем привет.

Для того, чтобы принимать запросы пользователей в сокет и дальше читать и писать в сокет можно использовать современный liburing.

Всё просто, если пишем эхо сервер. А если на формирование ответа требуется время (сходить в базу данных или открыть большой файл или сделать запрос по сети к другому сервису), то хотелось бы делать это асинхронно.

Один из вариантов это использовать потоки. Сторонние библиотеки использовать не хочется (libuv или libev).

Кроме потоков есть ещё варианты?

Ответ на: комментарий от Skullnet

У тебя представления о работе веб-серверов из 90-х. Да, тогда и правда были идеи мультитредов на всё подряд.

тот же apache

Ага, веб-сервер из 90-х как раз. Закопай и осваивай nginx.

firkax ★★★★★
()
Ответ на: комментарий от Shadow

Потоки не в ядре это вообще полная бессмыслица. Единственное чем они могут пригодиться - это подкостылить неумеху-программиста, который не умеет делать event loop, зато по мультитред-учебникам из 90-х научился спамить треды на каждую мелочь - так он сможет, не переучиваясь, чуть улучшить свой софт по сравнению с ядерными тредами.

У мультитреда всего два нормальных применения есть:

1) ждать в тредах безальтернативно блокирующие сисколлы (обычно это работа с файлами)

2) получить возможность сожрать больше одного ядра проца числодробилками.

firkax ★★★★★
()

Не знаю что такое liburing, но все сетевые задачи (как приём соединений клиентов так и отправка запросов в базу или ещё куда по сети с ожиданием ответов) мультиплексируются через select/epoll/kqueue. С диском да, либо надеемся что он много не потратит, либо делаем для него отдельный тред и обмениваемся с ним запросами-ответами.

firkax ★★★★★
()
Ответ на: комментарий от Nazar22

при использовании event loop очередь клиентов будет ждать, пока идёт обработка запроса текущего клиента. если обработка долгая, то у клиентов задержка получения ответа

И сверху все заполировать Fan-In Fan-Out Concurrency Pattern, чтобы уже наверняка…

Obezyan
()
Ответ на: комментарий от Enthusiast

Нет, конечно! Никакого позикса. Зеленые потоки легковесные.

К ним ближе всего асинхронные вычисления по сути. Только разница между зелеными потоками и асинхронными вычислениями в том, что зеленые потоки поддерживаются рантаймом какого-нибудь языка, и их присутствие почти не заметно - выглядит как деталь реализации.

Для асинхронных вычислений, как правило, нужно писать код. См. котлин, C#, F# и тому подобное. Async/await расставляется в ручную.

Зеленость же потока ты и вовсе можешь и не заметить, как например, в хаскеле.

Что касается плюсов и сишки, то там такой сущности нет по определению. Слишком близки эти два языка к железу.

Что касается раста. С асинхронными вычислениями будет много сложнее, чем в том же котлине, но много проще, чем в плюсах. Это если кратко.

Возможно, что скорость исполнения в расте будет хороша. Кстати, хороший вариант.

У меня же реальный опыт с многопоточными серверами только на плюсах с asio.

anonymous
()
Ответ на: комментарий от anonymous

Ну, и то, что одну тысячу соединений держат позиксные потоки - тоже из опыта. Здесь предполагается, что данные передаются по сети регулярно и активно, но с некоторой разумной задержкой. Линукс вполне тысячу, две тысячи системных потоков держит.

Про проблему select в линуксе (а не солярисе!) с большим количеством дескрипторов, вы, наверное, в курсе. Так, на всякий случай

anonymous
()
Ответ на: комментарий от anonymous

Ну, и то, что одну тысячу соединений держат позиксные потоки - тоже из опыта… Линукс вполне тысячу, две тысячи системных потоков держит.

Пока ваши «тысячи потоков» дерутся за CPU довольно непредсказуемым образом, ваши конкуренты оставляют вас далеко позади на асинхронщине с минимумом накладных расходов на переключения контекстов и с прочими плюшками вроде осмысленной приоритизации запросов.

Про проблему select в линуксе (а не солярисе!)

Я не вижу как select() в принципе сделать лучше чем O(N), epoll() решает все эти проблемы.

bugfixer ★★★★★
()
Ответ на: комментарий от firkax

У тебя представления о работе веб-серверов из 90-х. Да, тогда и правда были идеи мультитредов на всё подряд.

Какой мультитред в 90-е? Что вообще за дичь. У меня ещё в 2004 году был одноядерный компьютер, и только где-то к 2006-2007 году начали подвозить процессоры с несколькими ядрами, а настоящий многопоточный софт начали осваивать только спустя пару лет и то, иногда, чтобы обходить всякие ограничения. Хотя бы вспомни, что даже браузеры всё в одном потоке делали и это жутко тормозило.

Skullnet ★★★★☆
()
Последнее исправление: Skullnet (всего исправлений: 2)
Ответ на: комментарий от bugfixer

Так select можно делать в каждом потоке отдельно, потоке, который обслуживает одно соединение. Тогда N будет 1. Здесь другая модель, не как у асинхронщины. (хотя как это множество select обслуживается на уровне системы - не знаю точно)

И я не спорю про большое количество соединений. Там позиксные потоки заплюхаются - сам видел в тестах. Просто есть такое N, что можно решить позиксными потоками. Ну, а дальше асинхронщина или зеленые потоки, без вариантов.

Кстати, дополню на счет зеленых потоков. Имеет смысл городить специальный рантайм в языке для них, только если операции ввода-вывода будут сами асинхронными. Именно так и сделано в хаскеле. Причем, прелесть хаскеля в том, что ты не заморачиваешься такими деталями - просто пишешь безнес-логику. Работаешь с вводом-выводом как с синхронным, а он на деле-то асинхронный!

anonymous
()
Ответ на: комментарий от firkax

Единственное чем они могут пригодиться - это подкостылить неумеху-программиста, который не умеет делать event loop

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

Skullnet ★★★★☆
()
Ответ на: комментарий от anonymous

Так select можно делать в каждом потоке отдельно, потоке, который обслуживает одно соединение. Тогда N будет 1.

Спешу расстроить - не получится. Если конечно у вас все потоки на одном и том же низком дескрипторе не спят, что бессмысленно.

bugfixer ★★★★★
()
Ответ на: комментарий от Skullnet

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

Довольно забавно это слышать от человека который multi-CPU HW увидел только после того как оно на консьюмерский рынок просочилось. Ага.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Большинство потоков как раз на селекте (полле) и сидят, что почти сон. Поэтому особый гонки у системного планировщика нет. Причем данные гуляют довольно активно. Хотя при большей активности, наверное, пришлось бы что-то менять

anonymous
()
Ответ на: комментарий от Enthusiast

Большинство потоков вэб-сервера будет находиться в состоянии ожидания ввода/вывода данных с диска и поэтому затраты планировщика операционной системы на переключение потоков будут малы - зашел, увидел «спячку», вышел.

Об том и речь. Ты создал 500 потоков, но всё равно в конкретный момент времени работает 1, остальные ждут данные. Вопрос: а нахрена тогда потоки, если можно то же самое делать в 1 поток?

Зато работает запросопоточный вэб-сервер просто

Это уже вопрос абстракции. Можно зелёные потоки использовать, например, как метод асинхронщины.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Об том и речь. Ты создал 500 потоков, но всё равно в конкретный момент времени работает 1, остальные ждут данные. Вопрос: а нахрена тогда потоки, если можно то же самое делать в 1 поток?

Представим, что пришёл ты за едой в большой продовольственный магазин. Выбрал еду, положил в корзинку и идёшь платить. Подходишь к кассам и видишь, что работает одна автоматическая касса, куда люди подходят с ручными, маленькими корзинками, и десяток обычных касс с кассиршами, где расплачиваются покупатели с огромными телегами с едой. Скорость обслуживания автоматической кассы составляет десять человек в час, а скорость обычной кассы с кассиршей - два человека в час. Ты подумал, посмотрел, что набрал немного товаров, и пошёл на автоматическую быструю кассу. Люди обслуживаются быстро, всё идёт хорошо.

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

Запросопоточный сервер будет работать устойчивее из-за того, что один «кривой» пользовательский запрос не заклинит всю остальную очередь запросов, а просто «подвесит» один поток.

К тому же все ядра процессора будут загружены, как и множество кассирш в магазине на параллельных кассах. Что-то да дадут эти ядра в общей силе вычислений.

Enthusiast ★★★
()
Ответ на: комментарий от Enthusiast

Какая-то невразумительная каша. Правильная аналогия будет такая: у тебя 10 очередей, 5 кассирш (cpu), но одна касса (диск).

тому же все ядра процессора будут загружены, как и множество кассирш в магазине на параллельных кассах

Нет, не будут. Диск у тебя один.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от anonymous

Нет, конечно! Никакого позикса. Зеленые потоки легковесные. К ним ближе всего асинхронные вычисления по сути. Только разница между зелеными потоками и асинхронными вычислениями в том, что зеленые потоки поддерживаются рантаймом какого-нибудь языка, и их присутствие почти не заметно - выглядит как деталь реализации.

Насколько я понял, «зелёные» потоки это обёртка над «ядерными» потоками, но со своим планировщиком «зелёных» потоков. То есть когда возникнут ограничения ядра «Линукса» на выделение стековой памяти для «ядерного» потока, то дополнительные «зелёные» потоки создадутся, но будут по очереди запускаться на меньшем количестве «ядерных» потоков. Некоторые «зелёные» потоки могут быть приостановлены своим «зелёным» планировщиком при этом.

Выходит так, что «зелёные» потоки не могут быть быстрее «ядерных» потоков, ибо они сами в итоге работают на «ядерных» потоках. Просто количество одновременно работающих «зелёных» потоков может быть больше, но с меньшей производительностью каждого из них по сравнению с «ядерным» потоком.

Enthusiast ★★★
()
Ответ на: комментарий от Enthusiast

Выходит так, что «зелёные» потоки не могут быть быстрее «ядерных» потоков

Ты сначала разберись, а потом рассуждай. Зеленые потоки, это просто интерфейс в виде потоков к асинхронщине. Это то же самое что генераторы, или async/await вид сбоку. К потокам ОС это не имеет отношения. Планировщик зелёных потоков это ивент луп.

no-such-file ★★★★★
()
Ответ на: комментарий от Skullnet

Какой мультитред в 90-е? Что вообще за дичь. У меня ещё в 2004 году был одноядерный компьютер

мультитред не имеет отношения к числу ядер. он имеет отношение к активностям, происходящим паралельно(истинно или псевдо) со своим контекстом исполнения.

уже два одноядерных проца, это «мультитред». компы, связанные по сети уже работают как «мультитред».

на проце могут крутиться тыщи потоков, псевдопаралельно, даже если там одно ядро. и даже два ядра. и даже 100. число потоков ограничено памятью и неким рационально выбранным потолком, который поддерживает кернел, чисто в силу особенностей реализации, а не числом физических ядер.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от Skullnet

И что? А тред-спамеры появились в 90-е. И апач твой любимый оттуда же. Да, это было по-дурацки, да и сейчас осталось по-дурацки, потому что дело не в ядрах, вер-сервер не упирается в проц обычно.

firkax ★★★★★
()
Ответ на: комментарий от no-such-file

Вопрос: а нахрена тогда потоки, если можно то же самое делать в 1 поток?

чтобы все делать в один поток, тебе придется ручками спасать контекст одного вычисления, и переключаться на другое, восстанавливая его контекст.

и после долгих раздумий ты получишь стекфул корутины. которые и есть такие ручками управляемые треды.

а как только ты станешь переключать корутины не ручками, а по неким ивентам - ты получишь просто треды.

alysnix ★★★
()
Ответ на: комментарий от alysnix

чтобы все делать в один поток, тебе придется ручками спасать контекст одного вычисления, и переключаться на другое, восстанавливая его контекст.

Чтобы всё делать в один поток, надо сделать так, чтобы контекст не надо было «спасать и переключать». И делается это на уроврне проектирования твоей проги, а не подключением магических библиотек. Магические библиотеки в любом случае будут добавлять свои тормоза, польза тут, как я уже писал, только в том, что с их помощью программист, который нормально делать вообще не умеет, сможет сделать хоть не совсем ужасно.

firkax ★★★★★
()
Ответ на: комментарий от Skullnet

Какой мультитред в 90-е? Что вообще за дичь.

исторически все многопользовательские ОС были многопроцессными, то есть еще более изолированными друг от друга активностями, чем треды.

а процессор был один, большой и толстый, с кучей проводов, в виде шкафа.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от firkax

Чтобы всё делать в один поток, надо сделать так, чтобы контекст не надо было «спасать и переключать».

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от qulinxao3

истоистерически были маняфрэймы которые «почти» всегда мульти ядерны

без разницы. там всегда число онлайн юзеров было больше числа физ. ядер. то есть мультипроцесс эмулировался программно, и потому число физ ядер тут не играет роли(лишь повышает отзывчивость всей системы за счет повышенной выч. мощи)

alysnix ★★★
()
Ответ на: комментарий от Enthusiast

зелённые <потоки> потому и быстрее что можно оставаться в контексте текущего процесса т.е меньше ресурсов нужно переключать при переключении потоков (если в зелёнке и они сами из одного процесса)

qulinxao3 ★☆
()
Ответ на: комментарий от alysnix

ну не/

есть принципиальная граница

утя один проц вседержатель у которого не может быть гонки с самим собой - гонки могут быть в бирюльках которыми жонглирует процессорный блок в каком-то порядке - и клинч на ресурсах

и если у тя 2(и более) одновременно действющих - и поэтому всякие коггерентности нужно уже на апаратном уровне

реальный прикол что и переход с 2 на большее(3 и более) число действующих относитительно независимо УУ тоже привозит внезапности

qulinxao3 ★☆
()
Ответ на: комментарий от qulinxao3

утя один проц вседержатель у которого не может быть гонки с самим собой

может конечно. типичный пример - любая прога с гуем.

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

потому что они конкурируют за процессор! пока идут вычисления - гуй стоит. пока рисуется гуй - стоят вычисления.

и решается это нормальным образом только разнесением гуя и вычислений в разные вычислительные потоки. то есть в разные треды.

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

alysnix ★★★
()
Ответ на: комментарий от alysnix

Повторяю, этот вопрос решается правильным проектированием софта. То, что ты называешь «контекстом кода», не должно выходить за пределы функции-обработчика события, и это должно быть небольшое количество её локальных переменных. А настоящий контекст должен храниться глобально в сети из динамически выделенных структур, и переключать там ничего не нужно.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Повторяю, этот вопрос решается правильным проектированием софта.

ну спроектируй правильно ide в котором и контент рисуется плавно, и комплятор код компилит одновременно(естессно в одном треде, чтобы это все). короче чтобы гуй не застревал, пока компилятор в том же треде генерит.

alysnix ★★★
()
Ответ на: комментарий от firkax

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

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

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

@уровни абстракии@ неспа!

в процессе с гуи гонка на ресурсах

в 2ух ядрах гонка между 2мя - костыльно решаема с успехом

3 и больше оказывается более другое универсальное решение (классическая(что бы это ни было) распределённости и конус Миньковского(ТМ))

компьютерфил утюбный

ваще 3ёх мерие не случайно

qulinxao3 ★☆
()
Ответ на: комментарий от alysnix

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

firkax ★★★★★
()
Ответ на: комментарий от firkax

Компилятор это отдельный процесс вообще, причём тут он?

отдельный процесс не рассматриваем, поскольку ты ж сам сказал, что без всех этих процессов можно обойтись неким «правильным проектированием». вот и будет считать, что их нет.

пусть компилятор это просто вызываемая некая функция, в том же самом треде.

и теперь покажи как это «правильное проектирование» работает.

alysnix ★★★
()
Ответ на: комментарий от firkax

не ставь мне свои блевики, фиркакс. все твое знание о «правильном проектировании» напоролось на невозможность нарисовать гуй без фризов, для простейших приложений.

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

и все ему говорят тогда - «чувак. сделай свой тред и работай в нем. а главный тред будет заниматься отрисовкой. и общайся между ними асинхронно.»

но никто и никогда не говорит - «чувак! просто разбей свой код на хуки, которые будет вызывать main loop гуя»… потому, что такой совет немедля отобьет охоту программировать вообще. это такая извращенческая имитация мультитреда, что хуже не бывает.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от firkax

ты ничего не написал. ты заявил о «правильном проектировании без тредов», и обломился на первом же нубском вопросе по гую.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от Enthusiast

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

Тогда создается иллюзия, что весь ввод-вывод как бы синхронный, но на самом деле внутри он асинхронный, что заметно облегчает программирование (см. примечание внизу).

Именно так и реализовано в хаскеле. Плюс там можно все-таки некоторые потоки жестко привязать к системным, что нужно, например, для GUI. Однако по умолчанию потоки в хаскеле - обычные зеленые, без привязки.

Вообще, я с голангом почти не знаком, но могу предположить, что они это поведение скопировали (??), как скопировали и то, что хаскель создает нативные двоичные файлы со встроенным сборщиком мусора. Впрочем, почти все языки ФП создают такие бинарники.

Кстати, для исходной задачи подошел бы и голанг.

Примечание.

Ну, и чтобы жизнь медом не казалась, придумали для зеленых потоков еще и асинхронные исключения… Не знаю, какую аналогию тут провести. В общем, жизнь хаскелистам они портят. Я бы такие исключения не вводил. Ограничился бы обычными, которые обычно подразумеваются в других языках.

anonymous
()
Ответ на: комментарий от alysnix

мультитред не имеет отношения к числу ядер. он имеет отношение к активностям, происходящим паралельно(истинно или псевдо) со своим контекстом исполнения.

По факту нет никакой разницы на одном ядре запускать один поток или несколько. Производительность будет одинаковая. Многопоточный софт начал развиваться вместе с многоядреными процессорами.

Skullnet ★★★★☆
()
Последнее исправление: Skullnet (всего исправлений: 1)
Ответ на: комментарий от alysnix

Асинхронная обработка tcp запросов (комментарий)

Читаем:

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

Я нигде не предлагал делать event loop для числодробилок, и не надо мне это приписывать. А вот для тех задач, где сама суть работы в обработке запросов без собственной значимой нагрузки проца (как у автора) - нужно именно оно.

firkax ★★★★★
()
Ответ на: комментарий от firkax

А вот для тех задач, где сама суть работы в обработке запросов без собственной значимой нагрузки проца (как у автора) - нужно именно оно.

обработка запроса на компиляцию файла - целиком укладывается в ваше определение. ну или сложный поиск в базе по запросу от пользователя или кода.

то есть понятие «обработка запроса» ничего тут не раскрывает. для сложных, долгих «обработок запросов» - ваша схема не работает.

ваша схема работает для примитивной фигни, и чем фигня сложней, тем сложней реализовать схему коротких неблокирующих запросов.

а треды работают ВСЕГДА. хоть для сложных, хоть для простых запросов, хоть для блокирующей, хоть неблокирующей обработки.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)