Long polling что такое

HTTP Long Poling в общем работает следующим образом:

1. Клиент обращается к серверу с запросом

2. Сервер предоставляет ответ, оставляя при этом соединение открытым

3. Как только на сервер поступает новая информация, адресованная клиенту, информация направляется к нему используя открытое соединение. Пакеты, отправляемые таким образом называются push notifications


Как только клиент получает информацию, он вновь отправляет запрос и устанавливая соединение с сервером, далее процесс повторяется.

Фактически, создается подобие модели при которой запросы генерируются сервером. Клиент всегда готов принимать HTTP запросы, которые отправляет сервер.

При использовании Server-Sent Events (HTTP Long Poling) в приложении всегда возникает ряд моментов и потенциальных сложностей, основные из них
  • Использование WiFI и сетей мобильных операторов с постоянным переключением между ними — при этом меняется IP адрес и разрывается соединение
  • Сообщения могут теряться если клиент недоступен, система должна быть организована так, чтобы все сообщения доходили, при этом в нужном порядке
  • Балансировка нагрузки между серверами, обеспечивающими Long Poling

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

Приложения, требующие использования технологий передачи данных, инициализируемых сервером обычно используют AJAX запросы Javascript или написаны на Ruby/Python.

Есть сайт на Laravel с посещаемостью real-time в 700-1000 человек. Ранее сайт использовал чат стороннего разработчика. Он использовал WebSockets.

Всё было прекрасно, пока в один прекрасный момент разработчик чата отказался его поддерживать в связи с высокой нагрузкой. С этого момента начались поиски альтернативных чат-систем…

Выбор технологии

Многие скажут, что сейчас уже никто не использует старые браузеры типа Опера12 или ИЕ8, однако таких людей пока еще довольно таки много, поэтому решено было выбрать именно Long Polling.

Изначально сайт был размещен на Shared hosting, из-за чего был огромный ряд ограничений. Тем не менее, хостинг справлялся с нагрузкой. Это радовало. И единственной возможной реализацией чата был long polling на PHP.

Реализация

От слов к делу

Принцип понятен и прост. Аяксом посылается запрос на сервер, который считывает сообщения. Длительность запроса, а точнее ответ от сервера, зависит от того, добавил ли кто-то сообщение. То есть, после запроса запускается цикл, который проверяет, нет ли новых сообщений, и если есть новое сообщение — цикл прерывается, возвращается ответ с новым сообщением. После получения ответа запрос повторяется. И так далее.

Реализовать такую штуку на PHP проще не куда. Я потратил на реализацию в сумме около 3х часов своей жизни.

Проблемы

Тем не менее, оставил его в стадии MVP (Minimum viable product). И, как оказалось, не зря. При теситровании функционала на продакшн сайт просто падал. Оперативка заполнялась в течение 1-2 минут и гасила сервер.

Подумав немного, я решил, что дело в сервере. И спустя некоторое время сайт был перенесен на VPS. Скажу сразу, что конфигурация слабенькая, и оперативки всего 1Гб. Тем не менее, на VPS чат держался 3-4минуты. Уже лучше.

В итоге, после длительных раскопок интернета я понял, что писать чат на PHP — бессмысленно, если нагрузка на него будет больше, чем 10 человек.

Осталось 2 варианта:

  1. Ставить nodeJS и писать чат на веб-сокетах
  2. Писать сервис на чем-то другом

И как вы думаете, что я выбрал? Конечно же второй вариант, иначе бы не писал эту статью.

Поскольку C++C реализаций серверных приложений не так уж много, тем более кросс-платформенных, и тем более, что я не силен в Сях, я выбрал Lazarus. Хорошо, что у него есть такой компонент как FPHTTPServer и пример реализации приложения.
Оговорюсь еще о том, что у меня нет под рукой Linux. Все приложения я делаю на винде. Я просто был уверен что перекомпилировать приложение на другой системе — это раз плюнуть. Ведь так же гласит девиз! «Write once — compile everywhere!». Впрочем, мне это удавалось, когда на моем ноуте было 2 системы (win7 + linux mint).

Читайте также:  Что означает tbt в инстаграме

Скомпилировав приложение на win, я проверил функцонал локально. Все работало как часы. Тем не менее, это был простой exe-шник и хотелось что-то больше — написать нормальный сервис типа apache. Lazarus — это вам не Delphi, хоть и очень похож. Во-первых он бесплатный, что автоматически вызывает подозрение относительно качества продукта. А во-вторых,… впрочем как и в-третьих и еще н-ное количество — все неудобства связаны с этим. Большинство вопросов на форуме лазаруса так и остаются без ответа. Это очень печально. Так же как и отладка приложения с выскакивающей ошибкой типа «uncatchable error». Но, к счастью по окончанию недельного срока — я завершил аналог того функционала который был написан на PHP за 3 часа.

Довольный как слон, я решил скомпилить приложение на линуксе. Залогинившись под root на сервер и выполнив команду apt-get-install lazarus — я быстро поставил лазаря, скопировал исходники и, с помощью отрытых ранее в интернете команд lazbuild -r project.lpi, скомпилировал приложение. Это был единственный, поистине, самый удачный и быстрый этап.

