Главная > AJAX, Flash, ActionScript, Open Source, Высокопроизводительная архитектура > Веб-приложения реального времени: jSocket,Node.JS, Redis, MQ. Часть 1.

Веб-приложения реального времени: jSocket,Node.JS, Redis, MQ. Часть 1.

3 мая 2010

Приветствую свои читателей. Да, у меня снова выдался сложный период и обновлений в блоге не было долгое время, хотя в твиттере я продолжал писать, пусть и не каждый день. Но сейчас праздники, все сервисы, над которыми я работаю, вроде более менее работают, поэтому можно, вместе с отдыхом, подумать при новую статью. Буквально неделю назад я наконец добрался до новой потрясающей штуки - серверного JavaScript и его платформы - Node.JS. Реально, это очень и очень мощная и приятная штука. За последнее время это первый раз когда от программирования получаешь просто удовольствие. Тем более, что писать различные асинхронные штуки на этом движке очень и очень легко.

Для тех, кто не в курсе - Node.JS это серверный интерпретатор JavaScript построенный на базе V8 от Google, дополненный системными библиотеками, неблокирующим асинхронным I/O и прочими вкусностями. И очень, очень быстрый. Несмотря на текущую версию 0.1.93, он уже вполне пригоден для промышленного использования, хоть и со многими оговорками, в первую очередь связанных с отсутствием или сыростью дополнительных библиотек. Также, поскольку развитие идет полным ходом, часть API не стандартизированная и поэтому вы можете запросто потратить полдня на то, что в этой версии часть функций перенесена в другой модуль или изменились параметры функции. Однако это просто детские болезни роста и при должном старании из этого даже получаешь удовольствие.

Если кто помнит мои материалы раньше, я интересовался темой AJAX и вообще обмена данными в веб-приложении. На сегодня только AJAX делает это более-менее удобно и стандартно (обычно, используя сторонние библиотеки для сокрытия кросс-браузерных отличий). Но очень хотелось бы иметь что-то лучшее - например, WebSockets. Но до HTML5 нам еще далеко, хотя сокеты можно использовать уже сейчас (и мы, в следующих статьях о них обязательно поговорим, эта тема одна из приоритетных). Создавая реал-тайм приложения, работающие в веб-среде, очень и очень необходимо передавать данные, в обе стороны, при этом имея минимальные задержки и постоянно открытый канал. Именно такая задача стоит перед мною сейчас в рабочих проектах. Мы уже используем Comet (на базе Dklab_realplexor) и основной функционал нашего проекта покрывается за счет Long-polling технологии. Для чатов и обновления страницы достаточно редко (то есть минимум раз в несколько секунд, но может быть долго без событий вообще) это подходит просто отлично. Но если требуется гораздо более быстрый канал с постоянным обновлением, здесь уже все гораздо сложнее.

Мы для некоторого функционала использовали стороннее решение, вернее, библиотеку, предоставляемую поставщиком данных - там для обновлений с частотой до 30 -40 раз в секунду используется XHR Multipart, однако это очень плохо сказывается на производительности. Да и свои сложности вроде невозможности регулировать скорость потока данных, сделали такое решение неприемлемым. К сожалению, я не могу раскрыть детали и конкретные названия, однако это уже не касается технической стороны темы.

И так, стоит задача - пересылать в веб-приложение (клиенту) поток данных с регулируемой скоростью, при этом граничные требования - это постоянное соединение или минимизация потерь на реконнект, так как за это время может накопиться большое количество данных. Именно поэтому мы не рассматривали варианты лонг-поллинга - время на пересоздание подключения слишком велико, за этот период происходит до десятка событий, которые должны быть отправлены клиенту. Оставалось только реализовывать стриминг, однако надежных решений я так и не нашел, да еще и кроссбраузерных. Для слишком уж старых клиентов или с ограниченной поддержкой, можно использовать IFrame, однако у него также сложности с производительностью на таких объемах, сложности с отловом ошибок (сетевых). Но как запасной вариант для клиентов, где ничего другого нельзя примерить, это подойдет. Для всех остальных решено было использовать, вернее, снова попытаться использовать встроенные в Flash сокеты. Поскольку у нас уже была часть приложения построенная на флеше, то можно было не опасаться, что у клиента не будет проигрывателя, а значит можно заточить канал передачи на использование его возможностей.

