Как стать автором
Обновить

Комментарии 130

Очень познавательно, спасибо!
Видно, что какого-то эталонного решения пока не найдено, но поиск идёт, и это хорошо
Поиск уже давно прошёл стороной тех кто пишет подобные статьи :)
советую лучше прочитать Programming paradigms for dummies и перестать искать какое-то «эталонное решение», тк это ни к чему не приведёт.
В нашем мире, я так полагаю, вообще не существует эталонных решений, и любое решение заточено под свою область применения (поэтому, кстати, изучая любое решение нужно обязательно изучать и контекст его применения, и предпосылки к его возникновению).

Что лучше: километры или сантиметры? Для географа — километры. Для портного — сантиметры.

Что лучше: арбуз или кусок мяса? Посреди пустыни — арбуз. В сибири зимой — кусок мяса.

Слово «лучший» почему-то исторически считают само собой разумеющимся и не требующим пояснения.
Из-за чего у детей возникает когнитивный диссонанс, типа: — «Мой папа лучше!», — «Нет! Мой папа лучше! А ты получи в морду!».

Я бы предложил что-то типа: «лучший для X в условиях Y — дающий X наибольшую выгоду в условиях Y». А «выгоду» каждый сам уже может определить для себя.

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

Смысл всех этих сравнительных таблиц — предоставить человеку удобный инструмент определения наилучшего для него решения в текущий момент времени.
Чтобы это было доступно каждому, и чтобы это можно было сделать быстро.
Отличный ответ! Надеюсь, это хоть немного заткнет холиварщиков, вечно пытающихся обосновать «лучшесть» какого-либо языка или платформы.
Извините, но хорошие холиваршики обычно пытаются обосновать не «лучшесть», а «практическую приемлемость и выигрышность той или иной платформы для того или иного набора факторов». Если вы перестали холиварить, значит ваше развитие закончилось <=> смерть программиста внутри.
Спасибо, очень показательный комментарий! Плюсанул.
p.s. «Кто в юности не был революционером — лишен сердца, а кто потом не стал консерватором — лишен мозгов.»
У. Черчилль
Мало кто об этом сейчас помнит, но у такого непопулярного языка программирования, как Tcl, практически с самого рождения событийная модель не только присутствовала, но и всячески поощрялась. Более того, применительно к вебу существуют такие штуки как древний tclhttpd, а также значительно более современный Wub, в котором всё завязано на асинхронный ввод-вывод (хотя, например, потоки поддерживаются, но практически не нужны). А если вспомнить про наличие в Tcl coroutines, всё становится ещё шоколаднее…
>порядка 10 000 одновременно работающих потоков, которые постоянно что-то вводят/выводят
Статья доставила :)
ага ) меня тоже порадовало
Причём замечательно выводят :-)
Великолепно. Очень приятно было почитать.
Если хотите, можете добавить что под Windows аналогом epoll является технология «Completion Ports».
Completion Ports не является аналогом epoll
Completion Ports является аналогом epoll :).
Я писал приложения, использующие iocp и epoll, а не обёртки вроде asio. А вы? :)
Мне прям интересно, вы не любите признавать ошибки или правда верите в то что утверждаете?
Я писал на Completion Ports сервер, способный держать миллион входящих подключений. А вы сейчас будете угрюмо троллить нюансы реализации и пытаться по-своему трактовать термин аналог. Можете себя не утруждать — API у них, безусловно, разное. Но и то, и другое решает одну и ту же задачу — это асинхронная эволюция select() способная работать с тысячами подключений.
а почему для обслуживания миллиона входящих подключений с помощью iocp вам приходилось под каждый сокет выделять буфера для чтения? А вот мне с аналогичным epoll'ом для этого не потребовалось выделять под каждый полумёртвый сокет этот кусочек памяти. Возможно ваш аналогичный iocp имеет проблемы с масштабируемостью? )
Потому что аналог — это не то же самое что точная копия. Я не знаю, какие причины побудили архитекторов Windows аллоцировать память при создании запроса WSARecv(), но могу предположить что это связано с оптимизацией по скорости работы. Несколько лишних гигабайт памяти сейчас ничего не стоят.

ладно, можете считать его аналогом :) Но никогда не говорите об этом тем кто писал кроссплатформеные приложения с использованием iocp и epoll, это очень больная тема для них )
Что, тяжко с Completion Ports после epoll? Ну вы не один такой, мы с ним тоже <вырезано цензурой> изрядно :). Неудобный интерфейс, куча недокументированного поведения. Но это как бы WinAPI, от него ничего другого и не ожидается. Но по функциональности это аналоги — решают одни и те же задачи похожим образом.
НЛО прилетело и опубликовало эту надпись здесь
А вот win2k8- верит :)
НЛО прилетело и опубликовало эту надпись здесь
Простите что влезаю, но по-моему, они решают одну и ту же задачу — быстрое и масштабируемое (с количеством сокетов в отличие от select/poll) уведомление приложения о сетевых событиях. В чем же не аналогичность?
Наверно потомучто iocp был создан для рисования прогресс баров во время копирования файлов :)
Вы подменяете IO Completion Ports и Overlapped IO. IOCP в Windows много где применяется. И высоконагруженные сетевые сервера — это одно из основнаых применений, см. MSDN.
И еще можно добавить, что для Node.JS разработана новая библиотека libuv, которая является единым интерфейсом для libev и IOCP.
Статья написана доступным языком, достойным Википедии (хотя, там уже есть) и снабжена ссылками. Это очень хорошо.
По сравнению с аналогичной отличной статьёй о том же самом, но в применении к nodeJS, из ссылок и фактов почёрпнуты дополнительные данные, спасибо.