Проверив работу чата уже на реальной нагрузке, я понял, что моя идея была оправдана. Чат работал, нагрузка на сервер увеличилась не намного. Это радовало. Теперь я мог вернуться на винду и продолжить работу над улучшенной версией — сервис (daemon). Как и следовало ожидать — на винде все получилось, хоть и с небольшой задержкой по времени.

На линуксе проект не скомпилился. При компиляции выдавалась ошибка о том, что не найден юнит Interfaces. Это было очень странно. На форуме лазаря внятного ответа так и не появилось. Многие лишь писали о том, что при переустановке все заработало. Однако переустановка не помогала. В конце-концов я понял, что моя версия лазаря отличается от той, которая стоит на линуксе. Обновить репозиторий не получилось. Точнее репозиторий обновился, а вот лазарь так и остался прежней версии.

Тогда я попытался скачать установочный пакет с помощью wget. В результате вылезла ошибка 177 (точный номер уже не помню). После очередного выплеска эмоций я понял, что проблема в каком-то символе в ссылке. Я перелил установочный пакет на MS OneDrive и создал короткую ссылку, после чего успешно скачал файл.

Далее я удалил старый лазарус и поставил новую версию. И как вы думаете, что сообщил компилятор? Конечно же! Непонятные ошибки!

Опираясь на прошлый горький опыт с поиском ответов в интернете я начал изучать конфиги лазаря. Проблема оказалась проста — при установке лазарус почему-то не создал новую папку с конфигами. Папочка осталась от предыдущей версии, хотя я удалял старый лазарус с помощью apt-get remove –purge. Ну что ж, я забекапил старые конфиги, удалил папку и снова попытался перекомпилить Lazarus IDE. Магическим образом, после этого папка с конфигами создалась и я смог наконец скомпилировать даймон для линукс.

Победа! Или нет?

Сервисное приложение успешно скомпилировалось, но как же его теперь запустить?

Как и следовало ожидать — простой скрипт для создания записи в service start… не сработал. Более того, я опять же нашел посты на форуме с моей проблемой без ответа.

Поковыряв еще часик интернет, я нашел как можно запустить сервис с помощью start-stop-daemon. В итоге я понял, что код скрипта запуска, который выложили в примерах лазаруса — работает, но только если запускать его из терминала. В противном случае он выдавал что-то типа «не найден какой-то файл».

Финиш

Вы можете помочь и перевести немного средств на развитие сайта

Поиск по форуму
Расширенный поиск
К странице.

