Различия асинхронной и многопоточной архитектуры на примере Node.js и PHP

В последнее время наблюдается рост платформ, построенных на асинхронной архитектуре. На асинхронной модели построен самый быстрый в мире веб-сервер nginx. Активно развивается шустрый серверный javascript в лице Node.js. Чем же хороша эта архитектура? Чем она отличается от классической многопоточной системы? На эту тему было написано огромное множество статей, но полного понимания предмета они дали далеко не всем. Часто приходится наблюдать споры вокруг Node.js vs PHP+apache. Многие не понимают, почему некоторые вещи можно сделать на Node.js, но нельзя на PHP или наоборот — почему вполне правильный рабочий код на PHP сильно замедлится в Node.js, а то и повесит ее. В данной статье я бы хотел еще раз подробно объяснить разницу в их архитектуре. В качестве примеров двух систем, возьмем вебсервер с PHP и Node.js.

Многопоточная модель


Эта модель известна каждому. Наше приложение создает некоторое количество потоков (пул), передавая каждому из них задачу и данные для обработки. Задачи выполняются параллельно. Если потоки не имеют общих данных, то у нас не будет накладных расходов на синхронизацию, что делает работу достаточно быстрой. После завершения работы поток не убивается, а лежит в пуле, ожидая следующей задачи. Это убирает накладные расходы на создание и удаление потоков. Именно по такой системе и работает вебсервер с PHP. Каждый скрипт работает в своем потоке. Один поток обрабатывает один запрос. Потоков у нас достаточно большое количество, медленные запросы забирают поток надолго, быстрые — обрабатываются почти мгновенно, освобождая поток для другой работы. Это не позволяет медленным запросам забирать все процессорное время, заставляя подвисать быстрые запросы. Но у такой системы есть определенные ограничения. Может возникнуть ситуация, когда к нам придет большое количество медленных запросов, например работающих с БД или файловой системой. Такие запросы заберут себе все потоки, что сделает невозможным выполнение других запросов. Даже если запросу нужно всего 1мс на выполнение — он не будет вовремя обработан. Это можно решить увеличением числа потоков, чтобы они могли обработать достаточно большое количество медленных запросов. Но к сожалению потоки обрабатываются ОС, ей же выделяется и процессорное время. Поэтому чем больше потоков мы создаем, тем больше накладных расходов на их обработку и тем меньше процессорного времени выделяется каждому потоку. Ситуация усугубляется самим PHP — блокирующие операции работы с БД, файловой системой, вводом-выводом так же тратят процессорное время, не выполняя в этот момент никакой полезной работы. Тут мы поподробнее остановимся на особенностях блокирующих операций. Представим себе такую ситуацию: у нас имеется несколько потоков. Каждый обрабатывает запросы, состоящие из 1мс обработки самого запроса, 2мс на доступ и получение данных из БД и 1мс рендеринга полученных данных. Всего на каждый запрос мы тратим, таким образом, 4мс. При отправке запросов к БД поток начинает ожидать ответа. Пока данные не вернутся — поток никакой работы выполнять не будет. Это 2мс простоя на запрос в 4мс! Да, мы не можем сделать рендеринг страницы, не получив данные из базы. Мы обязаны ждать. Но ведь при этом мы получаем 50% простоя процессора! А сюда сверху можно накинуть дополнительные расходы ОС на выделение процессорного времени каждому потоку. И чем потоков больше — тем больше этих расходов. В итоге мы получаем довольно большое время простоя. Это время напрямую зависит от длительности запросов к БД и файловой системе. Лучшее решение, которое позволяет нам полностью загрузить процессор полезной работой это переход к архитектуре, использующей неблокирующие операции.

Асинхронная модель


Менее распространенная модель, нежели многопоточная, но имеющая не меньшие возможности. Асинхронная модель построена на очереди событий (event-loop). При возникновении некоторого события (пришел запрос, выполнилось считывание файла, пришел ответ от БД) оно помещается в конец очереди. Поток, который обрабатывает эту очередь, берет событие с начала очереди, и выполняет связанный с этим событием код. Пока очередь не пуста процессор будет занят работой. По такой схеме работает Node.js. У нас имеется единственный поток, обрабатывающий очередь событий (с модулем cluster — поток будет уже не один). Почти все операции неблокирующие. Блокирующие тоже имеются, но их использование крайне не рекомендуется. Далее вы поймете почему. Возьмем тот же пример с запросом 1+2+1мс: из очереди сообщений берется событие, связанное с приходом запроса. Мы обрабатываем запрос, тратим 1мс. Далее делается асинхронный неблокирующий запрос к базе данных и управление сразу же передается дальше. Мы можем взять из очереди следующее событие и выполнить его. К примеру мы возьмем еще 1 запрос, проведем обработку, пошлем запрос к БД, вернем управление и проделаем то же самое еще один раз. И тут приходит ответ БД на самый первый запрос. Событие, связанное с ним помещается в очередь. Если в очереди ничего не было — он сразу же выполнится, данные отрендерятся и отдадутся назад клиенту. Если в очереди что-то есть — придется подождать обработку других событий. Обычно скорость обработки одного запроса будет сравнима со скоростью обработки многопоточной системой и блокирующими операциями. В худшем случае — на ожидание обработки других событий потратится время, и запрос обработается медленнее. Но зато в тот момент, пока система с блокирующими операциями просто ждала бы 2мс ответа, система с неблокирующими операциями успела выполнить еще 2 части 2х других запросов! Каждый запрос может выполняться чуточку медленнее в целом, но в единицу времени мы можем обработать гораздо больше запросов. Общая производительность будет выше. Процессор всегда будет занят полезной работой. При этом на обработку очереди и переходе от события к событию тратится гораздо меньше времени, чем на переключение между потоками в многопоточной системе. Поэтому асинхронные системы с неблокирующими операциями должны иметь не больше потоков, чем количество ядер в системе. Node.js изначально вообще работал только в однопоточном режиме, и для полного использования процессора приходилось вручную поднимать несколько копий сервера и распределять нагрузку между ними, например, с помощью nginx. Сейчас для работы с несколькими ядрами появился модуль cluster (на момент написания статьи все еще имеющий статус experimental). Вот тут и проясняется ключевое отличие двух систем. Многопоточная система с блокирующими операциями имеет большое время простоя. Чрезмерное количество потоков может создать много накладных расходов, недостаточное же количество может привести к замедлению работы при большом количестве медленных запросов. Асинхронное приложение с неблокирующими операциями использует процессорное время эффективнее, но более сложно при проектировании. Особенно сильно это сказывается на утечках памяти — процесс Node.js может работать очень большое количество времени, и если программист не позаботится об очистке данных после обработки каждого запроса, мы получим утечку, что постепенно приведет к необходимости перезагрузки сервера. Также существует асинхронная архитектура с блокирующими операциями, но она гораздо менее выгодна, что можно будет увидеть далее на некоторых примерах. Выделим особенности, которые необходимо учитывать при разработке асинхронных приложений и разберем некоторые ошибки, возникающие у людей при попытке разобраться с особенностями асинхронной архитектуры.

Не используйте блокирующие операции. Никогда


Ну по крайней мере пока не поймете полностью архитектуру Node.js и не сможете аккуратно работать с блокирующими операциями.
При переходе с PHP на Node.js у некоторых людей может возникнуть желание писать код в таком же стиле, как и раньше. Действительно, если нам надо сперва считать файл, и только потом приступить к его обработке, то почему мы не можем написать следующий код:

var fs = require('fs');
var data = fs.readFileSync("img.png");
response.write(data);

Этот код правильный и вполне рабочий, но он использует блокирующую операцию. Это значит что до тех пор, пока файл не будет прочитан, очередь сообщений обрабатываться не будет и Node.js будет просто висеть, не совершая никакой работы. Это полностью убивает основную идею. В то время, пока файл читается, мы могли бы выполнять другую работу. Для этого мы используем следующую конструкцию:

var fs = require('fs');
fs.readFile("img.png", function(err, data){
	response.write(data);
});

Разберем ее подробнее: у нас происходит асинхронное чтение из файла, при вызове функции чтения управление сразу же передается дальше, Node.js обрабатывает другие запросы. Как только файл будет считан — вызывается анонимная функция, переданная в readFile вторым параметром. А точнее событие, связанное с ней, ложится в очередь и когда очередь доходит до нее — выполняется. Таким образом, мы не нарушаем последовательность действий: сперва считывается файл, потом обрабатывается. Но при этом мы не занимаем процессорное время ожиданием, а позволяем обрабатывать другие события в очереди. Это обстоятельство очень важно помнить, так как всего несколько неаккуратно вставленных синхронных операций могут сильно просадить производительность.
Используйте такой код, и вы безнадежно убьете event-loop:

var fs = require('fs');
var dataModified = false;
var myData;

fs.readFile("file.txt", function(err, data){
	dataModified = true;
	myData = data+" last read "+new Date();
});

while (true){
	if(dataModified)
		break;
}

response.write(myData);