… Этот шаблон «reacror» известен давным-давно — микроконтроллеры, не имеющие своей развитой системы событий, вовсю его используют, опрашивая порты и реагируя на них в бесконечном цикле (или как вариант, в одиночном цикле обхода после события таймера). Наконец-то он получил своё имя :).
Любой веб сервер функционирует не совсем так как Вы описали. У него есть уже пул предсозданных тредов (рабочих лошадок). Любой запрос перенаправляется для выполнения свободному треду. По окончании тред возвращается обратно в пул.
Но даже при 10000 тредах большинство из них находится в состоянии ожидания. Проблема переключения контекстов отчасти решается при помощи Green Threads, когда N виртуальных тредов мэпятся в M нативных (M
… (обрезали)… (M «много меньше» N). Планировщик позволяет эффективно распределить очередь задач для каждого треда.

Переключение контекстов и большое количество тредов — это далеко не основная причина выбора асинхронной модели. Более того, в асинхронной архитектуре будет больше переключений контекста, нежели в синхронной. Основная задача — это отказ от разделяемых состояний и соответственно исключение транзакций и блокировок общих ресурсов из работы системы. Именно там теряется производительность, особенно если какой-то ресурс не очень быстрый. Вместо одной долгой транзакции, связывающей и блокирующей одновременно множество компонентов на все время выполнения, каждый компонент работает независимо, а связь между ними осуществляется асинхронно. Управляет всем этим асинхронный процесс или конечный автомат (что по-сути одно и то же).

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

Как плата за масштабируемость — это потеря целостности состояния всей системы в любой момент времени. Поэтому задача должна быть сначала адаптирована, чтобы целостность не была критична. В самом деле, не критично, если на френдленте пост появится на пару секунд позже, нежели в блоге афтора.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за замечание.
Я действительно не задумывался о различиях «асинхронного» и «неблокирующего» ввода/вывода.
Посмотрел здесь: stackoverflow.com/questions/2625493/asynchronous-vs-non-blocking
Пишут, вообще, как я понял, что неблокирующий — это сразу ошибку выкидывать, а асинхронный — это «асинхронный».

Можете мне покидать ссылок на эту тему?
Если я смогу почитать и понять то, о чём Вы написали — я поправлю статью соответствующим образом.
НЛО прилетело и опубликовало эту надпись здесь
Ох, наконец, вкурил.
Сейчас поправлю статью.
Получается, нужно уточнить, что реактор у нас синхронный (если нет данных — идёт дальше, а на следующем цикле снова спросит).
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я тоже доволен, что не зря писал — провёл небольшую генеральную уборку в своих мозгах)
Я когда-то писал на C++ сервак для баннербанка, он, конечно же был асинхронным и работал данные через неблокирующий IO. Это позволило показывать примерно 1000 запросов в секунду на машине класса P3.

Но я по прежнему не догоняю, почему нужны коллбэки. То есть можно, да, но зачем? У меня была структура данных — и FSM на каждый запрос, и их общий массив, по которому я проходился после poll (тогда еще еpoll не внедрили)

Я делал так — читал из сокетов асинхронно до конца HTTP запроса, распаршенный опрос кидал в пул потоков-обработчиков, потом обработчик формировал строку ответа и она опять асинхронно отдавалась обратно.

Но сам обработчик — формировалка строки — был абсолютно синхронный. Я сразу генерил HTML код. Никак не пойму зачем нужно такое усложнение, как формировать ответ по коллбэкам. Наверное, это круто для каких-то comet вещей, но в общем случае, даже для highload… Достаточно что сама работа с сокетами будет асинхронной.
Да, черт, путаница слов. Короче был non-blocking io.
Это нужно например если при обработке так же встречаются какие-то тяжелые операции, например запросы к БД. В остальных же случаях — действительно только лишняя морока при отладке.
То есть, применительно к вебу, это целесообразно, когда время обработки каждого запроса таково, что если просто параллелить генерацию ответов на фиксированное количество тредов — то оно заткнется в этой самой генерации на блокирующий IO с SQL сервером? То есть мы можем еще добавить неблокирующий IO с SQL cервером, и коллбэки вешаются на события внутри генерации ответов?

Я когда-то ( в 2002 ) понял что если я буду в баннербанке дергать SQL, то все сдохнет намертво, поэтому мы делали так- раз в пол часа на основе БД генирились xml-ные планы показов, которые, после всасывания их в отдельном треде просто становились гигантскими деревьями/хэшами в памяти сервера и вся работа уже шла на основании их. Ну там были нюансы по поводу памяти/mmap, но это уже детали.

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