Другое название способа – "Очередь ожидающих запросов". Краткая схема такова:

  1. Отправляется запрос на сервер
  2. Соединение не закрывается сервером
    • пока не появится событие
    • Событие отправляется в ответ на запрос
    • Клиент тут же отправляет новый ожидающий запрос
    Читайте также:  Микро atx материнская плата

    Каждый пакет данных, таким образом, означает новое (не учитывая Keep-Alive) соединение,
    которое будет открыто столько, сколько нужно, пока сервер не решит прислать информацию.

    На практике, соединение обычно переустанавливается раз в 20-30 секунд, чтобы избежать возможных проблем, например с HTTP-прокси.

    В отличие от простого поллинга, здесь уведомление о событии приходит гораздо быстрее.

    Такие задержки вполне терпимы в случае, если событий немного, и совершенно незаметны, если обновления с сервера приходят раз в минуту и реже. При активном чате и больших сетевых задержках ("большой ping"), они уже более ощутимы.

    • Задержки между событием и уведомлением
    • . но не такие как в поллинге
    • Входящий трафик на сервер

    Этот метод достаточно удобен в реализации, лишь немного сложнее простого поллинга.

    В качестве транспорта можно использовать что угодно – от XMLHTTPRequest до тегов script. Все просто, поэтому пример не рассматриваем.

    Как я понимаю это способ избавиться от недостатков в реализации XMLHTTPRequest – сброс/очистка устаревшего содержимого в response?

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

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

    Например в php файле в начале мы ставим:

    Таким образом скрипт может выполняться например час. И в течении часа браузер будет выводить поступающие данные.

    Так почему мы не можем сделать такой запрос через ajax , и обрабатывать данные по мере поступления? Проблема в том, что тот же jquery позволяет вызывать callback функцию только после полной загрузки документа.

    а можно привести пример такого запроса?
    желательно на примере чата))

    Обычный запрос на сервер. Запрос идет на демон чата, демон чата отвечает на запрос только когда приходит сообщение.

    А как же timeout? Если сообщений долго не приходит соответсвенно и демон чата тоже долго не отвечает, выйдет лимит времени и ошибка, прийдется переподклбючаться.

    Да, именно так. Так что раз в 30 секунд по-любому новый запрос делается на сервер.

    ИМХО оптимальнее отслеживать разрыв соединения и только тогда восстанавливать связь.
    30 сек. это очень маленький интервал – да когда есть прокся это оптимально, но если сервер может ждать и дольше

    И сколько в среднем длится простой соединения, пока сервер его не закрывает?
    и что в течении этого времени предлагается делать? циклически проверять наличие обновлений? какой же сервер это выдержит (при чате с 50-100 посетителями онлайн)?

    Простоя нет: соединение закрывается, и тут же открывается новое. Задержка на установление соединения с сервером зависит от канала посетителя. Как правило, это в пределах 5-300мс.

    Если идет активный чат с кучей сообщений, то такой способ не очень хорош, а если чат, например, двусторонний (два человека, поэтому не так много сообщений), или просто получение событий с сервера – он работает замечательно.

    Я предполагаю, что имелся ввиду простой сервера в ожидании наступления события, т. е. каким образом серверу сделать паузу. Мне самому это очень интересно. Я использую ASP и не знаю, как сделать задержку – ни Sleep, ни setTimeout там нет. Допоможите, пожалуйста.

    А можете всё же пример написать, так проще для понимания.

    Нужен пример!

    Илья Кантор пожалуйста приведите пример!

    Пример чего конкретно привести?

    Это обычный XMLHTTPRequest.

    Лично меня интересует пример обработки long-polling запроса в Java сервере (в частности Jetty). Без написания специальных Continuation-сервлетов здесь не обойтись?

    PS: вообще учебник хорош – для начинающего в этом деле – само то!
    Но дизайн местами убивает 🙂 Какое-то время откладывал чтение данного ресурса исключительно из-за визуального отторжения.

    можете описать вот этот момент серверного скрипта

    Читайте также:  Дешевые 8 ядерные процессоры

    Соединение не закрывается сервером

    * пока не появится событие
    И в чем тогда разница этого метода от частых опросов если каждые 20-30 секунд посылается новый xmlhttprequest?

    Разница в том, что событие отсылается на клиент тут же, в виде ответа на "подвисший" XmlHttpRequest.

    Кроме того, это одно соединение каждые 20-30-100 секунд, а не одно раз в 5-10.

    Илья, спасибо все теперь понятно. Вы тут в комментариях писали (Если идет активный чат с кучей сообщений, то такой способ не очень хорош, а если чат двусторонний, или просто получение событий с сервера – он работает замечательно.)
    Что то я тут не понял разницу чата с кучей сообщений и двусторонним чатом. Могли бы вы по подробнее описать этот момент?

    Поправил соответствующий комментарий.

    Илья, Вы на мастер классах говорили, что timeout может быть сколько угодно по времени, даже несколько часов.
    Здесь Вы пишете про 30 секунд.

    Я сам пробовал на чате написанном на nodeJS сделать timeout больше 30 секунд – не вышло.
    Вот пример чата от разработчика nodeJS http://chat.nodejs.org/
    В нем тоже timeout 30 секунд.

    Возможно я Вас неправильно понял, Вы наверное имели в виду Socket.io – да
    там есть возможность держать соединение по времени столько сколько нужно.

    Можно дольше делать. Что у вас не вышло, напишите подробнее.

    Заработало).
    Я раньше тестировал в Firebug для FireFox 3.6 не работало, а сейчас работает, так здорово)))
    Даже не знаю баг это был или я что-то не правильно делал.

    Но всё таки я где-то слышал, что браузеры должны timeout держать 30 секунд, потом отключаться.
    Возможно дело в том, что я использовать кросс-доменный запросы и в качестве транспорта script.
    Может быть на script задержка в 30 секунд действует?

    2. Соединение не закрывается сервером
    пока не появится событие

    Непонятно как завершить соединение с сервером при закрытии страницы. У меня продолжает висеть соединение после закрытия страницы. Разрывать соединение обязатетьно по таймауту или есть другие способы?

    для меня не совсем понятен вопрос с сервером. в чём разница с простым AJAX запросом? толко в том что мы тянем время за которое будет возможность получить эти данные? По сути когда запрос выполняется, то скрип так-же завершается и при новом запуске всё идёт по новой? а как быть если мы пишем свой демон на C++, то мы должны плодить под каждого пользователя поток в котором будет висеть заблокировнное соединение до момента отправки данных или разрыва?

    Данный способ отлично подходит если необходимо наиболее скорейшее получение события с сервера. При обычном AJAX мы долбим сервер каждые 5-10 секунд. В таком случает если событие произошло в 1-ую секунду после запросы то клиент получит событие только через 4-9 секунд, а ведь в некоторых приложениях это большая задержка (чаты). А в случает с лонг поллингом задержка будет только в момент создания нового соединения что в худшем случает составляет 1-2 секунды а обычно на много меньше, а к тому же это только в том случает если событие произошло в момент создания нового соединения а шанс что это произойдет очень мал

    Добрый вечер. Хотел было воспользоваться данной технологией. Но на одном из форумов мне сообщили о том, будто Long poll съедает много ресурсов сервера. Так ли это?

    Да, long polling съедает ресурсы сервера при большом количестве одновременно использующих "сервис" (чят или что-нибудь другое) клиентов, хотя бы потому что приходится поддерживать большое количество TCP-соединений.

    Вообще long polling – это плохая практика. Её не следует использовать, если только вы не уверены, что вам ну уж точно он нужен.

    Ну ведь вконтакте использует его.
    Вконтакте Api

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Adblock detector