Нагрузочное тестирование: Node.JS vs phpDaemon

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

    Потенциальная нагрузка, которую должен выдерживать такой чат — около 10000 одновременных keep-alive соединений. Каждое новое сообщение должно записываться в основную базу данных, а так же в «быструю», задача которой хранить в себе лишь актуальную часть переписок между пользователями, то есть служить своеобразным «временным» хранилищем, из которого сообщения будут сразу доставляться адресату.

    Для решения этой задачи хорошо подходит MongoDB. Поскольку в ней хорошо отлажен процесс репликации, мы можем отдельным демоном периодически опрашивать таблицу local.oplog.$main на предмет наличия в ней новых сообщений, и, если таковые имеются, сразу доставлять их по адресу в формате JSON. Для устойчивой работы под нагрузкой, таких демонов будет несколько, к каждому из которых будут подключены несколько тысяч переписчиков.

    И здесь перед нами встал выбор, что использовать в качестве «быстрого сервера», на котором будет размещены демоны? Для этого мы протестировали два решения: на основе phpDaemon и на основе Node.JS.

    Итак, для начала, полная архитектура чата:

    Полная структура чата
    Рис. 1. Полная структура чата
    1. Каждый новый клиент, входящий в переписку устанавливает соединение с одним из «быстрых» серверов (phpDaemon или Node.js) и от него же будет получать сообщения.
    2. При отправке на основной сервер нового сообщения, оно записывается в постоянную базу данных (MySQL), а так же в некую «быструю» базу данных (в данном случае MongoDB), где будет храниться только актуальная переписка, например за последние сутки.
    3. Master MongoDB записывает изменения в коллекцию local.oplog.$main для репликации.
    4. «Быстрые сервера» в это время ведут опрос «быстрой» базы на предмет наличия в ней новых сообщений, и, если такие есть, отправляют их тому клиенту, которому они адресованы.

    В нашем тестовом варианте не будет постоянной базы данных, а так же авторизации пользователей. Будет только один «быстрый сервер», который будет забирать новые сообщения из коллекции и рассылать всем активным в данный момент клиентам (рис 2).

    Упрощенная структура чата
    Рис. 2. Упрощенная структура чата

    Составы играющих сегодня команд:

    Команда Node.js
    1. Node.js (http://nodejs.org)
    2. Библиотека Socket.IO для установления соединений между клиентом и Node.js (http://socket.io)
    3. Библиотека Node-mongo-native от christkv для работы с MongoDB из Node.js (https://github.com/christkv/node-mongodb-native)
    4. Ну и файл oplog.js из той же библиотеки для опроса MongoDB на предмет наличия новых сообщения (https://github.com/christkv/node-mongodb-native/blob/master/examples/oplog.js)

    Команда phpDaemon
    1. phpDaemon (http://phpdaemon.net)
    2. Скрипты из состава phpDaemon для установки соединения между клиентом и сервером (https://github.com/kakserpom/phpdaemon/tree/master/clientside-connectors/websocket)
    3. Для работы с MongoDB так же будем использовать встроенные возможности phpDaemon
    4. Для опроса базы будет использоваться немного измененное нами приложение MongoNode (https://github.com/kakserpom/phpdaemon/blob/master/app-servers/MongoNode.php), которое не пишет изменения в Memcache, а отправляет их всем подключенным пользователям.

    Судья встречи Apache Benchmark.

    Итак, мы запускаем один из демонов, который ведет polling MongoDB, а так же «общается» с пришедшими клиентами. С помощью утилиты Apache Benchmark с другой машины подключаем несколько keep-alive клиентов (время тестирования 20 секунд). В это время сторонней формой начинаем записывать в MongoDB сообщения. Для каждого демона и каждого числа keep-alive соединений делаем по 5 итераций, потом усредняем полученные данные и видим результат.

    Демон
    Complete requests
    Requests per second
    Time per request, ms
    (across all concurrent requests)
    Min
    request, ms
    Max Request, ms
    10 keep-alive соединений
    phpDaemon
    1147
    54.36
    18.440
    21
    7116
    Node.JS
    1285
    64
    15.618
    16
    9064
    100 keep-alive соединений
    phpDaemon
    1543
    75.15
    13.313
    22
    12889
    Node.JS
    1284
    64.12
    15.765
    17
    13347
    500 keep-alive соединений
    phpDaemon
    1365
    67.73
    14.824
    15
    15174
    Node.JS
    1236
    61.71
    16.611
    23
    16078
    1000 keep-alive соединений
    phpDaemon
    1159
    56.99
    17.680
    19
    13103
    Node.JS
    1528
    75.02
    13.354
    17
    16785

    Красным цветом отмечены «лучшие показатели»

    Как мы видим, на небольшом числе одновременных соединений (10), чат на основе Node.JS работает лучше. Обрабатывается большее число запросов, соответсвенно сокращается время на обработку одного запроса. Правда, phpDaemon имеет меньшую длительность самого долгого запроса, что, правда, не слишком принципиально.

    При увеличении числа соединений до 100, а затем и 500 картина изменилась. Вперед по показателям вышел уже phpDaemon.

    А при 1000 keep-alive соединений показатели phpDaemona упали, а Node.JS наоборот возросли, что в итоге дало довольно сильный разрыв.
    Конференции Олега Бунина (Онтико) 142,60
    Конференции Олега Бунина
    Поделиться публикацией

    Вакансии компании Конференции Олега Бунина (Онтико)

    Комментарии 55
    • +5
      Что использовалось в качестве транспорта в phpd? HTTP или FastCGI? Что было спереди — nginx? Соединение через unix-socket?
      У нас phpDaemon отдает до 2600 rps, и это не предел, это при работе с MongoDB и сложной логикой выполнения запроса. А у вас цифры несколько смешные…
      • 0
        Да FastCGI, nginx и unix-socket.
      • 0
        Удивили более чем достойные результаты phpDaemon. Если учесть тот факт, что phpDaemon не компилируется, то его производительность очень хороша.
        • +1
          Какая разница, компилируется он или нет, если он после одного запуска постоянно крутится в памяти?
        • 0
          А на каком железе это тестировалось и что всякие *top показывали, где кто затыкался?
          • 0
            интересно было бы посмотреть результаты с APC
            • 0
              Здесь XCache используется.
              • +6
                вообщето, должно быть фиолетово, это же демон и смысла кешировать опкоды нет.
            • +3
              Node надо запускать по воркеру на каждое ядро проца. Дает +110% к производительности.
              У вас только один процесс? В статье не указано.
              • 0
                Да, для тестирования один использовался, чтоб условия были равные. В самом проекте будет несколько, там в начале есть про это. Но все равно спасибо)
              • +3
                а как же тест на 5 и 10 тысяч потоков?
                • –4
                  А к чему эта фотография Рэя Остина и Орландьера Солиса?
                  Ваш проект как то связан с боксом?
                  • +1
                    Ну… В каком-то смысле любой проект связан с боксом :)
                  • +2
                    Ещё не хватает данных, через сколько минут (часов) phpdaemon загнётся из-за багов и утечек.
                    • 0
                      Это предположение или вы делали подобное тестирование?
                      • +1
                        мы этим пользовались к сожалению :)
                        • +1
                          Не используйте php в качестве демона. Все очень точно подмечено. php отваливается по неизвестным причинам. Уже более 2 лет работает чат на проекте и проблемы решить так и не удалось, при том что библиотеки были переписаны с нуля, 100% контроль, нет привязки к каким-либо сторонним библиотекам где непонятно кто и что писал.
                          • 0
                            PS раз в месяц стабильно отваливается, при том что народ там почти не сидит. Сами планируем заюзать node.js или python
                            • +7
                              Тут в соседнем лагере подсказывают, что подобный проект — практически идеальное поле боя для Erlang.
                              • 0
                                Более того в каждой второй книге по эрлангу рассказывают как писать чат =)
                                • 0
                                  Мы тоже аналогичную задачу переносим на Эрланг сейчас и это логично.
                              • 0
                                PHP последний? Раньше память текла даже не на демонах, а на фоновой обработке о крону, но в последнее время не заметно.
                            • 0
                              Я видел ваши исходники, у вас просто кривые руки =) И это не связано с самим phpdaemon'ом.
                              • 0
                                Как Вы могли видеть наши исходники? Промышленный шпионаж? ;)
                                • 0
                                  Ваших не видел. Видел исходники проекта о котором говорит gryphon.
                          • +1
                            Странные у Вас тесты.
                            Исходя из данных: чем больше соединений тем быстрее становится Node.js — то есть при повышении нагрузки Time per request стремится к нулю и растет Requests per second.
                            Мне так же не понятно как при повышении числа соединений до 1000, сохранении Min request (почти без изменений) и увеличении Max Request (почти в два раза) получилось обработать больше запросов чем при 10.
                            Либо у Вас ошибка в измерениях, либо я чего-то не пониманию.
                            • +1
                              Еще интересно какая версия NodeJS использовалась.
                              • 0
                                Это да, важно. Например, node.js 2.x работает быстрее, чем node.js 4.x, на 64-битных системах. Это связано с тем, что последние версии V8 не включают часть оптимизаций в 64-битном режиме (надеюсь, это изменится скоро).
                              • +1
                                Погодите… 50-60 запросов в секунду??? по 15 мс на запрос? Из PHP в виде демона совершенно точно можно выжать намного больше, даже из простого Apache+mod_php можно выжать пару тысяч запросов в секунду при «легких» обработчиках. ИМХО, что-то вы делаете не так, или же цифры неверные приведены
                                • +1
                                  Зачем городить огород с хттп-костылями для риалтаймовых сервисов? HTTP вряд ли годится для таких вещей, а комет — хак, нужный вообще непонятно для чего.

                                  Нет ничего приятнее, чем сочинять подобного рода быстрые вещи на RTMP с его нормальным RPC. На сервере Red5, на клиенте — прокси флешка, из которой в сторону JS свисает пачка методов, все просто и логично, ня.
                                  • 0
                                    Вроде же написано, что используется socket.io, то есть где возможно там websocket, где нет — flash socket, ну и далее по убывающей. Вполне надежно, и http используется только когда нет поддержки всего остального…
                                  • 0
                                    Зачем вы огород придумываете? Есть комет решения, например
                                    github.com/maccman/juggernaut
                                    • 0
                                      Насколько я понял, juggernaut использует те же транспорты, что и socket.io. Поэтому не вижу особого смысла связываться с первым, у которого реализована своя модель работы «каналы-подписки» завязанная на Redis, учитывая, что автор использует mongoDB.

                                      Но за ссылку спасибо, добавил в закладки, возможно пригодится.
                                      • 0
                                        Я к тому, что уже всё реализовано. И не надо было городить велосипед, писать свои js, свой комет итд. Если монго используется только для хранения сообщений — тогда тем более без разницы что использовать. Только в случае с джагернаутом не надо ничего придумывать. Всё уже готово и отточено. Да не совсем понятно зачем делать монго как буфер для отправки сообщений — можно сразу валить их на джагернаут(если его использовать).
                                        • 0
                                          Комет реализован и тут и там сразу. У MongoDB и Redis разные парадигмы — что может быть важно. Хотя в целом я с вами согласен, возможно автор как и я, просто не знал про jaggernaut.

                                          Но ведь нет ничего плохого в том, что человек реализует этот функционал по-своему — вдруг потом выложит в opensource и будет альтернатива тому же jaggernaut'у, но уже под MongoDB + socket.io и с отличающимися от «каналы-подписки» принципами :)
                                    • 0
                                      Не понятно почему такие результаты. Почему сперва выигрывает первый, потом второй, потом опять первый?
                                      • 0
                                        меня впечатляют ваши цифры.
                                        вероятно, тестирование проводилось на компьютере Intel Pentium 166 MMX, 32MB RAM, 1.6 GB HDD, я угадал?
                                        • 0
                                          Да, на простом ноуте. Но тесты, которые ставят перед собой цель получить абсолютные цифры, мне всегда были немного странны. Они делаются именно для сравнения различных технологий — для этого важно обеспечить одинаковость условий, а не идеальные условия.
                                          • 0
                                            абсолютных цифр и не надо. просто, на мой взгляд, для тестирование серверного ПО нужно использовать серверное аппаратное обеспечение, ну т.е. какой-нибудь типичный серверный процессор (ксеон или коре2квад), типичное количество памяти.
                                            на моём домашнем сервере на Atom 330 (который ну никак нельзя назвать серверным процессором) я получаю те же 50-70 rps в самом обычном php-fpm + mysql, поэтому ваши цифры кажутся мне немного странными. ведь у вас они должны быть на порядок выше.

                                            кроме того, нельзя просто взять и сравнить. то есть можно, но нужно ещё показать, что результат получился объективным, а не зависит от каких-то случайных факторов. т.е. монго вынести на отдельную машину и убедиться, что вы меряете производительность именно ноды и пхпдемона, а не монго, отключить везде логи, ну и т.д. убедиться, что время, которое вы меряете, это время работы ноды и пхпдаемона, а не время работы чего-то ещё.
                                        • 0
                                          Мне бы интересно было посмотреть результаты продолжительных тестов. Кто из подопытных первым загнется с протечкой или с дедлоком после суток нагруженной работы.
                                          • 0
                                            Talk is cheap. Show me the code. © Хотелось бы ваш чятик у себя попробовать — потестировать. Ну и код поразбирать на предмет косяков. Вдруг вы там на запросах к БД блокируетесь?

                                            Еще интересно сколько каждый процесс занимал памяти при разных нагрузках.

                                            И в завершении — не понял из статьи нафига вам MongoDB тут вообще? Типа если юзер заново подключился — ему кусок переписки за последние сутки прилетает? Или оффлайн сообщения (т.е. оффлайн-сообщения после суток удаляются)?

                                            У меня пока что осталось только ощущение «сферического теста в вакууме».
                                            • 0
                                              Почему запусаются одновременно оба демона?

                                              Вы пробовали тестировать их поотдельности?
                                              • 0
                                                Спасибо за замечание, в статье поправил. Они, конечно, по отдельности тестировались. В тексте от первого варианта теста осталось.
                                              • 0
                                                хм, и почему не на erlang?
                                                попробовали б модифицировать ejabbered, всетаки к задаче он имеет более близкое отношение
                                                • 0
                                                  Потому что у нас в команде нет знатоков Erlang'а.
                                                  • –2
                                                    думаю вам его пора заводить
                                                    • 0
                                                      Это экономически не целесообразно.
                                                • 0
                                                  а почему среднее время на запрос оказалось меньше минимального времени на запрос?

                                                  Демон Time per request, ms Min request, ms
                                                  phpDaemon 18.440 21
                                                  Node.JS 15.618 16
                                                  • 0
                                                    Это среднее время без учёта параллельных запросов.
                                                  • 0
                                                    Проверил свои старые наработки
                                                    Числа примерно в 20 раз лучше получились для обычного php+MySQL и не демона, и примерно в 100 раз круче если демона(phpDaemon)
                                                    Возможно смысл в более правильной архитектуре, заточенной именно под чат, а не просто так.
                                                    Не забываем что если вы шлете сообщение клиенту вы можете просто послать это сообщение клиенту(что нода что phpDaemon — event based. Сгенерите этот ивент и получите нулевой латенси реакции)
                                                    • 0
                                                      Вы выжали 5500 реквестов в секунду к phpDaemon? ;)
                                                      • +1
                                                        чо-то мне подсказывает что у вас всё тестирование упёрлось в винт с MongoDB :)
                                                        • 0
                                                          Попробуйте сравнить с Beseda (более стабильный и быстрый аналог Socket.IO) на стороне nodejs

                                                          github.com/geometria-lab/Beseda
                                                          • 0
                                                            Спасибо, посмотрим. Но этой статье уже год, а в итоге мы вообще отказались от Socket.IO и используем только long polling.

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

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