А есть примеры масштаба сайтов, когда такая асинхронная генерация ответов была бы разумным применением, а не джастофаном?
НЛО прилетело и опубликовало эту надпись здесь
агрегация данных.
>Но я по прежнему не догоняю, почему нужны коллбэки.
вы не один с этими страданиями :)
обслуживать сокеты в любом случае удобнее с помощью fsm'ки,
а обрабатывать запросы можно по-разному, зависит от того какие правила игры мы установим на обработку этих запросов.
просто оч часто люди пытаются протолкнуть одно решение, которое им показалось чертовски гениальным во все места, даже где оно может быть совсем не пригодным.
я вам расскажу страшную тайну: коллбечная модель — это не просто шаг назад. Это сели на мотоцикл и быстро-быстро поехали обратно в лохматые 70-е.

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

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

НЛО прилетело и опубликовало эту надпись здесь
Здравствуйте.
А Вы не Максим Лапшин, случайно?
Ваши комментарии сейчас добавлю к статье.
Можете ещё покритиковать конструктивно?
А как Erlang вписывается в эту «новую модель»?
НЛО прилетело и опубликовало эту надпись здесь
Добавил Эрланг в статью.
>Например, подобный «спагетти-код» является бичом программирования на Node.js’е (коллбек на коллбеке, коллбеком погоняет). Об этом знают, и с этим пытаются бороться.

Настоящим бичом node.js являются «эхсперты» которые его ежедневно уже второй год «представляют», находят «плюсы и минусы», что-то предрекают и жалуются на «спагетти-код», не написав истрочки реального приложения с ним.
За год работы у меня не возникало мест в коде где требовалось бы больше трех вложений. Если в одной функции приходится делать по несколько запросов к базе, файловой системе и внешнему серверу (где собственно нужна асинхронность), то есть смысл пересмотреть архитектуру. Еще более простое решение — не использовать анонимные функции.

Кстати в Windows 8 такая же асинхронность и такой же JavaScript. Надеюсь критики ноды не оставят этот момент без внимания.
В принципе, наверное, Ваша правда в том, что кривизна кода зависит в первую очередь от самих разработчиков, так что поправлю статью в этом месте.
Может быть у Вас и чистый код.
Но в среднем, будем считать, что склонность к лапше присутствует.
> Еще более простое решение — не использовать анонимные функции.

О как это интересно. Т.е., если взять сложную лапшу с кучей ассинхронных вызовов, которые не параллеляться, и разрезать её на несколько неанонимных фукнций, как раз по этим вызовам, то это уже не спагетти-код?
Именно.
Забавно :) Что вы понимаете под определением «спагетти-код».
Насколько я понял, люди такое уже пробовали делать, и итог их не очень впечатлил.
gist.github.com/839545
если на этот код внимательно посмотреть (и даже если не очень внимательно), то видно что f, bcd, cd все выглядят по сути одинаково. а это что означает? это означает, что можно абстрагировать это логику отдельно и переиспользовать:

function seq(funcs, cb) {
var i = 0;
function continue (err) {
if (err || i == funcs.length) cb(err);
funcs[i++](continue);
}
continue();
}

function f(cb) {
seq([a, b, c, d], cb);
}


не сказать, что прекрасно (я сам не поклонник все-через-callback программирования), но вполне нормально, особенно если эта абстракция в рамках проекта используется единообразно.
согласен с Вами в том, что в подобных случаях стоит попробовать найти какой-то общий шаблон построения кода приложения, и выделить его в небольшую библиотеку, берущую часть «некрасивостей» на себя.
Это как Биг-мак: если все месте, то это плохая еда, а если булочку отдельно. салатик отдельно. помидорку отдельно. котлетку отдельно, то это уже здоровый комплексный обед.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
весьма хорошее описание. буду сюда ссылать всех кто мня терроризирует подобными вопросами.
НЛО прилетело и опубликовало эту надпись здесь
спасибо, вы правы.
я работаю над этим.
Может я что-то не так понял из текста но проблема в многопоточных системах в основном не в дороговизне контекст свитча.
Может быть это я что-то не понял, а Вы поняли, поэтому напишите, пожалуйста, подробнее.
Здесь стоит уточнить, что такое контекст потока управления.
В классическом юниксе, например, это контекст процесса со всеми прибамбасами, со своим адресным пространством. Если 1000 форкнутых Апачей реально пугают людей, то 1000 Java threads на аналогичной задаче как-то не особо напрягают. Какой-нибудь Erlang диспатчит на том же железе потоки вообще тучами и не чихает.

Прелесть nodejs'а заключается в том, что он довольно элегантно прячет от программера всю маету с асинхронностью реального мира, предоставляю одну единственную нить, где все замечательно синхронизовано. Но взамен требует нарезать «юнитс оф ворк» весьма замысловатым способом.
Насколько я читал, потоки в Яве — системные:
en.wikipedia.org/wiki/Green_threads#Green_threads_in_the_Java_virtual_machine