Такой кусок кода будет занимать все процессорное время себе, не давая обработаться другим событиям. Пока проверка не завершится успешно, цикл будет повторяться, и никакой другой код не выполнится. Если вам необходимо дождаться какого-либо события то… используйте события!

var fs = require('fs');
var events = require('events');
var myData;
var eventEmitter = new events.EventEmitter();

fs.readFile("file.txt", function(err, data){
	myData = data+" last read "+new Date();
	eventEmitter.emit('dataModified', myData);
});

eventEmitter.on('dataModified', function(data){
	response.write(data);
});

Опять-таки, этот код выполнится только после выполнения определенного условия. Только эта проверка не запускается в цикле — код, выполняющий наше условие, с помощью функции emit вызывает событие, на которое мы вешаем обработчик. Объект events.EventEmitter отвечает за создание и обработку наших событий. eventEmitter.on отвечает за выполнение кода, при возникновении определенного события.
На этих примерах можно увидеть, как неосторожное использование блокирующего кода останавливает обработку очереди событий и соответственно стопорит работу всего Node.js. Для предотвращения таких ситуаций используйте асинхронный код, завязанный на событиях. Используйте асинхронные операции вместо синхронных, используйте асинхронные проверки наступления некоторого события.

Не используйте больших циклов для обработки данных. Используйте события


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

function incredibleGigantCycle(){
	cycleProcess();
	process.nextTick(incredibleGigantCycle);
}

Данный код выполнит тело цикла и создаст событие для следующей итерации. Никакой блокировки очереди событий в таком случае не будет.

Не создавайте больших операций, занимающих много процессорного времени


Иногда возникает потребность в обработке громадного объема данных или выполнение ресурсоемкого алгоритма (хотя писать злой матан на Node.js — не лучшая идея). Такая функция может занимать много процессорного времени (скажем, 500мс) и пока она не выполнится, много маленьких запросов будут простаивать в очереди. Что делать если такая функция все-таки есть и отказаться от нее мы никак не можем? В таком случае выходом может стать разбиение функции на несколько частей, которые будут вызываться поочередно как события. Эти события будут ложиться в конец очереди, тогда как события сначала могут пройти, не дожидаясь, пока наш увесистый алгоритм выполнится полностью. В вашем коде не должно быть больших последовательных кусков, не разбитых на отдельные события. Конечно есть еще выход в виде создания своего модуля на си, но это уже из другой оперы, не относящейся к вопросам асинхронного проектирования.

Внимательно следите за тем, какой тип функции вы используете


Читайте документацию, для того чтобы понять используете ли вы синхронную или асинхронную, блокирующую или неблокирующую функцию. В Node.js принято именовать синхронные функции с постфиксом Sync. В асинхронных функциях обработчик события по завершению функции обычно передается последним параметром и именуется callback. Если же вы используете асинхронную функцию там, где хотели использовать синхронную, у вас могут возникнуть ошибки.

var fs = require('fs');
fs.readFile("img.png", function(err, data){

});
response.write(data);

Разберем данный код. Начинается неблокирующее считывание файла асинхронным способом. Управление сразу же передается дальше — записывается ответ пользователю. Но при этом файл еще не успел считаться. Соответственно мы отдадим пустой ответ. Не забывайте, что при работе с асинхронными функциями, код для обработки результата функции всегда должен располагаться внутри callback-функции. Иначе результат работы непредсказуем.

Разберитесь с преимуществами асинхронных запросов


Иногда встречаются вопросы, почему приходится писать «спагетти-код» на Node.js, постоянно вкладывая друг в друга callback'и, когда на PHP все идет четко последовательно? Ведь алгоритм и там и там один и тот же.
Разберем следующий код:

 $user->getCountry()->getCurrency()->getCode()

и

user.getCountry(function(country){
	country.getCurrency(function(currency){
		console.log(currency.getCode())
	})
})


И там и там обработка пойдет только после завершения всех 3х запросов. Но тут имеется существенное различие: в PHP наши запросы к базе будут блокирующими. Сперва выполняется первый запрос, имеется некоторое время простоя процессора. Потом второй запрос с простоем, аналогично третий. При асинхронной неблокирующей архитектуре мы посылаем первый запрос, начинаем выполнение каких-либо других операций, связанных с другими событиями. Когда запрос от БД возвращается — обрабатываем его, формируем второй, отсылаем, продолжаем обработку других событий. В итоге и там и там получим 3 последовательно выполненных запроса. Но в случае с PHP у нас будет некоторый простой процессора, тогда как Node.js выполнит еще некоторое количество полезного кода, и может даже успеет обработать несколько запросов, не требующих обращения к БД.

Заключение


