Двунаправленный асинхронный обмен данными в веб-приложениях

    Одной из основных черт современного веба эксперты называют RIA, что часто расшифровывается как тренд, когда веб-приложения по функциональности приближены к настольным приложениям. Тем не менее, это приближение весьма условно. Подавляющее большинство «обогащеных» веб-приложений по-прежнему построены по модели «запрос-ответ». Т.е. события на стороне клиента могут быть отражены на стороне сервера, никак не наоборот. Для того чтобы реализовать такую банальную вещь как чат приходиться прибегать к изощренным уловкам. Спасибо Алексу Расселу (Alex Russell) из Dojo, у нас есть даже имя для подобной техники – Comet.

    Согласно cometdaily.com, существует несколько решений для того, чтобы эмулировать двунаправленный обмен данными между клиентом и сервером:
    • Long polling – сервер не отвечает сразу на XHR-запрос, а лишь тогда, когда в очередь приходит событие, адресованное данному источнику («вопрошающему»).
    • Forever frame – создает iframe, в который сервер постоянно дописывает события по мере их поступлений.
    • Script tags – динамически создаваемые JS-блоки, что позволяет кросс-доменные коммуникации.
    • ActiveXObject(”htmlfile”) – метод доступный в IE и использующий ActiveX
    • JSONRequest object ( www.json.org/JSONRequest.html ) — реализует двух-стороннее соединение, посредством двух одновременных запросов (один на передачу, другой на прием).

    Помимо этого возможно использование Flash объектов или Java-апплетов. И для полноты картины можно упомянуть такую древнюю технику как Forever GIF.

    От себя я бы еще добавил пару высокоуровневых решений:
    • Протокол Bayeux ( svn.cometd.com/trunk/bayeux/bayeux.html Dojo Foundation) — позволяет обмен событиями по схемам: один клиент – много серверов, один сервер – много клиентов. В частности используется в проекте CometD (www.cometd.com).
    • Протокол BOSH (http://xmpp.org/extensions/xep-0124.html XMPP standards foundation) – эмулирует двунаправленный поток данных между браузером и сервером, используя два синхронных HTTP-соединения
    • Lightstreamer ( www.lightstreamer.com ) – масштабируемый и надежный сервер для «пушинга» данных в «обогащегнное» приложение в реальном масштабе времени.
    • APE (Ajax Push Engine) ( www.ape-project.org ) – легковесный open source push-сервер, нашедший поддержку в MooTools, Dojo, jQuery. Подробнее на Хабре ( habrahabr.ru/blogs/webdev/60803).


    Как бы там ни было, любой из этих подходов в той или иной мере хак, когда хотелось бы использовать нативное решение. Ребята из рабочей группы HTML5, очевидно, хорошо понимают эту ситуацию и потому в стандарт внесен такой метод как Web Sockets. Его описание звучит как панацея: толерантен к фаерволам и роутерам, позволяет кросс-доменную коммуникацию, интегрируется с существующими HTTP балансерами нагрузки, допускает обмен бинарными данными, работает в защищенных соединениях и т.д. При всем при этом, API Web Sockets – очень простой (см. также habrahabr.ru/blogs/webdev/79038).

    image

    Ура, спасибо, все свободны? А вот и нет. В настоящий момент Web Sockets поддерживается только в девелоперской версии Chrome (начиная с 4.0.249.0 — www.chromium.org/getting-involved/dev-channel) и обещана поддержка в Firefox 3.7. Что же делать сейчас? Очевидно нужен фасад на клиентской стороне, который при наличии Web Socket использует его «родной» API, при отсутствии – обходное решение. Таким решением мог бы быть Kaazing Websocket Gateway ( kaazing.com/download ), если бы распространялся не за деньги, а по доброте душевной. Можно было бы использовать Orbited ( orbited.org ) совместно с io.js ( github.com/mcarter/js.io ), если вам непременно необходим помимо Web Sockets доступ в amqp, imap, irc, ldap, smtp, ssh, stomp, telnet, xmpp и Python на сервере проксирующий любые коммуникации. Мой выбор: WebSocket.js ( github.com/gimite/web-socket-js ). Крохотная JS библиотека и мост виде Flash-объекта.

    Теперь нам понадобится лишь сервер WebSocket и, в нашем случае (бакенд написан на Java), для этого отлично подходит Jetty, начиная с версии 7.0.1 (в настоящий момент ее статус — almost stable, но когда нас это пугало?). Если ваши серверные приложения написаны на php можно попробовать использовать phpwebsocket ( code.google.com/p/phpwebsocket ) или Phpwebsockets ( code.google.com/p/phpwebsockets ). Но на свой страх и риск так как оба решения экспериментальные.
    UPDATED: Пример реализации чата на phpDaemon habrahabr.ru/blogs/php/79377

    Уверен инсталляция Jetty не вызовет у вас никаких вопросов, но вы можете столкнуться с проблемой в WebSocket.js из-за, так называемой, cross-domain policy от Adobe. Flash-объекту надо разрешить открывать сокет на сервере. Самое надежное решение – разместить микро-сервер на порт 843, отвечающий на запрос политик соответствующим XML, как описано в www.lightsphere.com/dev/articles/flash_socket_policy.html. В статье даже представлен простеший XML-сервер на перле. Впрочем, мне он не очень понравился, и я писал свой сокет-сервер на базе примера из devzone.zend.com/article/1086.

    Теперь, когда мы располагаем Web Sockets, мы можем написать веб-приложения, способные мгновенно реагировать на события сервера. Это может быть система оповещений (notificatior) как на Facebook, это могут быть сервисы для взаимодействия пользователей в стиле Google Waves, это могут быть мониторы реального времени (например, пользователи онлайн) или же интеграция со сторонними источниками событий, таких как входящее SMS или событие в настольном приложении. Поскольку мы ожидаем самые разные события, из различных источников, направленные различным клиентам, потребуется надежная система управления потоками сообщений на сервере. Существует простой, но элегантный протокол STOMP ( stomp.codehaus.org ). В API всего несколько команд (SEND, SUBSCRIBE, UNSUBSCRIBE, BEGIN, COMMIT, ABORT, ACK, DISCONNECT) посредством которых общаются STOMP клиенты и STOMP брокеры. В частности в Zend Queue ( framework.zend.com/manual/en/zend.queue.stomp.html ) имеется адаптер STOMP. Брокер сообщений может быть реализован на базе Apache ActiveMQ ( activemq.apache.org ) или же RabbitMQ ( www.rabbitmq.com ).

    Впрочем, если вы настроены очень серьезно стоит обратить внимание на более развитый и гибкий ( habrahabr.ru/blogs/webdev/62502 ) протокол AMQP ( www.amqp.org ).

    Jetty, будучи контейнером JBOSS, обслуживает Web Sockets. Нам осталось использовать другой контейнер чтобы транслировать AMQP оповещения в Web Sockets. Для это следует установить один из брокеров для AMPQ. ZeroMQ ( www.zeromq.org ) считается самым быстрым. Qpid ( qpid.apache.org/amqp-brokers.html ) достаточно популярен, чего и следует ожидать от проекта Apache.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 52

      +6
      К сожалению, да, на сегодня поддержка WebSockets в браузерах только-только появляется.
      Я сделал небольшой тест, на котором можно проверить поддерживается ли она в вашем браузере: websockets.ru/test.html
      • UFO just landed and posted this here
          0
          Google Chrome 4.X — то есть dev-версия. В основную версию еще не перенесли.
        • UFO just landed and posted this here
          0
          Стоит добавить, что для протокола Bayeux также существует поддержка в Dojo, jQuery. И фактически данный протокол не является составной частью CometD, но безусловно используется в нем.
            0
            Ну проверить наличие WebSocket проще простого window.WebSocket либо true, либо false :)
              0
              а есть ли поддержка STOMP на стороне клиента?
                0
                я имею в виду на стороне браузера
                  0
                  Есть реализация клиента в Orbited например. Но она ужасна.
                • UFO just landed and posted this here
                    0
                    Это однозначно уловка для реализации двунаправленного обмена, что может пригодиться и для чата в том числе
                      +1
                      Просто добавлю — насчет уловки, мысль не моя, а расхожее мнение. Например, cometdaily.com/2007/12/11/the-future-of-comet-part-1-comet-today/ — Comet is a giant hack
                        0
                        Согласен.
                      +1
                      при всем уважении к автору, который видимо разбирается в теме, статья нечитабельна. Имхо лучше не вываливать 150 технологий в один пост.
                        +1
                        Вводная статья по теме: WebSockets — полноценный асинхронный веб.

                        ЗЫ. Буду рад фидбеку.
                        • UFO just landed and posted this here
                            0
                            К сожалению, вынужден с вами согласиться. Здесть проблема в том, что WebSockets это очень сильное расширение HTTP в первую очередь в плане идеи. Попытка воспринимать его привычно и приводит к таким недопониманиям. Поэтому нужно время и серия хороших статей, чтобы разработчики прочувствовали всю прелесть WebSockets.
                          +1
                          С другой же стороны для новичков станет сразу понятно куда двигаться когда будет столько новых слов на одной странице имеюзих отношение к одной теме.
                          0
                          Большой плюс WebSockets в его простоте. Эта простота состоит из двух вещей:
                          — не нужно наворачивать велосипед на помед массу протоколов и хаков, чтобы добиться желаемого результата — я имею ввиду Comet и Bayuex.
                          — на клиенте все делается очень просто — вы получили сообщение — вы его обработали. В большинстве случаев нам даже не нужно усложнять себе жизнь и тащить сюда Stomp и Ampq.

                          Давайте подумаем, что такое «подписка» в терминах Stomp? По сути это выраженное желание пользователя получать информацию по какому-то каналу, на который он «подписывается». Как это будет звучать в терминах WebSockets? Просто открыв веб-сокет на определенный УРЛ! И этого достаточно для 90% приложений.
                          Дело в том, что с приходом веб-сокетов мы уже можем легко выйти за ограничения 2 коннектов на домен. Раньше мы были вынуждены мультиплексировать все данные по одному каналу (привет, Bayeux). Поэтому, нам надо в каждом сообщении иметь некий признак откуда оно пришло.
                          Но теперь в этом нет необходимости: мы просто открывем столько каналов сколько нам надо. Если мы открыли сокет на какой-то урл — то это и значит, что мы на него «подписались». Причем, обратите внимание6 информация из этого сокета будет сразу попадать в нужное место. Нам не надо определять из какого канала она пришла и направлять ее нужному классу/объекту/функции. Теперь это само собой будет правильно роутиться!
                            0
                            Здесь есть одно маленькое Но.
                            Серверу может поплохеть от количества коннектов, например память ядра для буферов соединений закончится, или как вариант закончится количество соединений разрешённое ядру, или CPU завершится на управлении коннектами.
                            В общем на сколько нибудь живых проектах это может стать существенной проблемой требующей внимания.

                            Как оптимизацию производительности серверной части, я рассматриваю использование одного канала с подписками.
                              0
                              Да, но мне кажется, что кол-во соединений будет такое же, только теперь они будут жить внутри сервера.
                              • UFO just landed and posted this here
                                0
                                По сути STOMP или AMQP не обязательны, но с одним из них можно реализовать event-driven architecture и вовлечь в нее клиент через Web Sockets, не заботясь ни о семантике потоков сообщений, ни о масштабируемости системы в целом. В случае полноценного стартапа, а не отдельно взятой задачи — Message Queue это то что надо. А если система еще и гетерогенная, то без «очереди» вообще не обойтись.
                                +1
                                Кстати, а насколько сложно будет пользоваться веб сокетами не из браузера? Есть ли какие-то библиотеки для работы с ними?
                                  0
                                  По-моему никаких проблем — еще глядишь в curl какой-нибудь добавят. А уж в скриптовые языки и подавно, как только программисту более менее серьезного уровня это понадобится.
                                    0
                                    Здесь требования к клиенту tools.ietf.org/html/draft-hixie-thewebsocketprotocol-68#page-14
                                    В указаном примере WebSocket.js (http://github.com/gimite/web-socket-js/blob/master/flash-src/WebSocket.as) представлена имплементация на Actionscript
                                    0
                                    Собственно единственная проблема, которая все портит…
                                    А как быть с клиентами, которые выходят в инет через прокси, настроенный злобными админами, закрывшими соединения по всем портам кроме 80?
                                      0
                                      а разве тут не через 80 порт всё идет?
                                        0
                                        Сross-domain policy, к сожалению лезет через 843й порт.
                                          0
                                          Да. Это имхо недоработка в самом флеше.
                                            0
                                            Для таких ситуаций остается только вариант сервер-пуша через обычное не закрываемое сервером HTTP-соединение.
                                              0
                                              На другой хост?
                                            0
                                            С другой стороны, если вы коннектитесь к своему домену, то эта политика не должна вызываться и мешать.
                                            Кто спец по флеш сокетам, поправьте меня плз.
                                              0
                                              Насколько я помню, в любом случае флешом запрашивается файл crossdomain.xml. Причём сначала на порт, на который идет дальнейший коннект. В принципе проблема решаемая, и у меня работало, когда я изобретал подобный велосипед.
                                              Но а вот через фаервол, почему-то пробиться не получилось. В чём была проблема, вспомнить не могу.
                                              Так что мне тоже, хочется услышать мнение флеш специалистов по этому поводу.
                                                0
                                                Можно ли его отдавать с самого порта, куда идет подключение?
                                                Походу нет.
                                                Или как-то указать в параметрах флешки или еще где-то, что файл может быть запрошен с другого порта?
                                                  +1
                                                  С порта куда идет подключение, отдавать можно.
                                                  0
                                                  The crossdomain.xml file affects HTTP, HTTPS and FTP access to content on your webserver. (You can read more about it here.) This file has no effect on socket connections. You must set up a socket policy server to allow Flash-based socket access.
                                                  www.lightsphere.com/dev/articles/flash_socket_policy.html
                                                    0
                                                    То есть для сокетных коннектов единственный вариант — спец. сервер на 843 порту?
                                            0
                                            Так даже сам вебсокет сервер биндится на 10081 порт например.
                                            И туда соединяются клиенты.
                                            Правда если это станет общепринятым стандартом, я думаю 10081 будут открывать также как и 80.
                                          0
                                          поправьте, пожалуйста, опечатку Flesh-объекту -> Flash-объекту
                                            0
                                            Thx!
                                            –1
                                            автор, в соседнем топике есть реализация чата на вебсокетах, ссылку неплохо бы в статью добавить
                                              0
                                              Done. MongoDB? Респект. Мы рассматривали такой вариант, но потом выбрали Hadoop как DFS и собираяемся использовать HBase.
                                            0
                                            Какая-то каша у вас в голове.

                                            > Long polling
                                            Long polling отличается от HTTP Stream тем, что соединение открыто до тех пор, пока не придет первый ответ от сервера, после чего сам сервер соединение и закрывает. Клиент тут же открывает новое. HTTP Stream дежит соединение постоянно, разрывается оно только из-за проблем с сетью. Лонг поллинг нужен, например, что бы обходить всякие антивирусы, которые не отдают браузеру http запрос до тех пор, пока он не будет закрыт.

                                            > script tags или scriptTagProxy
                                            Две совершенно разных вещи, первая называется jsonp, нужна поддержка сервера на который идет запрос, а вторая проксирует запрос через специальный сервер, который уже и делает запрос.

                                            > JSONRequest object ( www.json.org/JSONRequest.html ) — реализует двух-стороннее соединения, посредством двух одновременных запросов (один на передачу, другой на прием).

                                            Ну а это просто бред.
                                            • UFO just landed and posted this here
                                              0
                                              Вчера написал простенький чат на JS в стиле Google Waves (участники видят набор текста друг друга в реальном времени). Работает одинаково шустро в Chrome 4 (WebSocket) и FireFox 3.5/Opera 10 (эмуляция)
                                              • UFO just landed and posted this here
                                                • UFO just landed and posted this here
                                                  • UFO just landed and posted this here

                                                Only users with full accounts can post comments. Log in, please.