Вообще, я не системный программист, поэтому не разбираюсь в тонкостях различий.
Вроде как я там написал (из википедии), что контекст — это память, связанная с потоком, его блокировки, «всякие прибамбасы».
Если это неправильно, Вы можете переформулировать моё описание, и я поправлю его в статье.
Что-то я не понял, Вы пишите про «системные потоки» в Java, но даете ссылку на green threads, где упомянута реализация 1.1 от 1997 года. Короче говоря, это было так давно, что даже я уже не помню.

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

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

Тогда, получается, контекст содержит: регистры, стек (тот самый сегмент стека, который ss, если я правильно понял), но не адресное пространство.

Я вообще в понятиях этих не силён, но, насколько я понимаю, «адресное пространство» — это по какому адресу какая переменная лежит в оперативке.
Все переменные, к которым есть доступ из потока (в том числе локальные) составляют это самое «адресное пространство».

Правильно ли я это понял?
Мне вот нужен был обработчик коннектов к сокету. Принимает подключение от флеш-клиентов на одном сокете, принимает от них команды, передаёт на другой и обратно. Одновременное кол-во клиентов — не более десяти. Не тысяч, просто десяти. Сначала возился с похапе (cli), получилось всё реализовать (даже с форками) но коряво (понимаю, php для этого, мягко говоря, не очень). Вчера вечером вспомнил что слышал что-то о ноде, попробовал. К утру уже всё заработало в лучшем виде (приём подключений, авторизация в БД и т.д.) Однако опытные товарищи закритиковали, мол нода ацтой и всё такое прочее. А мне SMP-то и не нужен, дикая хайлод не нужен. Поток команд тоненький… Оправдан ли в моём конкретном случае выбор ноды? или всё ж не стоит?
Вы пишете прокси, получается? Думаю, для таких вещей оправдан. Вот человек писал тоже на событийном цикле подобную вещь: habrahabr.ru/blogs/ruby/126231/
Ну, в принципе, это можно назвать проксёй. Но не в том смысле, в каком обычно понимается. Это одна из частей системы управления вот этими красавцами
НЛО прилетело и опубликовало эту надпись здесь
Вообще там от сервера до бота намного меньше чем 50мс, а боты очень шустрые. Но с ретрансляцией команд справлялся даже php. Но php с костылями, а в ноде всё нативно, так что намного проще и стройнее получилось.
А вот на отдаче видео с робота — однозначно Erlyvideo, как раз его ниша
прикольно
НЛО прилетело и опубликовало эту надпись здесь
Уточнаяющий вопрос:

"«Асинхронная событийная модель» хорошо подойдёт там, где много-много пользователей одновременно запрашивают какие-нибудь «легковесные» куски данных"

У нас неблокирующий кусок кода — тот же твитер, достаем из мемкеша например последние твиты Васи. В чем разница по существу между nginx и node.js? Более того, если я верно понимаю, node.js работает только на 1 ядре (если конечно у нас 1 копия запущена только). Те nginx саму обработку запроса может сам раскидывать по ядрам (точнее это делает система тк каждый вокер работает на своем ядре как бы), а node.js работает на одном ядре.

В каком моменте будет экономия?
Для статики конечно же нужен только nginx.
А если система динамичная? Скажем, тыща датчиков температуры на космическом корабле, где особо не покешируешь, и система, которая это всё «аггрегирует», то там я бы предложил писать на Node.js.

Node.js не поддерживает на текущее время многоядрёности. Говорят, это называется «SMP» (Symmetric Multiprocessing): ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%B5_%D0%BC%D1%83%D0%BB%D1%8C%D1%82%D0%B8%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5
Когда начнёт поддерживать SMP — тогда будет использовать мощь всех ядер.

Сейчас для Node.js есть фреймворк Express, у которого есть плагин для хранения сессий в Redis'е.
Если сделать так, то, думаю, можно будет запустить Node'ов по количеству процессоров, и всё будет работать как нужно.

Это моё мнение.
космический корабль на JS это круто :)

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

Как я понимаю конкретно в приеме соединений у node.js нет преимуществ перед тем же nginx, который так же юзает epoll, верно?

Будет ли событийная модель работать для мемкеш запросов? Те я представляю так: пришли 10 запросов одновременно. nginx распихал их по вокерам, каждый вокер открывает соединение с удаленным серваком и хочет получить инфу из мемкеш. Те 10 запросов одновременно висят и по идее система периодически переключается между каждым из 10 процессов и каждый из них делает проверку типа «а что ж у нас там пришло в сокет от удаленного сервака».

node.js: не знаю как на самом деле конечно, теоретически он, учитывая что все эти 10 запросов идут фактически внутри одного процесса, может их как бы объеинить в пул такой и по методу epoll ждет когда появится данные от удаленного сервака и в сязи с этим у системы нет необходимости делать context switch между 10 процессами и за этот счет идет определенная экономия.

