Comet
Уже приготовились мыть посуду? Что ж, придется Вас разочаровать, сегодня речь пойдет вовсе не о моющем средстве, а об одноименной технологии.
Comet представляет собой архитектуру веб-приложений, основной особенностью которой является тот факт, что отправка данных от сервера к клиенту (в роли которого обычно выступает браузер) не требует какого-либо запроса данных со стороны клиента. Это позволяет пользователям приложения более оперативно реагировать на возникающие на сервере события и быть в курсе процесса работы приложения без необходимости непрерывно опрашивать сервер с помощью веб-клиента.
Реализация этой технологии, как не сложно догадаться, основывается на JavaScript. Основной идеей является поддержание долговременных HTTP-соединений с каждым клиентом приложения и отправка новых данных клиентам с их помощью как только произошло их обновление или возникновение на сервере. Этот принцип послужил основой для альтернативных названий этой технологии: Server-push, Reverse AJAX. В случае классического AJAX (о котором тоже стоило бы сначала написать статейку, потом может быть соберусь) клиент асинхронно отправляет серверу запрос на получение какой-либо конкретной информации. В Comet же клиент с сервером как бы меняются ролями: инициатором передачи информации является сервер, а не клиент. Что же это меняет? Самым наглядным примером послужит реализация постоянного обновления какой-либо информации в реальном времени:
- Классическая модель отправки запросов. Для решения задачи понадобится постоянно обновлять всю страницу целиком, вручную пользователем или альтернативными способами автоматически. Совсем не вариант, будет генерироваться огромное количество запросов к серверу, каждый из которых будет очень существеннен по объему. Большое количество одновременно работающих пользователей такое приложение явно не выдержит, да и доступность данных в реальном времени не обеспечит.
- AJAX. По сравнению с предыдущим вариантом AJAX предоставляет массу преимуществ: размер передаваемых данных существенно уменьшается, особенно если использовать в качестве формата данных не XML, а JSON. Но тем не менее необходимость регулярно отправлять запросы серверу для получения обновленной информации останется. С ростом количества пользователей будет расти и количество запросов, открытие и закрытие которых будет неуклонно генерировать нагрузку на сервер. Для снижения нагрузки можно попытаться увеличивать интервал между запросами, но это лишь временная мера, обладающая существенным недостатком — увеличение этого промежутка времени повлечет за собой рост задержек между обновлением информации на сервере и обновлением клиентской части приложения.
- Comet. С каждым клиентом поддерживается постоянное HTTP-соединение, как только данные на сервере обновились — сервер сразу же отправляет по уже открытому соединению уведомление о необходимости провести изменения в клиентской части, это позволяет существенно сократить нагрузку на сервер и практически избавиться от задержек между обновлением данных на сервере и клиенте.
Для написания серверной части приложения может использоваться практически любой язык программирования, наиболее распространенным решением, пожалуй, является Java Servlet, запущенный в каком-либо контейнере — Apache TomCat, Jetty или Sun GlassFish. Для реализации этого подхода к организации общения клиента с сервером написано достаточно большое количество framework’ов и библиотек, наиболее полный список, я думаю, можно найти в английской википедии.
Но и для Comet-приложений рано или поздно возникает вопрос масштабируемости, так как традиционные HTTP-сервера создают новый поток (thread) для обслуживания очередных нескольких очередных новых соединений. Каждый поток в состоянии обрабатывать только небольшое количество HTTP-соединений, а так как в случае с Comet соединения находятся в открытом состоянии неопределенно долго, для каждых нескольких новых пользователей веб-приложения приходится создавать новый поток. Количество одновременно существующих потоков не безгранично, что в один прекрасный момент приводит к существенному росту издержек, связанных с созданием новых и управлением существующими потоками, а также все чаще и чаще возникающим отказам потокам в предоставлении серверу необходимых вычислительных. В таких ситуациях некоторые пользователи встречаются с неприемлимыми задержками в работе приложения или непредвиденными сообщениями об ошибках. Наиболее простым и в то же время эффективным решением подобной проблемы является горизонтальное масштабирование Comet-серверов с балансировкой нагрузки на программном или аппаратном уровне.
Если Вас заинтересовала эта тема — могу посоветовать взглянуть на пару достаточно интересных статей, в котором более с практической точки зрения описывается этот подход к построению приложений:
- Создание масштабируемых Comet-приложений с использованием Jetty и Direct Web Remoting
- Asynchronous HTTP and Comet Architecture (eng.)
Я же надеюсь написать статью более практической на направленности по этой теме лишь в обозримом будущем, не пропустить этот момент Вам поможет абонемент.
19 comments
Женя о Comet’е писал, с примерами.
а как установить долговременное хттп-соединение?
я так думаю если сервер тупо не будет отвечать некоторое время
, то такое соединение отвалится по таймауту…
Здравствуйте.
Технология действительно интересная НО идея не нова (что не удивляет).
Проблема которую Вы описываете — один keep-alive коннекшин = одному потоку, не может быть решена в рамках, например, стандартного Servlet API и какого нибудь Tomcat’а. Однако, кто мешает иметь множество постоянно открытых соединений и фиксированное количество потоков их обслуживающих? Кстати, недавно именно о таком подходе и писал http://neuronus-javax.blogspot.com/2008/03/neuronus-channels.html
Можно еще поковырять в сторону NIO каналов, но не все там так гладко (особенно под нагрузкой).
[quote comment="410"]Женя о Comet’е писал, с примерами.
[/quote]Спасибо за ссылку по теме, лишней здесь явно не будет 
[quote comment="411"]а как установить долговременное хттп-соединение?
я так думаю если сервер тупо не будет отвечать некоторое время
, то такое соединение отвалится по таймауту…[/quote]Если честно не понял, что Вы хотели услышать в ответ, попробуйте по ссылкам сходить, если Вам нужны примеры реализации, или можно в исходниках opensource framework’ов или библиотек покопаться.
[quote comment="412"]Здравствуйте.
Технология действительно интересная НО идея не нова (что не удивляет).
Проблема которую Вы описываете — один keep-alive коннекшин = одному потоку, не может быть решена в рамках, например, стандартного Servlet API и какого нибудь Tomcat’а. Однако, кто мешает иметь множество постоянно открытых соединений и фиксированное количество потоков их обслуживающих? Кстати, недавно именно о таком подходе и писал http://neuronus-javax.blogspot.com/2008/03/neuronus-channels.html
Можно еще поковырять в сторону NIO каналов, но не все там так гладко (особенно под нагрузкой).[/quote]Здравствуйте,
изменение алгоритма организации потоков, по-моему, можно рассматривать лишь как временную меру. Фиксированный pool потоков даст лишь более равномерное распределение задержек между пользователями, но при превышении количества соединений над количеством потоков в pool’е в X раз все равно производительность станет неприемлемой.
«Фиксированный pool потоков даст лишь более равномерное распределение задержек между пользователями, но при превышении количества соединений над количеством потоков в pool’е в X раз все равно производительность станет неприемлемой.»
Конечно, но Вы не учитываете того факта существуют очереди (буфера) которые сглаживают нагрузку до приемлемых (по мнению сервера) величин . (+) — при ассиметричной схеме (как в Comet) большее влияние имеет всетаки количество трафика от сервера к клиентам, а не соотношение количества очередей к количеству потоков.
P.S.
Описываемых (точнее ожидаемых) Вами проблем — не было.
Эта схема достаточно успешно была опробована и применена на боевой системе
[quote comment="415"]Эта схема достаточно успешно была опробована и применена на боевой системе
Описываемых (точнее ожидаемых) Вами проблем — не было.[/quote]Именно Comet? И какие задачи решало(ет) приложение, не отказался бы посмотреть, если это возможно.
«Именно Comet? И какие задачи решало(ет) приложение»
Нет, не Comet, а схема с переменным количеством каналов (коннектов) и фиксированным количеством потоков
Сервера решали задачу online трэйдинга для NASDAQ.
[quote comment="417"]«Именно Comet? И какие задачи решало(ет) приложение»
Нет, не Comet, а схема с переменным количеством каналов (коннектов) и фиксированным количеством потоков
Сервера решали задачу online трэйдинга для NASDAQ.[/quote]
То, что описанная Вами схема работоспособна ничуть не меньше динамического выделения потоков, было очевидно даже в теории, но вот как оно может увеличить количество одновременно подключенных к Comet-серверу пользователей я себе плохо представляю. Хоть потоков и будет приемлимое количество, но от этого каждый из них не сможет обрабатывать лишних пользователей… Видимо эт на практике надо пробовать, так что-то определенное сложно сказать (по крайней мере мне)…
Не знаю как на Яве, но на моих основных языках программирования (Python & C++) существуют вполне приличные фреймворки для асинхронной обработки сокетов в неблокирующем режиме. В предложенном подходе есть возможность сократить время отклика. Или перенаправить его в дополнительный поток, получив ответ после затратных вычислений/обращений к БД (что используемые мной фреймворки отлично умеют делать).
Самый главный вопрос: количество возможных открытых сокетов весьма ограничено — по одному на порт, которых в IPv4 65536 на один IP, и не далеко не все доступны.
Итого имеем 50 000 клиентов, раз в час интересующихся состоянием дел (ответ занимает по вычислениям десятые доли секунды. Он серверное время говорит, чтоб уж совсем смешно было). И все, приехали — сервер нужно горизонтально масштабировать. Сервер ничего не делает, но свободные порты уже закончились.
Или, что еще хуже — каждый клиент открывает несколько соединений: спросить дядю Васю для правой части страницы и тетю Соню для левой. Причем Васю нужно спрашивать каждую минуту, а для Сони хватит и получаса.
Товарищ Светлов, скажите пожалуйста, а сколько IP можно повесить на один сетевой интерфейс?
[quote comment="429"]а сколько IP можно повесить на один сетевой интерфейс?[/quote]
По-моему вполне очевидно, что один… хотя все упирается в смысл, вкладываемый в слово «повесить».
Один? Совсем один?
#ifconfig
wlan0 Link encap:Ethernet HWaddr 00:1D:60:93:D2:30
inet addr:192.168.1.77 Bcast:192.168.1.255 Mask:255.255.255.0
wlan0:0 Link encap:Ethernet HWaddr 00:1D:60:93:D2:30
inet addr:192.168.3.177 Bcast:192.168.1.255 Mask:255.255.255.0
Как видите, на одном сетевом интерфейсе висит два IP. Теоретически, можно повесить хоть сотню IP, и, следовательно, получить порядка 5 миллионов портов для коннектов
[quote comment="437"]Как видите, на одном сетевом интерфейсе висит два IP. Теоретически, можно повесить хоть сотню IP, и, следовательно, получить порядка 5 миллионов портов для коннектов
[/quote]
Ну да, я именно что-то подобное и имел ввиду под второй частью моего предыдущего комментария.
[quote comment="419"]Не знаю как на Яве, но на моих основных языках программирования (Python & C++) существуют вполне приличные фреймворки для асинхронной обработки сокетов в неблокирующем режиме. В предложенном подходе есть возможность сократить время отклика. Или перенаправить его в дополнительный поток, получив ответ после затратных вычислений/обращений к БД (что используемые мной фреймворки отлично умеют делать).
Самый главный вопрос: количество возможных открытых сокетов весьма ограничено — по одному на порт, которых в IPv4 65536 на один IP, и не далеко не все доступны.
Итого имеем 50 000 клиентов, раз в час интересующихся состоянием дел (ответ занимает по вычислениям десятые доли секунды. Он серверное время говорит, чтоб уж совсем смешно было). И все, приехали — сервер нужно горизонтально масштабировать. Сервер ничего не делает, но свободные порты уже закончились.
Или, что еще хуже — каждый клиент открывает несколько соединений: спросить дядю Васю для правой части страницы и тетю Соню для левой. Причем Васю нужно спрашивать каждую минуту, а для Сони хватит и получаса.[/quote]
Я креведко или сервер прослушивает только один порт?
Первым делом хочу сказать большое спасибо за представленную информацию, зачитываюсь. По поводу Comet. Мне кажется тут изначально неправильный подход выбран, так как HTTP не предназначен для такого рода применения. Как альтернативу предлагаю посмотреть на XMPP. Тема раскрыта здесь.
[quote comment="530"]Первым делом хочу сказать большое спасибо за представленную информацию, зачитываюсь. По поводу Comet. Мне кажется тут изначально неправильный подход выбран, так как HTTP не предназначен для такого рода применения. Как альтернативу предлагаю посмотреть на XMPP. Тема раскрыта здесь.[/quote]Спасибо за интересную ссылку, почитал. Может быть даже не досуге попробую и напишу об этом.
[quote comment="419"]Не знаю как на Яве, но на моих основных языках программирования (Python & C++) существуют вполне приличные фреймворки для асинхронной обработки сокетов в неблокирующем режиме. В предложенном подходе есть возможность сократить время отклика. Или перенаправить его в дополнительный поток, получив ответ после затратных вычислений/обращений к БД (что используемые мной фреймворки отлично умеют делать).
Самый главный вопрос: количество возможных открытых сокетов весьма ограничено — по одному на порт, которых в IPv4 65536 на один IP, и не далеко не все доступны.
Итого имеем 50 000 клиентов, раз в час интересующихся состоянием дел (ответ занимает по вычислениям десятые доли секунды. Он серверное время говорит, чтоб уж совсем смешно было). И все, приехали — сервер нужно горизонтально масштабировать. Сервер ничего не делает, но свободные порты уже закончились.
Или, что еще хуже — каждый клиент открывает несколько соединений: спросить дядю Васю для правой части страницы и тетю Соню для левой. Причем Васю нужно спрашивать каждую минуту, а для Сони хватит и получаса.[/quote]
Ну тут вы не правы. Взгляните на фотолинейку на mamba.ru (нужно залогинится чтобы увидеть). Это flash, который держит постоянное соединение с сервером. Серевер каждую секунду отсылает информацию о пришедшей фотке всем клиентам. В пиковые нагрузки чило коннектов приближается к 80 тыс. Сервер один и обрабатывает все по одному порту. Клиент идентифицируется парой Ip-адрес+порт. Сервер однопоточный на не блокируемых сокетах, помимо всего прочего отрабатывает запросы от php-клиентов (до 2000 соединений ~1000 запросов в секунду). Вобщем грамотно вылизанный демон (на С++ разумеется) может держать до 100 тыс. соединений (может и больше — не проверял) и до 100тыс запросов в сек. обрабатывать на серваке средней крутости.
Спасибо за Comet, а то я уже было собирался свою технологию изобретать (связка невидимого flash-контроллера держащего постоянное соединение с сервером по FlashRemoting, который уведомляет JavaScript при наступлении события). Я плоховато знаю JavaScript, но был уверен что HTTPRequest может принять только один ответ сервера — потом закрывается. После беглова просмотра статей, как я понял, проблема то не решена.
Resin поддерживает Comet.
http://www.caucho.com/resin-3.1/examples/servlet-comet/
Был использован в реальной разработке, все работоспособно.
Но ServletController там не обычный а CometController, с возможностью засыпания.