18 December 2011

Исследование производительности сервера SockJS

Python
Доброе время суток!

Так уж сложилось, что я занимаюсь всякими разнообразными push технологиями с использованием Tornado. Чуть ранее описывал Tornadio2, серверную реализацию протокола socket.io поверх Tornado.

Теперь хочу представить похожий проект — sockjs-tornado.

Для тех кому не очень интересно, есть другая полезная информация: сравнительное нагрузочное тестирование PyPy 1.7 против CPython 2.6.6, sockjs-node и socket.io (оба на node.js 0.6.5). Все под катом :-)

Во первых, что такое SockJS? Это клиентская библиотека написанная на javascript, которая имитирует API Websocket'ов, но при этом поддерживает все браузеры путем использования различных суррогатных заменителей в виде ajax long-polling, jsonp-polling и тому подобных. В целом, весьма похожа на socket.io, но с некоторыми ключевыми отличиями.

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

Так зачем оно надо, если есть socket.io?
Приведу пару причин, которые привели к разработке SockJS:
1. Разработчики socket.io, начиная с версии 0.7, пошли куда-то не туда. Вместо того, что бы исправлять ошибки, добавлять поддержку для разных браузеров, они решили сделать API более высокоуровневым. Я не спорю — все новшества очень удобные, но количество ошибок не уменьшилось. Например достаточно серьезный race condition не могут закрыть больше 3х месяцев. Подключение после отключения не работает до сих пор. Ну и тому подобное.
2. Иногда не хочется привязываться к конкретной библиотеке. Если использовать socket.io, то отказаться от нее будет уже сложно, так как придется менять все места где есть привязка к socket.io.

Так что такое SockJS?
1. Как уже было замечено ранее, это простая замена Websocket API для браузера. Соответственно, переезд существующего приложения использующего вебсокеты будет достаточно безболезненным (если не говорить о серверной части)
2. SockJS работает даже в Opera, которую socket.io ну очень не любит. Кроме того, SockJS правильно работает с разными антивирусами — откатывается на другой транспорт, тогда как socket.io вообще не может установить соединение.
3. SockJS поддерживает streaming протоколы: одно постоянное соединение от сервера, для исходящих данных. socket.io отказался от streaming транспорта начиная от версии 0.7.
4. Протокол сильно проще и очень хорошо документирован. Для разработчиков даже есть набор тестов, которые должна проходить серверная реализация.
5. Масштабируемость заложена в протокол. Например, load balancer'у не надо работать с куками, что бы организовать sticky session — вся необходимая информация уже есть в URL.
6. Библиотека очень хорошо протестирована в разных условиях, в наличие даже есть qunit тесты, которые проверяют и клиент и сервер прямо из браузера. Например вот пример тестов для sockjs-node: http://sockjs.popcnt.org/

В целом, эта штука просто работает.

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

После написания sockjs-tornado, решил проверить как он в сравнении с «родным» сервером написанном на node.js. Сейчас node.js весьма модный, особенно часто говорят о его производительности в различных push технологиях. Заранее скажу — результаты тестирования меня весьма удивили.

Методология тестирования была выбрана очень простая: у нас есть чат сервер с одной комнатой. Каждое входящее сообщение сервер просто отправляет всем участникам чата. Если интересно, вот код сервера.

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

Возможно некоторые будут спрашивать — а что собственно тестируется? А тестируется вот что:
— Скорость реализации Websocket протокола для разных серверов
— Максимальное количество сообщений, при котором сервер начинает захлебываться
— Накладные расходы на поддержку большого количества соединений
— Время отклика при разных уровнях нагрузки

На другой вопрос, а зачем нам нужен «тупой» чат, который вообще без логики? Если кто сталкивался с таким проектом как Humble Indie Bundle, у них количество заработанных денег показывается в реальном времени. Так вот, они используют «брокер», который «держит» большое количество веб клиентов. А так же у них есть источник данных (producer), который время от времени посылает брокеру сколько денег было заработано. Брокер должен разослать эту информацию всем своим подключенным клиентам. Чем быстрее работает брокер, тем большее количество клиентов он сможет обслужить за минимальное время.

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

Если вкратце, то получается следующая картина:
— sockjs-node может отправлять до 45,000 сообщений в секунду при среднем времени отклика в 200 мс.
— sockjs-tornado на cpython 2.6.6 может выдавать до 55,000 сообщений в секунду при времени отклика в 200 мс
— sockjs-tornado на pypy 1.7 просто «разрывает» со своими 150,000+ сообщениями в секунду.

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

Сравнительный график можно посмотреть тут. По оси X у нас количество отправленных сервером сообщений за одну секунду. По оси Y — время отклика. Каждая линия это комбинация сервера (node = sockjs-node, socket.io = socket.io node, cpython = sockjs-tornado на cpython, pypy = sockjs-tornado на pypy 1.7) с количеством одновременных подключений. socket.io приведен для примера производительности другого проекта на node.js.

Даже если не сравнивать node.js и cpython, производительность pypy оказалась для меня полной неожиданностью.

Ну и в заключение.

Рекомендую посмотреть в сторону SockJS если планируется какая либо функциональность реального времени, даже если уже рассматривали варианты с socket.io. И надеюсь что sockjs-tornado кому-то еще пригодится.
Tags:pythontornadosockjssockjs-tornadosocket.iobenchmarkpypynode.js
Hubs: Python
+55
13.2k 126
Comments 13
Python Developer
from 80,000 to 200,000 ₽kt.teamRemote job
Python developer
from 170,000 to 200,000 ₽AIR ProductionМосква
Python Разработчик (Python Backend Developer)
from 150,000 ₽Правое полушарие ИнтровертаRemote job
Python разработчик
from 70,000 to 130,000 ₽AGORARemote job
Python Developer
from 2,500 to 4,000 $OffersHubRemote job