так ли оно на самом деле?
Я для себя пока решил, что выигрышь ноды относительно систем с синхронным вводом/выводом состоит именно в том, что она однонитевая, и у неё свой простой «планировщик» — пройтись последовательно по всем дескрипторам и проверить на них наличие новых данных.
Если nginx использует асинхронный ввод/вывод, то, думаю, он со своими несколькими воркерами отлично справится, мб даже лучше, чем Node.js — ведь на асинхронном вводе/выводе нет постоянных блокировок.
Но я не знаком с устройством nginx'а, поэтому тут могу только гадать.
Кстати, всякие вконтакты и твиттеры, насколько я понимаю, не парятся, и просто всё кешируют, и у них всё работает нормально.

Тогда, наверное, можно сбавить тон статьи.
НЛО прилетело и опубликовало эту надпись здесь
да, но вопрос не совсем в этом. То что nginx умеет эффективно отдавать статику я знаю, но тут то не статика.
Внёс в статью оговорки о том, что наша система не кешируется, и добавил в начале абзац «о кешировании».
На кой черт все эти ссылки? И более того, на кой черт эти странные локализации терминов?
Если читатель этого не знает — ему не нужна ваша статья, потому что ему не интересно. А если знает — то ему не нужны эти ссылки(Правда, скорее всего не нужна статья, потому что это проходят на первом курсе университета, кроме, разве что, деталей epoll.).
Если Вам не интерестно, то что Вы здесь делаете?
Во-первых, я не говорил, что мне не интересно.
Во-вторых, я не могу узнать, интересно мне или нет до прочтения статьи.
В-третьих, вам то какое дело?
Ну, я не проходил ничего из этого на первом курсе своего университета, потому что учился на физфаке.
Кто-то вообще не имел возможности учиться в универе.
Вы считаете этих людей недостойными приобщиться к касте программистов?
Кстати, Вы не учились, случайно, году этак в 2005-ом на мехмате МГУ?
Я просто помню там с местного форума, парень был один, тоже с ником «Stroncium», и почему-то меня троллил по кд.
Ну, подразумевалось проходят в другом смысле. Мне тоже в университете ничего этого не рассказывали.

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

Нет, не учился в МГУ. Кстати, наверное, вы что-то путаете с его ником, возможно это был «Strontium», потому что исходя из результатов поисковых запросов, кроме одной девушки и меня до последней пары лет stroncium'ов в рунете больше не было, а во всем интернете была только пара малоактивных людей.
А почему вы вообще решили написать статью по довольно обширным темам, если не разбираетесь в:
>Что такое «асинхронная событийная модель»
— парадигмах программирования (прочитайте Concepts, Techniques, and Models of Computer Programming)

>Технические подробности о модели сервера приложений
— системное программирование (прочитайте The Linux Programming Interface: A Linux and UNIX System Programming Handbook)

>По мере того, как растёт или память, связанная с потоком, или количество потоков, начинает не хватать «кеша второго уровня» (L2) у процессора, который не может уже вместить в себе все эти «контексты» потоков, и эти «контексты» приходится начинать писать в обычную оперативную память (и потом доставать их оттуда)
— память (прочитайте What every programmer should know about memory)

> Новый «тренд» был вызван тем, что серверы с традиционной многопоточной архитектурой перестали справляться с возрастающей нагрузкой в «веб-два-нольном» интернете.
— архитектура существующих highload приложений (ну тут чего-то отдельного вроде не найти, но всё легко гуглится)

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

Ну и так для общего развития:
Проблема с трэдами ОС больше в том что нужно выделять много памяти под стэк (гугловцы реализовали в последнем гцц интересную штуку под названием split stacks, когда он динамически может расширяться, но для этого все либы и програму надо компилировать с этим флагом), а для обслуживания сокетов с блокирующим IO их не используют тупо потомучто это оч неудобно.
Похожая проблема под винду с iocp — это когда на каждое полуживое соединение надо выделять бессмысленые буфера для чтения, из-за чего система хуже масштабируется. В node.js скорее всего сделали всё как proactor pattern и теперь при использовании линуксов получают такой же недостаток как и под виндой, зато теперь они кроссплатформеные(не смотрел libuv — так что это моя догадка, ибо так почти все делают)

Разделяйте сущности в системе, то что подходит для работы с сокетам, возможно будет плохо работать с обработкой запросов от сокетов. Например сокеты у нас могут жить вечно и их поведение может быть довольно непредсказуемым, как например события о том что сокет закрылся итп, поэтому очень удобно всё это реализовывать с вашими любимыми колбэками. У запросов обычно ограничения по времени выполнения ~30сек и их поведение довольно предсказуемо, поэтому тут мы можем использовать простенькие аллокаторы памяти со сборщиками мусора, ну а модель, которую выбрать для реализации логики риквестов может быть совсем разной — это уже зависит от задач, но тупое использование колбэков не даст тот выигрыш, который нужен большинству сайтов — понижение лэйтэнси, а не увеличение rps.
Чтобы получить инвайт и годовую подписку на Bookmate.
Шутка.
На самом деле я сначала просто решил сам со всем этим разобраться, потом решил, что это будет полезно всем, а потом ещё выяснилось, что без помощи коллективного разума самому разобраться в этой теме будет очень сложно.
Собираюсь устранить все противоречия в этой статье, и сделать её неким подобием ФАКа для интересующихся.
В этом мне помогут все, кто явно укажет на противоречивые утверждения в статье, и предложит свою правильную замену этим утверждениям.
Пойду читать Ваши ссылки.
>Собираюсь устранить все противоречия в этой статье, и сделать её неким подобием ФАКа для интересующихся.
Тогда нужно начать с разделения взаимодействия с ОС и тем что будет построено в итоге.
То что блокирующий IO+куча ОС тредов будет неэффективным для обслуживания кучи сокетов при использовании примитивов ОС, ещё не значит что блокирующий IO нельзя сделать эффективным, спрятав всю эту низкоуровневую часть.
Поэтому я даже не знаю как тут можно привязать тему «асинхронной событийной модели» и почему она сейчас «в моде». Так как поверх низкоуровневых конструкций можно делать что угодно.
Например представьте такую выдуманую декларативно-императивную штуку вроде мэйкфайлов:
get_session:
  memcache.get('session')