Такие особенности Node.js необходимо знать и понимать, иначе при переходе на него с PHP вы можете не только не улучшить производительность своего проекта, но и существенно ее ухудшить. Node.js это не только другой язык и другая платформа, это другой тип архитектуры. Если вы будете соблюдать все особенности асинхронной архитектуры — вы получите преимущества от Node.js. Если вы будете упорно продолжать писать свои программы так, как писали бы их на PHP — не ждите от Node.js ничего, кроме разочарования.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    0
    А никто не сравнивал асинхронную архитектуру на JS и PHP? Скорость разработки, сложность отладки и поддержки, скорость выполнения?
      +2
      Скорость разработки — ниже. Ибо просто особенности описанного процесса, и ни только ассинхронного, делают процесс разработки и отладки сложней. Но как только вы «войдете» в этот процесс, уже не будет сложностей. Я также хочу дополнить автора, в том, что Node перезагружается практически мгновенно. Если ее использовать скажем с Forever то для пользователя падение вашего сервера будет практически не заметен.

      А еще, важный момент, Node это не просто язык программирование, ни аналог PHP, или другого серверного языка. Это симбиоз HTTP сервера и серверного языка программирования. Вы сами будете писать на языке высокого уровня маршруты сайта. Вы будете сами определять какие ресурсы и как кешировать.
        +2
        Node это не просто язык программирование
        Вот именно, давайте изначально не будем сравнивать пчел с мёдом. ЯП vs. application-сервером и фреймворк.

        Вы сами будете писать на языке высокого уровня маршруты сайта. Вы будете сами определять какие ресурсы и как кешировать.
        Все это можно делать на любом языке программирования.
          –3
          Ни холивара ради, поясните мне, как вы в стандартной связке Linux + Apach + Php + MySQL проверите, является ли текущим запросом CSS или Изображение или еще что то, если в Php (5 — 5.4) такие запросы просто не приходят? Их Apache отдает самостоятельно. Иначе зачем он нужен вообще?

          Как в Php написать такое?

          app.is('an image', function(req) {
          return 0 == req.headers['content-type'].indexOf('image');
          });

          app.post('/image/upload', function(req, res, next) {
          if (req.is('an image')) {
          // выполняем определенные действия
          } else {
          next();
          }
          });

          Может Вы что то другое имели ввиду?
            0
            PHP CGI + Sockets (fsockopen). Это я в защиту Vbart.
            На любом языке программирования, который умеет работать с сетью, можно написать веб сервер.
              +4
              Знаете почему они не доходят до PHP? Потому что это эффективно, админ всё правильно настроил. Вы можете настроить apache/nginx — чтобы все запросы передавались на бэкенд, если вам это необходимо. Не вижу никаких проблем. То же самое касается и Node.js.
              • НЛО прилетело и опубликовало эту надпись здесь
                  –1
                  RoR — тоже симбиоз, угу. Только симбиоз там опциональный, как и всё остальное в рельсах.
              0
              Похоже вы меня не поняли. Я имел в виду сравнение асинхронной архитектуры на JS (nodejs) и асинхронной архитектуры на PHP (phpdaemon).
                +2
                Я проводил. По состоянию на более чем год назад — phpdaemon проигрывал очень сильно, причем по всем параметрам.
                Документация phpdaemon не соответствовала актуальному api настолько что примеры из неё отказывались запускаться, установка требовала танцев с бубном. По потреблению памяти / скорости работы у даймона тоже все было плохо. Цифры точно не помню, но было что-то вроде 300мб даймон против 7мб ноды, на простейшей задаче.
                Ну и чисто эстетитечески — phpdaemon оставляет ощущение костылей. Коими в общем и является.

            +7
            Иногда встречаются вопросы, почему приходится писать «спагетти-код» на Node.js, постоянно вкладывая друг в друга callback'и, когда на PHP все идет четко последовательно? Ведь алгоритм и там и там один и тот же.

            Ответ на этот вопрос предельно прост. Потому, что это JavaScript. Асинхронность к этому более-менее ортогональна. Существуют разные подходы к написанию асинхронного кода. И существуют мощные, развитые асинхронные фреймворки, удобные, простые, хорошо продуманные, как то Twisted или Tornado.

            Node.js != асинхронный подход
            Node.js == много пиара

            Используется JavaScript успешно в программировании интерфейсов в браузере — замечательно. Но неужели для серверного программирования сложно выучить более подходящий язык?
              –2
              Полностью поддерживаю позицию по вопросу node.js. Просто страшно становится, когда в дискуссии на тему производительности различных платформ, в качестве основного аргумента в сторону node,js используют понятие асинхронности…

              P. S. Свои истоки асинхронная модель берет от автоматов Мура/Мили. Или было еще что-то пораньше?
                +1
                В питоне, кстати, помимо именно фреймворков облегчающих работу с асинхронным IO есть целые рантаймы скрывающие асинхронное IO: gevent, eventlet, greenlet.
                Хотя им не все доверяют.
                  0
                  Разумеется, еще и uGreen — замечательная штука, сам пользовался.
                  +4
                  а что такого (не такого) в языке? Он разве как-то заточен? Язык это просто язык. Питон также не предназначался для веба никогда
                    +4
                    Наверное имеется в виду, что возможности других языков позволяют при асинхронной работе использовать как callback, которые тоже есть в Торнадо, так и например генераторы — www.tornadoweb.org/documentation/gen.html

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

                    В JS я не знаю способа выйти из функции и потом вернуться в нее в ту же точку
                      +1
                      Да, генераторы в торнадо очень хорошо позволяют причесать всю эту лапшу из callback'ов и сделать код понятнее
                      +1
                      Питон также не предназначался для веба никогда.
                      Это замечание справедливо и его можно ещё усилить: а вот JavaScript предназначался для веба изначально.
                        +1
                        А PHP для сервер-сайд веба :)
                          0
                          Мне кажется усиление получится сомнительное — JavaScript предназначался для веба, но не для server-side.
                      +2
                      Если кто-то пропустил, то можно прямо сейчас посмотреть асинхронный фреймворк на php, называющийся phpDaemon. Если запустить его в один воркер, то получится в точности однопоточный nodejs. Так что любители синтаксиса php могут опробовать асинхронность прямо сейчас, не переучиваясь на js.
                      Кстати говоря, phpDaemon также весьма шустр.
                      Кстати говоря (часть 2): асинхронный код на php очень сильно отличается от «обычного» php-кода. «Вроде функции те же, а чё написано не понятно».
                        +3
                        phpDaemon практически не имеет документации, пиара, и кроме того, порог вхождения из "обычного Js" в "Ноду" гораздо ниже, чем с "обычный php"->"phpDaemon". Я вот не тупой, а почти день убил на эту штуку чтобы посмотреть. Пока не пользую, оставил самописного демона, хотя асинхронные запросы к БД влекут :)

                        Поэтому посмотреть «прямо сейчас» проблематично =/
                          0
                          Там же есть примеры, да и по сути всё это не настолько сложно.
                            +2
                            Да уж поставить его куда как сложнее, чем поставить Node.js
                            phpDaemon сырой и без танцев его заведёт только хороший *nix админ. При том, что Node.js ставится за пару команд.
                            Это мой опыт. Документации практически нет, инструкции — пара штук, 1-2 года свежести. Кстати, проект живой, я подписан на рассылку. Поэтому описанные инструкции уже местами устарели. Хотя в мэйллисте автор проекта phpDaemon и другие помогают решать задачи, отвечает на вопросы.

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

                            А я сейчас на пороге выбора, и хотя phpDaemon у меня завёлся, я сейчас думаю, на чём начинать проект. И склоняюсь к Node, как к более удобному в технической поддержке. Да и люблю я JavaScript.

                              0
                              За что человека минусовали? Самому в скором времени нужен будет сервер для вебсокетов, а ещё не определился с платформой.
                                +1
                                Я в свое время остановился на tornado — рекомендую рассмотреть этот вариант.
                                –1
                                phpDaemon сырой и без танцев его заведёт только хороший *nix админ. При том, что Node.js ставится за пару команд.
                                Прибавлю, что в Windows можно начать «установку» Node.js вообще одним движением пальца, жмякнув мышóю по гиперссылке http://nodejs.org/dist/latest/node.exe (правду сказать, затем всё же понадобится ещё несколько жмяков мышóю для того, чтобы сохранить файл в такую папку, которая прописана в PATH).

                                (Здесь слово «установка» я взял в кавычки, потому что настолько простую последовательность действий и установкою-то назвать не хочется.)
                                  +1
                                  Лет 8 назад я писал однопоточный демон на PHP с помощью socket_select() (мне нужен был клиент для iChat с постоянным коннектом, реализующий несложную текстовую игру). Работало это достаточно шустро и надёжно, перегружал этого демона не чаще раза в неделю. Я и подумать не мог, что именно эта технология через несколько лет станет популярной и начнёт набирать обороты.

                                  Кстати написать нечто подобное с нуля может любой средний программер на PHP, это может быть полезно, например, для того, чтобы «на берегу» разобрать все тонкости работы с таким демоном. Как Вы понимаете, в nodejs по сути реализовано то же самое, просто больше библиотек и пафоса.

                                    –1
                                    Нет, этого я не понимаю и даже считаю некорректным, потому что многие важны частности не укладываются в эту абстракцию сути. (Например, я убеждён, что V8 как движок для запуска джаваскриптов куда быстрее, нежели Zend как движок для запуска PHP-скриптов.)
                                      0
                                      Несмотря на то, что V8 скорее всего по-любому шустрее Zend, о быстродействии тут речи не идёт. Суть в концепции построения приложения. В конце концов, если быстродействие будет так важно, мы напишем всё на С++.
                          +1
                          Самый быстрый в мире веб-сервер nginx.

                          Самый быстрый среди самых используемых. Существует масса самописных и форков, которые работают несколько быстрее.

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

                          Глупости. Явно быстрее было бы все хранить в ОП, использовать синхронную модель, не использовать функции для работы с файловой системой, сокетами, wait functions. Другими словами, все зависит от конкретной ситуации.
                            +1
                            P. S. Статья понравилась, считаю ее качественной по отношению к среднестатистической хабрастатьей.
                            +6
                            Различия асинхронной и многопоточной архитектур на примере самого ужасного из асинхронных фреймворков и продукте, к многопоточности не имеющего отношения.

                            А так да — статья норм.
                              +2
                              Не знаю Node.js. Но он, разве, действительно на столько плох, как вы говорите? В чем тогда заключается его текущая популярность?

                              Буду очень благодарен за ваш комментарий по этому вопросу.
                                +1
                                > Но он, разве, действительно на столько плох, как вы говорите?

                                Да. Потому что он — мешанина из коллбэков, без возможности утилизации SMP и всякими радостями JS типа плоского пространства имен и т.п.

                                > В чем тогда заключается его текущая популярность?

                                Hype. Причем одновременно в двух значениях — media circus и hype cycle
                                  0
                                  Спасибо за ответ! Предполагаю, что понял что вы хотели сказать.
                                    +2
                                    В nodejs еще есть хохмы типа раннего Rails:
                                    — течет память? Не проблема, перезапустим сервер, он же быстро перезапускается! (в последних версиях чуть-чуть исправили ситуацию)
                                    — нужн использовать два ядра? Запустим две VM. Нужно использовать 4 ядра? Запустим 4 VM.

                                    Отсутсвие утечек памяти? Автоматический SMP с учетом переключения контекстов ядра и т.п.? Нет, не слышали :)
                                      0
                                      Все ещё течет?
                                      Год назад текло, сейчас не заметно. Можно пример кода на котором это наблюдается? Или ссылку на статью. Не хотелосб бы наступить.
                                      По наблюдениям — основная проблема ноды на данный момент — сборщик мусора и плохая утилизация больших объемов памяти.
                                        0
                                        Пахнет взаимоисключающими параграфами. Это я к тому, что часто под течью имеют в виду именно несвоевременную сборку мусора :(
                                        0
                                        Слежу за багтрекером и вносимыми изменениями уже больше года — исправлений и подтверждённых багов по поводу мемликов было очень мало.

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

                                        Реальные мемлики действительно имеют место в сторонних модулях, как раз на днях напоролся в таком, казалось бы проверенном модуле, как socket.io.
                                          0
                                          > Чаще всего за мемлики принимают специфическую работу GC, который не успевает срабатывать при большой нагрузке, для борьбы с этим рекомендуется раз в несколько секунд дёргать GC руками через --expose-gc + gc()

                                          Боже какой ад. То есть это еще хуже, чем я предполагал
                                            0
                                            Толсто же.
                                            Переходить на ручную сборку мусора приходится только на запредельных нагрузках, на которых в альтернативных решениях скорее всего тоже полезут специфические проблемы. Только вот там возможность ручного управления может и отсутствовать.
                                            И если уж делать вызов gc руками — лучше делать это не раз в несколько секунд, а выбирая моменты снижения нагрузки, а если их нет — по фактической необходимости, отслеживая занятую память.
                                              +2
                                              Стандартное поведение GC — это и есть «выбирая моменты снижения нагрузки».
                                              По наблюдениям при большом кол-ве неубранного мусора нода помимо памяти начинает ещё и процессор хавать, что оставляет ещё меньше времени для GC и это растёт как снежный ком. А минусов в вызове gc() по интервалу никаких не нашёл, он в основном отрабатывает за 1 — 2 мс, что для обычных задач не критично.
                                                0
                                                Мне интересно, при каких таких «запредельных» нагрузках уже приходится ставить вызов GC вручную по интервалу
                                                  +2
                                                  http://habrahabr.ru/post/123154/

                                                  Вообще, при 20к конкурентных подключений уже можно ощутить заметный выйгрыш. Правда пока кроме синтетических тестов встречать такие ситуации не приходилось.
                                              –2
                                              Где вы тут ад нашли, 3 строки кода на всё про всё. Что вы ещё хотите от автоматической сборки мусора, если нужно более-менее управляемое поведение — делаем руками.
                                                +3
                                                Что я хочу от автоматической сборки мусора? Предсказуемость, быструю работу, отсутствие необходимости вообще ее запускать вручную
                                        +2
                                        У него просто Erlang головной мозга и банальное не умение понимать того, что он не знает. У Ерлангеров это профессиональная болезнь — ненавидеть ноду. Вероятно зависть…
                                          +2
                                          В ноде внезапно появился автоматический SMP? Или внятное управление иерархиями процессов? Или отслеживание ошибок в удаленных процессах? Или легковесная реализация иммутабельных данных (обязательная для внятной асинхронности)?
                                            +1
                                            Нет, там нет SMP и разработчики использующие node.js действительно считают, что запуск 4VM на 4-х ядерном сервере это нормально.

                                            > Или внятное управление иерархиями процессов?

                                            Взял специфичную для ерланга и его процессов фичу @ выставил как аргумент

                                            > Или легковесная реализация иммутабельных данных (обязательная для внятной асинхронности)?
                                            Взял философию ерланга (которая только одна единственная и она правильная, прямо как Аллах, слава ему) @ выставил как аргумент

                                            > В ноде внезапно появился автоматический SMP?

                                            Там можно циклом for запускать воркеров по кол-во ядер, считается?
                                              +4
                                              > там нет SMP и разработчики использующие node.js действительно считают, что запуск 4VM на 4-х ядерном сервере это нормально.

                                              То есть, я все правильно сказал.

                                              > Взял специфичную для ерланга и его процессов фичу @ выставил как аргумент

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

                                              > Взял философию ерланга

                                              Сорри. Имел в виду не иммутабельные, а неразделяемые (share nothing). Пусть будут мутабельными :)

                                              > Там можно циклом for запускать воркеров по кол-во ядер, считается?

                                              Нет.
                                                –1
                                                > Ничего специфичного в этом нет.

                                                Где кроме ерланга есть процессы такие как в ерланге?

                                                > Да хотя бы управлением четырьмя VM на 4-х ядрах

                                                Есть мастер который может останавливать, запускать новые в рантайме, а так же запускать их с произвольным кодом (по 2 VM, на 2х портах с разными задачами).

                                                  –5
                                                  Не кормите его. Хейтеры только этого и ждут.
                                                    +2
                                                    > Где кроме ерланга есть процессы такие как в ерланге?

                                                    Для появления иерархии процессов нужны процессы, как в Эрланге? Нуну

                                                    > Есть мастер который может останавливать, запускать новые в рантайме, а так же запускать их с произвольным кодом

                                                    Каким образом эти VM бджут общаться между собой?
                                                      –1
                                                      > Каким образом эти VM бджут общаться между собой?

                                                      Можно настроить pub/sub через redis или zeromq.

                                                      > Для появления иерархии процессов нужны процессы, как в Эрланге? Нуну

                                                      Для этого нужны процессы вообще. Или вы про другие процессы? Если просто надо иерархию всех вызовов и прочего — DTrace уже изобрели, добавить можно к много чему, не только node.js.
                                                        +2
                                                        > Можно настроить pub/sub через redis или zeromq.

                                                        То есть городить неведомо что на ровном месте

                                                        > Для этого нужны процессы вообще. Или вы про другие процессы?

                                                        Процессы/потоки/акторы — без разницы. Даже если архитектура строго на коллбэков, у тебя есть иерархия этих коллбэков или объектов, их содержащих.

                                                        Вон, ты не можешь даже SMP по-человечески использовать без того, чтобы не возводить вокруг ад из pub/sub и т.п.
                                                          +2
                                                          > То есть городить неведомо что на ровном месте

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

                                                          > Процессы/потоки/акторы — без разницы. Даже если архитектура строго на коллбэков, у тебя есть иерархия этих коллбэков или объектов, их содержащих.

                                                          DTrace. Я уже сказал. Зачем ты прогнозировал ту часть комментария? Я понимаю, что ерланг не умеет нормально со строками работать (они же не нужны), но ты то умеешь.

                                                          > Вон, ты не можешь даже SMP по-человечески использовать без того, чтобы не возводить вокруг ад из pub/sub и т.п.

                                                          взял философию erlang/otp @ попытался ее натянуть на node.js
                                                            +2
                                                            > Начнем с того, что VM выполняющие одни и те же функции не должны между собой общаться.

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

                                                            > DTrace. Я уже сказал. Зачем ты прогнозировал ту часть комментария?

                                                            Какое отношение DTrace имеет к организации иерархии процессов?

                                                            > взял философию erlang/otp @ попытался ее натянуть на node.js

                                                            Неверно понятая философия
                                                              –2
                                                              > 2. Кто сказал, что VM, выполняющие одинаковую работу, не могут между собой общаться?

                                                              2 треда в апаче которые обрабатывают запрос клиента общаются между собой?

                                                              > Какое отношение DTrace имеет к организации иерархии процессов?

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

                                                              > Неверно понятая философия

                                                              no shared state + message passing + erlang processes; Очевидно же ты просто берешь то, что есть в ерланге и выставляешь как плюс. Кроме как отсутствие SMP (настоящего) можешь, что-то сказать дельное? Почему у тебя анальная боль только от node.js? В Ruby тоже нет SMP, и тоже есть фрэимворк (и не один) который утилизирует паттерн «Реактор».
                                                                +2
                                                                > 2 треда в апаче которые обрабатывают запрос клиента общаются между собой?

                                                                VM'ы преднахначены только для одноразовой обработки запросов от пользователя?

                                                                > Внезапно, им можно «рисовать» иерархию процессов на сервере или внутри приложения.

                                                                Я говорил о «рисаовать»?

                                                                > no shared state + message passing + erlang processes; Очевидно же ты просто берешь то, что есть в ерланге и выставляешь как плюс.

                                                                Очевидно, я беру то, что является «way to go» в создании масштабируемых распределенных прилоэений и выставляю это, как «way to go». И к Erlang'у это имеет отношение только потому, что этот путь в нем уже реализован.

                                                                > Кроме как отсутствие SMP (настоящего) можешь, что-то сказать дельное?

                                                                Дельного даже в этой подветке я уже сказал много.
                                                                  0
                                                                  > VM'ы преднахначены только для одноразовой обработки запросов от пользователя?

                                                                  Ты же прекрасно понял о чем я.

                                                                  > Я говорил о «рисаовать»?

                                                                  А что еще с ними можно сделать? Управлять реактором можно, свои события выстреливать можно, обработчики тоже можно. В случае с коллбеками ничего кроме как нарисовать ничего не получится. Хватит притягивать божественные процессы ерланга куда только можно.
                                                                    0
                                                                    > Ты же прекрасно понял о чем я.

                                                                    Нет, не понял

                                                                    > А что еще с ними можно сделать?

                                                                    Я уже понял, что ты не понимаешь, что такое иерархия процессов.
                                                                      0
                                                                      Хватит тупить. Я тебе прошу, хватит тупить. Серьезно, я монитор от твоего жира не успеваю оттирать. Я понимаю, что тебе больно от того, что node.js и ruby, популярнее erlang, и то, что там сообщество нормальное и развитое и библиотеки есть и даже драйвера к базам данных (которые, согласно тебе, не нужны для настоящих приложений). Я понимаю, это очень больно, но старайся контролировать это боль, хорошо? Мне кажется ты справишься. Хватит выставлять мак-сообщество идиотами.
                                                                        0
                                                                        Ну понятно, раз аргументов нет, надо попытаться оскорбить оппонента.

                                                                        1. С какого перепугу ты VM'ы node.js сравниваешь с процессами/потоками apache'а, которые одноразово выполняют запрос?

                                                                        2. С какого перепугу на вопрос «как организовывать иерархию процессов/коллбэков с мониторингом и отслеживанием нештатных ситуаций» ты отвечаешь «DTrace нарисует»?

                                                                        3. С какого перепугу ты считаешь что городить огород из pubsub/redis/zeromq для элементарных, по сути, задач — это нормально?

                                                                        Так что тут еще вопрос, чей жир капает у тебя с монитора.
                                                                          0
                                                                          1. Потому, что цепочка из колбэков будет обрабаывать один запрос от клиента. В этой цепочке нет смысла делать общение с другими цепочками. Связь между VM'ами на уровне сообщений от мастера к клиенту и vice versa, вполне достаточно.
                                                                          2. Потому, что ты пытаешься притянуть процессы ерланга за уши. Отлеживать нештатные ситуации можно только через исключения и проверку аргумента в колбеке. Хватит притягивать ерланговские процессы.

                                                                          3. Какой там огород? Я же не RabbitMQ притащил. pubsub позволит разнести VM'ы на разные сервера и сохранить связь между ними. Хватит притягивать ерланговские сообщения.
                                                                            +1
                                                                            > Потому, что цепочка из колбэков будет обрабаывать один запрос от клиента.

                                                                            То есть ты хочешь сказать, что node.js ничем не отличается от, скажем, РНР? Один запрос — одна цепочка действий, всё?

                                                                            > В этой цепочке нет смысла делать общение с другими цепочками.

                                                                            Если ты неспособен что-то представить, не значит, что это не нужно

                                                                            > Потому, что ты пытаешься притянуть процессы ерланга за уши.

                                                                            Это не ответ на вопрос «зачем DTrace»

                                                                            > Отлеживать нештатные ситуации можно только через исключения и проверку аргумента в колбеке.

                                                                            Ну то есть недалеко ушли от С/С++ с таким же «удобством».

                                                                            > Какой там огород? Я же не RabbitMQ притащил. pubsub позволит разнести VM'ы на разные сервера и сохранить связь между ними.

                                                                            Ну то есть хваленая мегагиперкрутая система nodejs неспособна даже на одном компьютере внятно общаться между VM без ручного создания груды велосипедов на каких-то сторонних технологиях. Спасибо
                                                                  +1
                                                                  2 треда в апаче которые обрабатывают запрос клиента общаются между собой?

                                                                  Если это Apache+mod_php, то php скрипты вполне могут через, например, shmem.
                                                                    +1
                                                                    Очевидно же ты просто берешь то, что есть в ерланге и выставляешь как плюс
                                                                    согласен, это нечестно по отношению к инвалидам nodejs
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  0
                                                                  Но нет – для этого берется php, а общее состояние в итоге воссоздается через сериализацию, ключи, доп. библиотеки, протоколы, сокеты, сеть…

                                                                  shared memory стандартный для POSIX механизм разделения данных между процессами. Для Linux ещё есть /dev/shm.
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                      0
                                                                      Просто на удивление(!), мало кто задумывается, какое кол-во проблем вытекает из этой пост-CGIной парадигмы.

                                                                      Ну, я задумываюсь уже лет 10, а толку. Изучать Си и системное программирование, реализовывать в PHP многопоточность, при этом как-то оставляя обратную совместимость, посылать соответствующие патчи без всякой гарантии, что их примут… И всё это ради того, чтобы реализовать true FastCGI с разделением данных между всеми обработчиками запросов, когда существуют пускай не столь эффективные, но рабочие костыли.
                                                                        0
                                                                        мало кто задумывается, какое кол-во проблем вытекает из этой пост-CGIной парадигмы.
                                                                        Да нет — на самом деле много кто задумывается, только действуют не многие.
                                                                        У меня например самописный application server, как раз с такой поддержкой FastCGI, какую вы имеете в виду (правда не для пыха, а tcl и питона) и со всеми плюшками таких серверов, как то пулы данных и потоков, очереди событий и сообщений, background processing, connection sharing и тд и тп.
                                                                        И кстати навесить FastCGI на любой уже существующий application server не есть сложно — нужно просто сделать.
                                                                        Просто порог вхождения для программирования на таком сервере потом «немного» выше — многие из известных мне php программистов не представляют даже какие вещи и как быстро на этом можно собирать, написав в десятки раз меньше кода.
                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                            0
                                                                            Или же имеется в виду что-то другое?
                                                                            Упрощенно говоря вы правильно поняли.
                                                                            + плюс interface для app-server в интерпретере для управления;
                                                                            + плюс rpc для background.
                                                                            А tcl просто потому что гениален, быстр и имеет прекрасный jit — раз, легко интегрируется — два, critcl — три.
                                                                      –1
                                                                      Какой-то бред вы написали. Если VM выполняют одну и туже задачу, то они не должны обмениться ничем. Только на уровне Master -> Worker. Если же надо связать несколько разных VM с разными задачами — сообщения на zeromq первое, что приходит в голову. Люди даже на с++ общения в тредах на нем делают (дам подсказку, сеть использовать не обязательно.
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          0
                                                                          Какой-то бред вы написали. Если VM выполняют одну и туже задачу ...
                                                                          Вы абсолютно не разобрались в материи, и видно что ничего сложнее календаря и простите «helloworld» вы никогда не делали.
                                                                            0
                                                                            А вы я смотрю каждый день твиттер делаете.

                                                                            > Вы абсолютно не разобрались в материи,

                                                                            Это вы не смогли осилить, что я имел ввиду под «Если VM выполняют одну и туже задачу»

                                                                            > простите «helloworld» вы никогда не делали.

                                                                            Как дерзко то и без оснований.
                                                                      +1
                                                                      Мастер поток может общаться с дочерними через worker.send(), process.on('message', function () {… }). Для вещания между всеми процессами есть хороший модуль clusterhub основанный на этом функционале. Т.е. в этом случае можно обойтись без Redis.
                                                                        0
                                                                        Он просто сейчас скажет, что ерланг может все это делать по сети из коробки, поэтому надо было действовать наперед.
                                                                          +1
                                                                          > Мастер поток может общаться с дочерними через worker.send(), process.on('message', function () {… }).

                                                                          Спасибо.

                                                                          — Может ли дочерний поток что-то передать родителю?
                                                                          — Может ли родитель отслеживать, жив ли дочерний поток и каким-то образом обрабатывать нештатные ситуации?

                                                                          > Для вещания между всеми процессами есть хороший модуль clusterhub основанный на этом функционале.

                                                                          Спасибо.

                                                                          Из краткого описания как раз видно, что что они пытаются натянуть модель акторов/событий на node.js. Что говорится, на безрыбье…
                                                                            +2
                                                                            > — Может ли дочерний поток что-то передать родителю?

                                                                            Да может. process.send({ hello: 'master' }); передаст сообщение родителю. Он получит его в worker.on('message', function(data) { });

                                                                            > — Может ли родитель отслеживать, жив ли дочерний поток и каким-то образом обрабатывать нештатные ситуации?

                                                                            Да может, для этого существуют несколько событий сигнализирующих о разных этапах помирания процесса.
                                                                              +1
                                                                              Спасибо
                                                                              0
                                                                              Может ли дочерний поток что-то передать родителю?
                                                                              Да. Пример из документации по Node.js:

                                                                              var cp = require('child_process');
                                                                              
                                                                              var n = cp.fork(__dirname + '/sub.js');
                                                                              
                                                                              n.on('message', function(m) {
                                                                                console.log('PARENT got message:', m);
                                                                              });
                                                                              

                                                                              Может ли родитель отслеживать, жив ли дочерний поток?
                                                                              Аналогично, n.on('exit', function(code, signal){ …
                                                                                +1
                                                                                Спасибо
                                                                +2
                                                                А я наоборот, где-то год назад сходил на мастер-класс по эрлангу к товарищу Лапшину, и понял, что сервер буду писать на node.js.
                                                                  0
                                                                  Чем вызвано такое желание?
                                                                    +1
                                                                    Эрланг показался слишком уж низкоуровневым и непривычным для реализации игровой логики. Можете считать, что я просто «ниасилил». Тяга к императивному стилю оказалась сильнее.
                                                                    Но должен признать, что именно на этом мастер-классе я примерно понял, как можно более легко и удобно писать на ноде.
                                                        +9
                                                        Чего вы людей в заблуждение то вводите?

                                                        Пока процесс делает блокирующий вызов он процессорное время не потребляет!

                                                        Ппц…
                                                          0
                                                          По-моему, только в случае если где-то внутри блокирующего вызова не используются асинхронные механизмы или принудительная передача управления ядру. Если блокирующая функция написана как-то вроде (псевдокод):
                                                          file_read(file) {
                                                            _file_read_init(file);
                                                            while(!_file_read_ready(file))
                                                            {
                                                            };
                                                            return _file_read_data(file);
                                                          }
                                                          

                                                          то она будет потреблять процессор, если _file_read_ready() не использует асинхронные механизмы или не вызывает, скажем sleep();
                                                          +2
                                                          Если у вас задача — за 2мс получить данные из БД и за 2мс их обработать то единственным разумным решением будет запустить по 2 потока на ядро.

                                                          Вот если у вас под 90% времени IO — то тогда да, есть смысл сэкономить память и разгрузить планировщик OS используя «асинхронный» подход (неблокирующее IO, FSM и собственный планировщик)
                                                            +1
                                                            Если у вас 90% времени IO — значит либо алгоритмы работы с данными диске надо оптимизировать, либо программа написана очень хорошо. А вот если в sys система дофига времени проводит, и количество контекст свитчей зашкаливает, вот тогда надо думать thread pool vs. async
                                                            +2
                                                            «Но ведь при этом мы получаем 50% простоя процессора!»

                                                            Разве мы получаем 50% простоя процессора? Ведь в это время процессор работает с БД, делает выборку, формирует ответ.

                                                            В конце концов все рассчитывается процессором. Отправив запрос на считывание файла мы забираем часть мощности процессора на загрузку файла, в этот момент получив следующий запрос, менее ресурсоемкий, процессор ведь не прекращает считывание файла он лишь также выделяет другую часть своей вычислительной мощности. А как придет этот запрос, в другом потоке или асинхронно мне кажется не имеет значение для загрузки процессора. Или я ошибаюсь?
                                                              0
                                                              В конце концов все рассчитывается процессором. Отправив запрос на считывание файла мы забираем часть мощности процессора на загрузку файла
                                                                +2
                                                                Упс, зацепил shift+enter случайно. Хотел ссылку дать в ответ на цитату: DMA
                                                                +2
                                                                Работать с БД, делать выборку и формировать ответ может процессор на другом краю Земли (чаще, конечно, в том же датацентре, но не суть), localhost в свойствах подключения — это частный случай, да и все равно какое-то время процессор будет простаивать ожидая данных с диска или даже с памяти. не 50%, конечно, но это же пример.
                                                                  0
                                                                  с чего вы взяли, что процессор будет простаивать? что мешает ОС переключить его на выполнение другого процесса/потока? Да, смена контекста — накладные расходы, но тем не менее в многопоточной программе блокирующий ввод/вывод в одной нити не приводит к блокировке всех потоков, в отличие от однопоточной на основе epoll/kqueue, кстати.
                                                                    0
                                                                    Современные популярные ОС для ПК и серверов — это ОС с вытесняющей многозадачностью. Если у нас, скажем, 10 процессов с одинаковым приоритетом, работающих вплоть до физического ввода/вывода синхронно, то за 1 секунду все десять отработают по 0,1 секунды. ОС не знает, выполняет процессор сложные вычисления или гоняет цикл типа while(read_port(disk) !== DATA_READY);. С точки зрения ОС процессор работает, с точки зрения пользователя — простаивает, не выполняя полезной работы, выполняя пустой цикл до наступления какого-то события 10% процессорного времени. В асинхронных же системах процесс сообщает системе, что ждёт какого-то события, и пока событие не наступит, передавать ему управление смысла нет, он «спит» и ждёт «будильника». Хотя бы в таком примитивном виде как while(read_port(disk) !== DATA_READY) { usleep(1); }
                                                                      0
                                                                      Вообще-то, в современных популярных ОС потоки, которые заснули на блокирующем сисколе, не получают своего кванта времени до тех пор, пока сискол не отработает. Никаких пустых циклов ожидания в здравом уме никто не делает, исключением из этого является спинлок.
                                                                        0
                                                                        Потому что сискол работает асинхронно, неявно переводит процесс в спящее состояние.
                                                                          0
                                                                          Обычный блокирующий read() работает асинхронно? Вот это внезапная новость!
                                                                            0
                                                                            Никаких пустых циклов ожидания в здравом уме никто не делает

                                                                            Это неправда?
                                                                              0
                                                                              Что неправда? Сформулируйте вопрос корректно.
                                                                                0
                                                                                Вы готовы гарантировать, что вызов read() в произвольной библиотеке не опускается до асинхронных операций, или просто предполагаете «здравый ум».
                                                                        +1
                                                                        с чего Вы взяли, что блокирующие операции ввода/вывода работают именно так, с пустым циклом? Системные вызовы реализованы несколько иначе :)
                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                    0
                                                                    в целом статья хорошая — спасибо.
                                                                    есть несколько замечаний:
                                                                    На асинхронной модели построен самый быстрый в мире веб-сервер nginx.
                                                                    отстал от жизни — уже не самый. Но Сысоев, несомненно, внес большой вклад в развитие рунета.
                                                                    При отправке запросов к БД поток начинает ожидать ответа. Пока данные не вернутся — поток никакой работы выполнять не будет. Это 2мс простоя на запрос в 4мс! Да, мы не можем сделать рендеринг страницы, не получив данные из базы. Мы обязаны ждать. Но ведь при этом мы получаем 50% простоя процессора!

                                                                    В это время будет отработан другой поток. Операционная система сама распределит ресурсы! Да, есть небольшие накладные расходы. Мониторинг моего продакшен сервера показывает загрузку на все 800% всех 8- ядер.

                                                                    Согласен, что «блокирующие операции — зло». Но с неблокирующими операциями есть свои подводные камни, своя оборотная сторона медали.
                                                                    Предположим, что мы ждем ответа от «тяжелого запроса», в это время пришел еще один «тяжелый запрос» — мы открыли новый буфер для обработки соединения, затем новый… а первый еще не обработался… и не обработался второй… А процесс обработки всего один…
                                                                    В итоге может возникнуть такая ситуация, что заполнится вся выделенная область памяти и воникнет переполнение
                                                                    буфера или начнется «сокетный голод».
                                                                    Конечно, если будут чередоваться тяжелые и легкие запросы, что ближе к жизни, то такой ситуации может и не возникнет… Но, дураков в жизни хватает, до сих пор нахожу в проектах «поиск по Like 2-Процента». Так что, Дурак может убить любую архитектуру, будь она многопоточная или асинхронная.
                                                                      0
                                                                      «отстал от жизни — уже не самый»
                                                                      А кто и как это определеяет, кстати? Просто интересно, может я тоже отстал. =)
                                                                        0
                                                                        Приктика -критерий истины, в нашем случае это определяют тесты
                                                                        www.monkey-project.com/benchmarks/raspberry_pi_monkey_nginx
                                                                        gwan.ch/benchmark
                                                                        есть и еще малые быстрые сервера, наш рунет немного зациклился на nginx
                                                                        собираюсь сделать исследование и обзор
                                                                          +2
                                                                          Жуткий оффтоп, но:

                                                                          Приктика -критерий истины

                                                                          Именно, и на практике выходит вот как.

                                                                          это определяют тесты

                                                                          Тесты хэлоуворлдов? Тем более от авторов конкурирущих продуктов? Не смешите.

                                                                          Мы не побуликуем тесты, в которых nginx обходит всех остальных, включая перечисленных вами, только по одной простой причине: мы знаем как настраивать свой продукт, знаем его сильные и слабые стороны, знаем как из него выжать максимум. Публиковать подобные тесты, как это делают конкуренты — это опускаться до их уровня.

                                                                          Nginx показывает высокий уровень производительности на большинстве задач, при этом прекрасно масштабируется, обладает богатым функционалом, имеет хорошую поддержку и активно разрабатывается. И год от года становится ещё лучше. В общем и целом — отличный выбор. Выжимание максимума rps в искусственно созданных условиях на синтетических задачах — интересно только админам локалхостов.

                                                                          есть и еще малые быстрые сервера

                                                                          Есть. Но когда пишут «самый быстрый», то стоит огласить критерии, а то окажется, что самый быстрый веб-сервер побеждает в состязаниях далеких от реальных задач (как то отдать ab «Hello world»).

                                                                          наш рунет немного зациклился на nginx

                                                                          И хорошо. От продвижения отечественного продукта в конечном итоге выигрывает вся страна.
                                                                            0
                                                                            я не вижу здесь конкуренции
                                                                              0
                                                                              Когда пользователя побуждают к отказу от одного продукта в пользу другого, проводя сравнение с помощью специально поставленных тестов — это что?
                                                                                0
                                                                                см комментарий ниже, у этих продуктов разнное назначение.
                                                                                Мне будет трудно убедить тебя уйти с MySQL на редис или Монго, хотя они более производительней. Почему? — потому что каждый из них решает определенный класс задач. Где-то дучше редис, где-то Монго, а где-то мускуль.
                                                                                  0
                                                                                  И слава разработчикам, я не видел на сайте MongoDB каких-либо заявлений относительно Redis.
                                                                                    0
                                                                                    А с Мускула на Постгрес? Вроде классы задач у них очень сильно пересекаются.
                                                                                0
                                                                                >Именно, и на практике выходит вот как.
                                                                                согл, популярность набирает — это факт
                                                                                >Но когда пишут «самый быстрый», то стоит огласить критерии, а то окажется, что самый быстрый веб-сервер побеждает в состязаниях далеких от реальных задач (как то отдать ab «Hello world»).
                                                                                Отдача «Hello world», согласись со мной, вполне сойдет, как один из критериев.

                                                                                Вообще-то, если внимательно почитать назначение, то эти сервера вовсе не предназначены для WEB порталов. У них совсем другая ниша: встраиваемые решения. У них простой функционал, а соответственно отсюда и скорость.
                                                                                  0
                                                                                  Отдача «Hello world», согласись со мной, вполне сойдет, как один из критериев.
                                                                                  Нет, не соглашусь. Хотите отдавать «Hello world», то соберите nginx без пары десятков опций и модулей, а в конфиге напишите:

                                                                                  location / {
                                                                                      return 200 "Hello world";
                                                                                  }
                                                                                  

                                                                                  Плюс буфера потюнить под эту строчку, + accept_mutex, + лог, и т.д.

                                                                                  Вообще-то, если внимательно почитать назначение, то эти сервера вовсе не предназначены для WEB порталов.
                                                                                  Отлично, если они имеют совсем другое предназначение и никак не претендуют на область применения nginx, то зачем их сравнивают с nginx, причем в стандартной сборке?
                                                                                    0
                                                                                    а сделать тест можешь?
                                                                                      0
                                                                                      Могу, получу некое число rps на своей системе, оно кому-нибудь нужно? Вот тут люди 1 миллион запросов в секунду с nginx снимают.

                                                                                      С практической точки зрения важно, чтобы nginx не становился bottleneck-ом в той или иной реальной задаче.
                                                                                      0
                                                                                      если без холивара, то я писал специализированные WEB сервера по подсчету кликов, в 4 раза производительней nginx. (мерилось ab моего сервиса и nginx, который проксировал запросы на Сишный демон)
                                                                                        0
                                                                                        хотя, отчасти ты прав — в энжиниксе были подключены все дефолтные модули…
                                                                                        убрать их — производительность ненамного поднимется
                                                                                          +1
                                                                                          Самый быстрый веб-сервер — это тот, который ничего не делает. =)

                                                                                          ab — кстати, слишком медленная и криво-написанная утилита. Её не имеет смысл использовать для тестирования nginx-а. Я, например, наблюдал ситуации, когда с точки зрения ab rps падал при выключении access-логов. Происходила забавная штука, nginx обрабатывал запрос так быстро, что успевал уйти в epool и отдохнуть там до того, как ab присылала новый. Цифры ab часто зависят от того, насколько её паттерн работы накладывается на паттерн работы веб-сервера. Это, впрочем, верно для любого синтетического бенчмарка.

                                                                                          Вместо Си-шного демона, я полагаю, можно было бы слепить что-нибудь на lua прямо в nginx, и вместе с luajit оно бы не сильно уступило специализированному Си-шному демону, а времени на разработку и отладку было бы потрачено в разы меньше.
                                                                                            0
                                                                                            Вспомнил забавный топик в рассылке:
                                                                                            mailman.nginx.org/pipermail/nginx/2012-April/033304.html

                                                                                            Человек протестировал одну и ту же конфигурацию с помощью трех разных утилит и получил в разы отличающиеся цифры, удивился. =)
                                                                                              0
                                                                                              да, знаю, есть такое…
                                                                                              просто я уже привые к аб :(
                                                                                                0
                                                                                                Попробуй wrk, http парсер у этой штуки от nginx-а, асинхронные треды на epool/kqueue. Собирается очень просто. Имеет смысл клонировать pipeline бранч.
                                                                                              0
                                                                                              Тестировал как-то пару лет назад G-WAN vs nginx на weighttp, статику (но не helloworld), так вот — G-WAN порвал тогда nginx как тузик грелку. Надо бы попробовать снова (оба заметно подросли). До сих пор не переехал с nginx, в силу занятости (нужно переписать несколько модулей) и не кроссплатформенности G-WANа (есть еще одна VM под винду).
                                                                                                0
                                                                                                Вашу статику с помощью weighttp запрашивают? =) Рекомендую почитать: раз и два.
                                                                                                  0
                                                                                                  И пожалуй три (The many pitfalls of benchmarking) — простая истина в изложении разработчика Varnish.
                                                                                                    0
                                                                                                    Вы меня совсем за новичка держите:
                                                                                                    Я специально включил «accesslog» на статику за неделю и полностью отэмулировал его на weig, со всеми плюшками, типа 304 (с if-modified-since, разрывом, релоад, и т.д.), с нескольких машин многопоточно и одновременно.
                                                                                                    И я прекрасно знаю отличие от продакшн — даже сотня браузеров ведет себя не как тесты подобные weighttp — и естественно сперва попробовал бы G-WAN в бою — но как говорилось уже руки не доходят.

                                                                                                    В G-WAN меня привлекают еще два момента:
                                                                                                    1) G-WAN разрабатывался изначально почти как app-server (nginx — нет) — например c++ servlet пишется под G-WAN на ура быстро — т.е. можно прикрутить tcl, питон (любимое подставить) как реальный application server и byebye fast-cgi;
                                                                                                    2) Для nginx я видимо так и не дождусь никогда NTLMAuthMod'а, а ниписать его под nginx4win видится мне весьма проблематично — для G-WAN это выглядит, судя по API, многим проще. Хотя не так давно они отказались от разработки под windows — кроссплатформенность мне пока что очень важна — но может быть удастся форкнуть его от предыдущих ревизий под винду.

                                                                                                    Может вообще буду юзать сразу оба — таки да — nginx я тоже люблю и уважаю)
                                                                                                      0
                                                                                                      Это очень любопытно, как вы планируете форкать GWan, учитывая его лицензию и отсутствие исходников?
                                                                                                        0
                                                                                                        Как то в памяти остался как опенсорс, ан нет — посмотрел, и правда elf.
                                                                                                        Может раньше по другому было?
                                                                                                        И кстати да, про лицензию не подумал совсем, спасибо.
                                                                                                          0
                                                                                                          Насколько я помню, всегда так было.
                                                                                                          0
                                                                                                          да, их отсутствие — это большой минус
                                                                                                  0
                                                                                                  github.com/yandex-load/yandex-tank
                                                                                                  Дарю :) Пока, правда, без красивых графичков.
                                                                                                    0
                                                                                                    Надо будет его побенчмаркать с помощью nginx-а, быстрее он wrk или медленнее. =)
                                                                                                      0
                                                                                                      Ой, да вы прикалываетесь, этот yandex-tank вообще на Perl. Боюсь себе представить, сколько мне серверов потребуется, чтобы создать такую нагрузку, которую бы nginx с хэлоуворлдом заметил.

                                                                                                      Сия тулза вообще главным образом для тестирования веб-приложений. Оверхэд самого веб-сервера с помощью неё измерить едва ли возможно.
                                                                                                        0
                                                                                                        Вообще-то собственно стрелялка на С написана, phantom называется. В дебиан контроле он по зависимостям тянется. На перле и питоне обвязка для запуска только написана. Я всякие маркетинговые цифирки плохо помню, но 60к рпс своими глазами видел.
                                                                                          0
                                                                                          немного оффтопа:

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

                                                                                          Лично, я nginx-сом вполне доволен и все проекты, с которыми я работал последние 5-ть лет, крутятся на нем. Про аппач — я давно уже и забыл…
                                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                                              0
                                                                                              Это означает замедление продвижения, если не его полную остановку и откат назад. Для многих дешевле купить побольше железа для Apache или lighttpd, чем обучать разработчиков и админов русскому.
                                                                                      +5
                                                                                      Каждый скрипт работает в своем потоке. Один поток обрабатывает один запрос. Потоков у нас достаточно большое количество, медленные запросы забирают поток надолго, быстрые — обрабатываются почти мгновенно, освобождая поток для другой работы.
                                                                                      Автор явно путает понятие потока и процесса. В случае с РНР, есть разные варианты обработки. Либо это Апач, который запускает n-е worker процессов. Либо это nginx с проксированием FastCGI на кучу запущенных процессов php-fpm, либо это лайти, c проксированием на запущенных spawn php-fcgi
                                                                                      В любом случае — это отдельные процессы, с отдельно выделенным адресным пространством.
                                                                                      В РНР нет ни каких потоков и многопоточности
                                                                                        0
                                                                                        В данном случае разницы между потоками и процессами никакой нету.
                                                                                          0
                                                                                          а что за такой данный случай?
                                                                                          есть понятие поток (thread) есть понятие процесс (process).
                                                                                            0
                                                                                            Который в статье написан. С точки зрения ядра Linux, к примеру, поток — это тоже процесс, просто он имеет общее адресное пространство и общие ядерные объекты типа дескрипторов с другими процессами, входящим в тот же process group. Если не пользоваться общей памятью, не использовать общие дескрипторы, то разницы между процессом и потоком с прикладной точки зрения нету.
                                                                                              0
                                                                                              Общая память разве не входит в основные отличия многопоточной архитектуры от многопроцессной?
                                                                                                0
                                                                                                с точки зрения Unix и в частности qnx — совершенно разные понятия, которые путать нельзя
                                                                                                  0
                                                                                                  Ещё раз: я не говорю о том, что между ними нету разницы в принципе. Я утверждаю, что если каким либо образом запустить php в нескольких потоках и при этом это будут независимые интерпретаторы, и если запустить его в нескольких процессах, то нет никакой разницы при сравнении с node.js, т.к. что мультитредность, что мультипроцессность — суть не меняется, ОС одинаково будет шедулить потоки, потоки точно так же будут спать в сисколах и т.д. Слово абстракция вам знакомо?

                                                                                                    +2
                                                                                                    Вы абсолютно не правы — это если php в том виде как есть запустить многопоточно, то отличий действительно будет не много.
                                                                                                    Если же php правильно сделать multithreaded — т.е., как вам уже верно намекнули выше, с общей памятью, общеми соединениями, очередями и др. плюшками — это будет извините совсем другой коленкор.
                                                                                                    И абстракция здесь не причем — автор сделал грубую ошибку — ему на нее указали. Просто как раз такими ляпами сбивают с пути истинного неопытных программистов:)
                                                                                                    Не холивара ради — правды для…
                                                                                                      0
                                                                                                      если каким либо образом запустить php в нескольких потоках и при этом это будут независимые интерпретаторы

                                                                                                      И зачем так извращаться? По крайней мере в Линукс, как самой популярной ОС для PHP. Суть многопоточности как раз в том что потоки не независимы, а могут шарить данные между собой меньшей ценой чем при многопроцессности.
                                                                                                        –3
                                                                                                        Отвечу сразу и вам, и комментатору выше.

                                                                                                        Что такое асинхронное выполнение кода, как в node.js? Это 1 поток, который получает уведомления от ядра, и свитчит контексты задач в userland. При этом внутри ядра для получения асинхронности дискового ввода-вывода AIO в Linux, к примеру, создаются потоки, которые блокируются, а потом посылают уведомление в userland. Минусы: сложно писать. Именно про это пишет автор поста.

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

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

                                                                                                        И да, я писал сервисы на тредпуле, которые держат десятки тысяч запросов в секунду. И да, я в курсе как применять epoll вместе с конечными автоматами для написания асинхронных приложений, писал хитрые модули для nginx и смотрел, как устроена node.js. И как бороться с лишними решедулингами в сетевых приложениях тоже, кстати, знаю.
                                                                                                          0
                                                                                                          Что вы там хотите шарить между потоками в типичном веб-приложении мне совершенно непонятно, т.к. в большинстве своём каждый запрос не зависит от остальных, это одно из условий горизонтального масштабирования. Или вы считаете, что синхронизация доступа к одному ресурсу из нескольких потоков бесплатна?

                                                                                                          Read-only данные, включая собственно исходный код приложения, вернее результат его интерпретации, обработанные конфиги и т. п., что постоянно для обработчиков всех запросов (пускай что-то и не в каждом используется) и инициализируется, как правило, при старте сервера, в общем глобальный нэймспэйс. PHP при традиционном использовании (встраивание в сервер или fpm) фактически ведёт себя как CGI приложение, сбрасывая практически всё, единственно не порождая новый процесс интерпретатора на каждый запрос, между обработчиками передавать данные можно только через внешние сущности типа кукисов, сессий, IPC, файлов, APC, мемкэша или СУБД, причём передавать можно только сериализуемые данные. Если уж совсем утрировать, то хочется чтоб если есть во фронт-контроллере конструкция require_once 'superframework.php'; $app = new App(); $app->mainLoop(), то чтобы она отработала при старте сервера, а не при каждом запросе.
                                                                                                            –1
                                                                                                            Даже если хранить в каждом процессе копию всего этого барахла (я про байткод), то получится аж целых пара мегабайт на процесс. При современных объёмах оперативки на серверах это смешные цифры. Если поднапрячься — то можно в шаред мемори положить, тогда совсем оверхеда не будет. И то, что не получается 1 раз проинициализировать всё нужное, проблема PHP, а не обычного многопроцессного приложения. А точнее, это даже не проблема, а защита от дурака.
                                                                                                              0
                                                                                                              Я скорее не про память, а про процессор на интерпретацию этого байт-кода. Миллионы, а то и миллиарды раз в день его интерпретировать как-то жаба душит
                                                                                                                0
                                                                                                                Разве у nodы нет Jit копилятора? У пыха правда есть APC или eAccelerator, так что насчет интерпретации миллион раз на дню — это вы завернули :)
                                                                                                                  0
                                                                                                                  Не знаю. Я про PHP пишу, что без многопоточности на нём плохо, неэффективно расходуются ресурсы.

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

                                                                                                                  А миллион раз в день приходится интерпретировать байт-код убогому чатику с 12 человеками онлайна, если чат обновляется раз в секунду.
                                                                                                                    0
                                                                                                                    а переписать на лонг-пул? используя демонизацию? Там есть свои плюсы.
                                                                                                                      0
                                                                                                                      Долго думал над этим вот:
                                                                                                                      А APC и прочие кэшеры на интерпретацию никак не влияет, они кэшируют результаты компиляции исходников в байт-код, но не интерпретацию этого байт-кода.
                                                                                                                      Вы спутали понятия интерпретация и компиляция, если упрощенно, то:
                                                                                                                      Компилятор читает (интерпретирует) source code и создает байт-код;
                                                                                                                      Байт-код не интерпретируется а исполняется.
                                                                                                                      Теперь про APC:
                                                                                                                      «APC обеспечивает ускоренное выполнение PHP-скриптов. Ускорение достигается путем компиляции PHP кода, который кэшируется и повторения компиляции можно почти полностью избежать.» Почти — потому, что есть такие бяки как «eval», которые исполняют (и компилят runtime) динамически генерируемый код (строку). Почитайте на досуге…
                                                                                                                      Я вам просто пару цифр оставлю: phpMyAdmin/Index (10.000 раз) без APC — 17.3s, c APC 2.35s.
                                                                                                                      Конечно APC не панацея — просто разговор шел про «Миллионы раз в день его интерпретировать как-то жаба душит...» — а это как раз чистый php-fpm без акселератора.
                                                                                                                        0
                                                                                                                        Байт-код исполняется путем его интерпретации. Чего-то вроду JIT в PHP нет. Ускорение достигается путем кэширования результатов компиляции. То есть без опкэшеров PHP на каждый вызов сначала компилирует исходники в байт-код в памяти, а потом его интерпретирует, а с ними компилирует только один раз, помещает результат в кэш, и при следующем вызрве сразу берёт байт-код.
                                                                                                                  0
                                                                                                                  А вы попробуйте написать теперь что-нибудь более серьезное, чем helloworld на 2МБ (я тоже про байт код). Что-то с крупными библеотеками, тысячами различных sql запросов (я про динамические конструкции, prepared statements, 35ГБ данных в месяц и т.д.), множественными связными деревьями в 100500 веток и т.д.
                                                                                                                  Я вам попытаюсь объяснить на примере нашего application server для одного такого приложения (кстати c++ и немного tcl + 2500 человеко дней работы).
                                                                                                                  single threaded (1 worker) front end жрет в пике 300MB, pipeliner (background) single threaded (1 worker) — 240MB.
                                                                                                                  Тоже самое с 3-я потоками 500MB и 312MB соответственно, с 10-ю всего 1350MB и 740MB соответственно.
                                                                                                                  Запустив 10 single threaded frontend и 10 single threaded pipeliner процессов я сожрал бы всю память на этой и двух соседних машинах. Ее можно конечно увеличить — только вот практика показывает, что максимальный performance increase получается при увеличении потоков, потом нодов на кластере и только в последнюю очередь — количества процессов на одной ноде.

                                                                                                                  Так-что не надо мне красиво расказывать кто-что делал и кто-что умеет и знает. В реальности иногда все обстоит совсем не так.
                                                                                                                  Это моя последняя попытка объяснить вам и возможно автору, что многопоточно и многопроцессорно есть две большие разницы — и только.
                                                                                                                    –1
                                                                                                                    Я в курсе разницы. Вы тоже уже поймите наконец, что я хотел сказать: что поток, что процесс одинаково будут спать на сисколлах, в отличие от асинк модели. А кто больше памяти будет жрать и как с ней работать — это соооовсем другой вопрос.
                                                                                                                    0
                                                                                                                    А вы работали когда нибудь активно с shared memory? Это же голая синхронизация без конца, причем далеко не тривиальная (concurrency, deadlock prevention, все дела ...). Почитайте хотя бы здесь на досуге.
                                                                                                                      0
                                                                                                                      А вы внутри одного процесса между потоками работу с памятью не синхронизируете?
                                                                                                                        0
                                                                                                                        Для того, что вы планировали сделать с шаред — это совсем не обязательно.
                                                                                                                        Мы тут про «асинхронное выполнение кода» говорим — нет. (ваша цитата 2 комента выше). Каким боком вы туда шаред приделывать собираетесь.
                                                                                                                      +1
                                                                                                                      все в шаредмемори положить не получится, плавали-знаем
                                                                                                                        0
                                                                                                                        Все объекты шарить между потоками тоже не получится. Но удобнее, безусловно.
                                                                                                                      0
                                                                                                                      В РНР существуют персистентные соединения, правда их не рекомендуется использовать
                                                                                                                        +1
                                                                                                                        Я в курсе. И даже в курсе почему не рекомендуется. Но это лишь малая толика того, из-за чего у меня сердце кровью обливается глядя на статистику «своих» сайтов.
                                                                                                                          0
                                                                                                                          >из-за чего у меня сердце кровью обливается глядя на статистику «своих» сайтов.
                                                                                                                          можно по подробннее, или в личке…
                                                                                                                            +1
                                                                                                                            Смотрю на посещаемость и переживаю, что куча одинаковой работы выполняется на каждом запросе.
                                                                                                      0

                                                                                                      Это просто богоподная статья, которая просто идеально описала все на примере двух языков. Респект! Ресссспектттт!!!

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

                                                                                                      Самое читаемое