Получилась следующая система:

  • Клиентский код работает через Flash XMLSocket, поддерживая постоянное соединение с сервером, "умный реконнект" при обнаружении ошибок, периодический keep-alive соединения - все это благодаря отличной библиотеке от Aman Gupta (http://github.com/tmm1) - jsSocket.
  • Серверная часть, входящие подключения, обслуживаются ТСP-сервером на базе Node.JS, позже, скорее всего, будет добавлена балансировка при помощи HAProxy, так, чтобы запустить несколько экземпляров сервера (по числу ядер), и раскидывать между ними клиентские подключения.
  • Для потока событий на сервере, которые передаются клиентам, применяем Publish/Subscribe механизм, встроенный в Redis. Он уже является частью нашей инфраструктуры, поэтому используется и в качестве быстрой и простой очереди сообщений. Хотя не все в текущем варианте меня устраивает, функционал достаточно ограничен (ладно, это я сгоряча, он просто простой, не требуйте от него многое), поэтому заманчиво в последствии перейти на более мощные решения очередей сообщений, используя STOMP или AMQP, хотя это сразу достаточно сильно усложняет систему.
  • Второй сервер, работающий полностью независимо от первого, занимается сбором и генерацией данных, которые и передаются клиентам. Это может быть что угодно, например, логи от разных сервисов, новости и т.п. Кстати, очень заманчиво сделать именно агрегатор логов в реальном времени, новостной клиент (RSS-ленты, используя  механизм pubsubhubbub). Да в принципе, это любой реалтайм сервис. Сервер не занимается обслуживанием клиентов, более того, он просто не знает о них ничего, зато он умеет принимать данные, классифицировать их и распихивать по очередях. Это значительно упрощает систему, да и разрабатывать можно также раздельно. Сейчас в качестве генератора данных у нас выступает удаленный сервер, передающий CVS-данные по HTTP, в будущем будут и другие источники, поэтому переключение пройдет совершенно безболезненно и даже незаметно для клиента, ведь напрямую они не пересекаются.

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

P.S. Этот материал можно обсудить в нашем форуме.

  • andrew

    Как вариант, можно попробовать установить RabbitMQ в качестве AMQP сервера, тогда можно поднять несколько кластеров rabbitmq вместо балансировки nodejs.
    Если Вам потребуется библиотека для RabbitMQ, то я как раз заканчиваю её 😉

    • Спасибо за комментарий. Я как то смотрел на него, но не сложилось — как то неудобно еще и ерланг использовать, он сходу не стал на нашем ес2 тестовом сервере, выдав старнные ошибки. думалось о ZeroMQ или Apache Qpid, но сейчас все больше и больше склоняюсь к HornetHQ.
      Но вообще интересно, я нашел одну либу для AMQP, у вас другая? Будет интересно взглянуть. Специфика проекта сейчас такая, что наверное всех возможностей протокола не надо, это в принципе просто замена самописной реализации на базе редиса — он мне очень импонирует, даже сам писал простую очередь сообщений на нем. Вот, мечтая, была бы реально простая и быстрая очередь, только STOMP доступ, персистентность, репликация и кластеризация, библиотеки для всех языков (в первую очередь — биндинги для прозрачной интеграции на стороне сервера — РНР, и прямое сопряжение с клиентом — Flash, JS) — вот это был бы предел мечтаний.

  • andrew

    кстати, WebSocket можно эмулировать при помощи этой библиотеки
    http://github.com/gimite/web-socket-js

    • да, конечно, помоему, единственный вариант. Рассматриваю веб-сокеты, правда так как они через NGinx и так не могут проксироваться, то значит выделять отдельный сервер все равно придется. А вариант с обычными сокетами флешовыми напрямую более гибкий, мне кажется — да и выбор клиентских библиотек большой. правда как быть с SSL еще не решено, но вроде это у нас обязательное требование.

  • andrew

    @aleks_raiden
    Я ставил RabbitMQ из стандартного репозитория Ubuntu, и как отдвельный Debian package. В последнем случае он требует Erlang + erlnag_nox (не спрашивайте, что это такое :))
    По поводу Qpid — крайне не рекомендую. Я видел его изнутри, «щупал» его функционал и первое впечатление — недоделка, RabbitMQ намного лучше.
    HornetQ, насколько я помню, имеет поддержку AMQP только в планах. В любом случае, это будет лишь интерфейс, как и JMS, что, скорее всего, скажется на производительности. Кстати, иметь Erlang Вас смущает, а Java — нет 😉
    Библиотека у меня своя, так как та, что на github — просто ужасна; также есть библиотека от автора node.js, она получше, но не лишена недостатков (как, впрочем, и моя тоже).
    После изучения AMQP мне он (AMQP) очень понравился — настолько грамотно всё продумано. К тому же есть клиенты и для Java, и для PHP, и для Python, и (скоро) для node.js.

  • Pingback: Tweets that mention Веб-приложения реального времени: jSocket,Node.JS, Redis, MQ. Часть 1. | Alpha-Beta-Release Blog -- Topsy.com()

  • kurokikaze

    Я недавно пытался работать с RabbitMQ и AMQP в Node, оказалось что нужные коннекторы пока в альфа-стадии, и там реализованы не все функции. Но написать свой сервер сообений или сервер очередей можно запросто. Я написал сервер очередей (http://kuroikaze85.wordpress.com/2010/05/05/nodejs-work-queue-server/) буквально в 70-80 строк кода: с подключением к нему по TCP для масштабирования, контроля выполнения заданий (невыполненные возвращаются обратно в очередь).

    В качестве пробы до сервера очередей я кстати сделал сервер сообшений, с подпиской на exchanges — эдакий упрощённый аналог RabbitMQ. Это тоже не так сложно. Обмен с сервером был по TCP, протокол на основе JSON (JSON.parse / JSON.stringify). Это отлично работало.

  • kurokikaze

    И вообще, приятно видеть новых Node-писателей 🙂 Подписался, надеюсь будут ещё интересные статьи на эту тему.

Developers.org.ua