get_user_info depends on get_session:
  db.get('user_info', session)

get_item_data:
  db.get('item_data')

generate_page depends on get_user_info, get_item_data:
  render_page(user_info, item_data)

и вот теперь чтобы сгенерировать страничку(generate_page), наш планировщик строит деревяшку зависимостей и начинает выполнять их, причём планировщик может одновременно получать данные о пользователе и данные о предмете, тк они не зависят друг от друга. Пример конечно тупой и надуманый, но надеюсь отлично демонстрирует что без всякой «асинхронной событийной модели» мы можем генерировать странички и даже получим выигрыш в понижении лэйтэнси, тк будем одновременно делать несколько запросов к базе.
Мы рассматриваем синхронный ввод/вывод.
Значит, эта выдуманная штука выполняет каждый таск (из тех, что через запятую) в своём потоке.
И, получается, каждый такой поток вызовет блокировку и «переключение контекста», и я не вижу здесь выигрыша по сравнению с обычным apache -> php -> db

Может быть я набил Вам уже оскомину этим словосочетанием, но как я понял, 10 000 потоков одновременно, которые блокируются, и вызывают планировщик, душат производительность именно «переключением контекста», которое является (видимо) слишком дорогостоящим, если вызывается, скажем, 10 000 раз в секунду.
Если же основной душитель — не «переключение контекста», то можете ли Вы мне объяснить, кто это?
НЛО прилетело и опубликовало эту надпись здесь
Я много писал на Яве, и знаю, что там есть слово synchronized. Если его не ставить — то, типа, хоть сколько тредов запусти, каждая из них будет видеть свою картину окружающего мира. И, как я понял, в таком случае не будет никаких блокировок и тразакций на общие ресурсы (никто ничего не обеспечивает, можно даже успеть записать пол-лонга, а другой считает пол-нового лонга и пол-старого).

Получается, как я понимаю, основной тормоз — это стек. nuit, вроде как, об этом написал (цитирую): «Проблема с трэдами ОС больше в том что нужно выделять много памяти под стэк». Все стеки не влезают в кеш процессора — и нужно каждый раз идти за ними в оперативную память.

Спросил у гугла, пишут, что по-умолчанию каждому потоку выделяется стек в 8Мб: adywicaksono.wordpress.com/2007/07/10/i-can-not-create-more-than-255-threads-on-linux-what-is-the-solutions/
Это ж ни в один процессорный кеш не войдёт…
Получается, при переключении контекста гоняются туда-сюда возможно безполезные 8Мб?
>Получается, как я понимаю, основной тормоз — это стек
тут не в тормозах дело, а в масштабируемости. Даже если будет под стэк выделяться 64кб, то для обслуживания 10к входящих соединений потребуется как минимум выделить 625мб виртуальной памяти.
Ну и tanenn правильно сказал, что с такой моделью очень большой удар по производительности будет в блокировках, тк скорее всего придётся часто обращаться к общим ресурсам.

>Получается, при переключении контекста гоняются туда-сюда возможно безполезные 8Мб?
кеш работает иначе, никто-ничего не гоняет при переключении контекста :) В «What every programmer should know about memory» всё отлично расписано.
Да, действительно, со стеком получается, что тормоза от его размеров не зависят.
Значит, если у нас 10 000 Апачевских воркеров, то сколько бы каждый из них памяти ни занял, не это вызывает тормозов.

Что тогда их может вызывать?

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

В таком случае не должно быть никаких «блокировок общих ресурсов»?
Или я себе не так представил весь этот процесс?
Я если честно даже не понимаю о каких тормозах речь.
Есть ли примеры когда какой-нибудь типичный сайт на node.js+postgresql выдавал больше rps'ов чем на примитивной связке nginx+uwsgi+python+postgresql?
Я не помню, откуда я это взял.
Сейчас пошёл гуглить, и нашёл какой-то замер: www.synchrosinteractive.com/blog/9-nodejs/22-nodejs-has-a-bright-future
Там говорят, что если в PHP-коде поставить sleep, то Node.js оказывается гораздо быстрее.

Здесь бенчмарки, и, видимо, при росте одновременных подключений, Апаче всё больше отстаёт от Node.js'а:
code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests
Хотя, вот этот бенчмарк, на который я ссылку дал, он никуда в базу не ходит, а просто отдаёт Hello World.
Получается, без блокировок в коде.
Значит, Node.js выигрывает имеенно на этапе работы с сокетами, а не в области самого кода?
Если моя мысль верна, то почему Node.js лучше работает со входящими подключениями, чем Апач?
Запощу ещё пару ссылок.
В каком-то смысле, они противоречат друг другу.
На stackoverflow кто-то спросил, почему однонитевые серверы быстрее Апача, и ему какой-то (с виду разбирающийся) человек тоже начал говорить про «context switch»'и
stackoverflow.com/questions/2583350/is-epoll-the-essential-reason-that-tornadowebor-nginx-is-so-fast

Здесь же человек проводит свои бенчмарки, и у него Реактор оказывается медленнее Апача, и он вообще критикует тех, кто считает системные потоки более медленными, чем Реактор с его событийным циклом:
kaishaku.org/twisted-vs-threads/

Какие-то «взаимоисключающие параграфы»…
Можно ли вообще в этом деле поставить точку, или это какой-то очередной «холивар»?
>Можно ли вообще в этом деле поставить точку, или это какой-то очередной «холивар»?
Сервера бывают разные:
Типичный веб-сервер (вроде nginx+uwsgi):
— обслуживаем клиентские сокеты используя конечный автомат+неблокирующий io+epoll
— исполняем запросы от клиентов в пуле полноценных ОС тредов
спокойно сможете создать сайт типа Stackoverflow, никаких проблем с нагрузкой не будет, надеюсь это достаточный хайлоад )

сервер для ММО игрушки:
— обслуживаем клиентские сокеты используя конечный автомат+неблокирующий io+epoll
— одним/нескольими тредами в бесконечном цикле каждые 10мс обновляем состояние игрового мира

Вариантов может быть очень много. Всё очень сильно зависит от задач.
Например попробуйте написать какой-нибудь прокси-сервер, используя треды — это будет чертовски неудобно. Так же и обработка обычных запросов при генерации страничек для интернет-магазина чертовски неудобна, когда всё превращается в кучу асинхронных ф-ций с колбэками. Или например мы живём внутри гугла и работаем с BigTable, тут для удобства мы придумываем что-нибудь вроде A New AppEngine Datastore API.
А в случае с нод.жс — автор этого проекта под сильным впечатлением от того что разобрался как работают всякие nginx'ы, решил что это «One True Way» и после нескольких неудачных попыток делать тоже самое на Си, вдруг родил связку v8+libev и выдал людям. В итоге вокруг скопилась куча мартышек, которые теперь сидят и давятся тем что им тут предоставили.
Там проскакивают толковые ребята, которые делают node-fibers итд, но как видим — такие вещи не проходят дальше. Да и вообще брать ущербный жаваскрипт и возиться с ним ещё и на серверах — эх, не жалеют себя люди :)
p.s. последний год пишу в основном на жаваскрипте :)
Поняяятно, значит всё дело в удобстве при решении конкретной задачи, а не в производительности.

Тогда что в статье напишем?
Что Node.js не быстрее обычного MPM пула в Апаче в плане обслуживания клиентов, и все те блоггеры, которые пишут, что «Node.js is blazing fast», официально пойдут лесом?
Ну смотрите, насколько помню первые примеры с нодом были с реализациями чатов и реалтайм аналитики. Если мы попытаемся как-то это реализовать с помощью (nginx+uwsgi+базы данных), то у нас получится решение, которое будет сильно уступать простенькому решению на ноде.
А если будем делать сайт типа Stackoverflow с архитектурой как у них (вертикальное масштабирование и вся нагрузка на бд), то тут мы практически не заметим особой разницы между нодом и nginx+uwsgi.
ок.
поправил статью, и убрал из неё все упоминания о быстроте событийной архитектуры относительно потоковой.
>Значит, эта выдуманная штука выполняет каждый таск (из тех, что через запятую) в своём потоке
поверх примитивов ОС можно реализовать свой собственный планировщик со своими потоками :)
shootout.alioth.debian.org/u64q/performance.php?test=threadring
НЛО прилетело и опубликовало эту надпись здесь
Предположим, ввод/вывод синхронен. И мы создали надстройку в виде «зелёных нитей» (поверх примитивов ОС). Вы утверждаете, можно реализовать свой планировщик, который будет гораздо лучше стандартного линуксового? Почему тогда в Линуксе его до сих пор нет?

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

Или Вы имели ввиду то, что для программиста он как бы синхронен, и поэтому мы будем считать его синхронным, и поэтому моя формулировка о том, что «если синхронен ввод/вывод и куча потоков, то тормозишь» не верна?
>Или Вы имели ввиду то, что для программиста он как бы синхронен, и поэтому мы будем считать его синхронным, и поэтому моя формулировка о том, что «если синхронен ввод/вывод и куча потоков, то тормозишь» не верна?
да :)
А по ссылке, которую Вы дали, я так понял, замеряют производительности разных «зелёных нитей» (и просто нитей) в разных языках?
И Haskell оказался чемпионом?
Я думал раньше, что Эрланг всех делает…
Да Haskell много где чемпион, вот только почему-то он не «в моде» :)
НЛО прилетело и опубликовало эту надпись здесь
Знаете ли Вы случайно, не решает ли NPTL этот недостаток?
У меня глупый вопрос:

Условно у нас база и сам скрипт на одном однопроцессорном одноядерном серваке. Никакие данные не прокешированы и тп.

В приведенном вами примере будет ли, и если будет то в чем, глобальная разница будет ли сервак выполнять запросы по получению user_info and item_data паралельно или последовательно?

Более того (теоретически) последовательное выполнение должно бысть быстрее тк системе не надо тратить ресурсы на context switch между этими потоками.

Задачи можно реализовать с помощью Python generators, никаких тяжёлых потоков, переключение между задачами оч быстрое
Так что возможен и такой исход:
П — приложение, БД — база данных, * — переключение контекста

П: выполняет get_user_info
П: выполняет get_item_data
*
БД: возвращает user_info
БД: возвращает item_data
*
П: рендерит страничку
> Новый «тренд» был вызван тем, что серверы с традиционной многопоточной архитектурой перестали справляться с возрастающей нагрузкой в «веб-два-нольном» интернете.
— архитектура существующих highload приложений (ну тут чего-то отдельного вроде не найти, но всё легко гуглится)

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

«Concepts, Techniques, and Models of Computer Programming» — это тысячестраничная жесть, которую мне не осилить, поэтому она отменяется.
citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.102.7366&rep=rep1&type=pdf

«The Linux Programming Interface: A Linux and UNIX System Programming Handbook».
uploading.com/files/get/8ae5bda1/
аааа, полторы тыщи страниц.
отменяется.
и ещё, мне сносит крышу от линуксовских названий (ls, umount, unmount, cd, chroot, chmod, ...) и названий низкоуровневых функций (mmap, ioctl, memcpy, strcpy, ...).
для меня это какой-то инопланетный язык.

«What every programmer should know about memory»
citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.91.957&rep=rep1&type=pdf
Вот это, похоже, что-то интересное. Спасибо, почитаю. Сто страниц, вполне нормально.
судя по всему мы ждем от вас еще одну статью на туже тему :)
>«Concepts, Techniques, and Models of Computer Programming» — это тысячестраничная жесть, которую мне не осилить, поэтому она отменяется.
эта книга очень полезна для всех программистов, просто чтобы понимать причины по которым многие языки такие какие они есть :) что для решения одной задачи удобно применять sql, для решения другой makefile итд. Что нет какой-то «асинхронной событийной модели», которая будет удобна в любых ситуациях. Поэтому у людей, знакомых с кучей различных языков, случается батхёрт, когда они видят весь этот голый колбэк-шит, с которым в большинстве случаев работают все нодовцы.

>«The Linux Programming Interface: A Linux and UNIX System Programming Handbook».
тут всё конечно не обязательно читать, но это отличный источник информации по темам, которые вдруг заинтересуют :)
Комменты не читай, сразу отвечай:
Не совсем верно говорить, что под стек линукс разом выделяет 8мб. 8мб — это предел размера стека и дополнение от гугла всего-лишь позволяет динамически его растягивать до большего размера. По факту в линуксе существует такой механизм как page fault. Т.е. первоначально под стек выделяется 1-2 страницы памяти (4-8кб). При очередном добавлении значения в стек, когда его границы переходят выделенные уже страницы срабатывает этот самый page fault (исключение уровня ос) и ос резервирует под стек еще одну страничку.
Видел как-то раз в каментах также, что данный механизм есть и в Винде тоже, и называется что-то типа «виртуальных страниц памяти».
То есть, получается, нити не пожирают сразу по 8 мб оперативы…
Я, когда смотрел лекцию Райана Даля (создателя Node.js'а), видел такую картинку:
thefoley.net/node/nginx-apache-memory.png
На цифры смотреть не стал, удовлетворился лишь общей тенденцией.
В принципе, если у него тут на графике при 4000 соединений 40 Мб оперативы естся, то как раз где-то по 8 Кб получается на нить.
Под стек линукс разом выделяет 8мб виртуальной памяти, дополнение от гугла позволяет делать такие же эффективные нити как в Го, расходуя минимум виртуальной памяти. То что физическая память не сразу выделяется под все странички — это уже другой вопрос, тут решается проблема с виртуальной памятью.
А в чём тогда смысл SplitStacks, если задача выделения малых количеств «настоящей» оперативы решается виртуальной памятью?
ну очевидно же :) они решают проблему с выделением малых количеств виртуальной памяти, она ведь тоже не резиновая и не берётся из воздуха.
Я думал, что её «два в шестьдесят четвёртой» битов, а оказывается, что около 128 Терабайтов
www.linuxquestions.org/questions/linux-hardware-18/what-is-max-memory-for-x64-pcs-721425/
Вроде как пока обычному приложению до таких объёмов как до Луны.
С другой стороны, все знают, как Билл Гейтс говорил в 1981-ом: «640Кб должно быть достаточно для каждого»
она же не магическим образом мапится на физическую, под таблицы нужно выделять физ. память.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории