Длинные опросы
Длинные опросы – это самый простой способ поддерживать постоянное соединение с сервером, не используя при этом никаких специфических протоколов (типа WebSocket или Server Sent Events).
Его очень легко реализовать, и он хорошо подходит для многих задач.
Частые опросы
Самый простой способ получать новую информацию от сервера – периодический опрос. То есть, регулярные запросы на сервер вида: «Привет, я здесь, у вас есть какая-нибудь информация для меня?». Например, раз в 10 секунд.
В ответ сервер, во-первых, помечает у себя, что клиент онлайн, а во-вторых посылает весь пакет сообщений, накопившихся к данному моменту.
Это работает, но есть и недостатки:
- Сообщения передаются с задержкой до 10 секунд (между запросами).
- Даже если сообщений нет, сервер «атакуется» запросами каждые 10 секунд, даже если пользователь переключился куда-нибудь или спит. С точки зрения производительности, это довольно большая нагрузка.
Так что, если речь идёт об очень маленьком сервисе, подход может оказаться жизнеспособным, но в целом он нуждается в улучшении.
Длинные опросы
«Длинные опросы» – гораздо лучший способ взаимодействия с сервером.
Они также очень просты в реализации, и сообщения доставляются без задержек.
Как это происходит:
- Запрос отправляется на сервер.
- Сервер не закрывает соединение, пока у него не возникнет сообщение для отсылки.
- Когда появляется сообщение – сервер отвечает на запрос, посылая его.
- Браузер немедленно делает новый запрос.
Для данного метода ситуация, когда браузер отправил запрос и удерживает соединение с сервером в ожидании ответа, является стандартной. Соединение прерывается только доставкой сообщений.
Если соединение будет потеряно, скажем, из-за сетевой ошибки, браузер немедленно посылает новый запрос.
Примерный код клиентской функции subscribe , которая реализует длинные опросы:
async function subscribe() < let response = await fetch("/subscribe"); if (response.status == 502) < // Статус 502 - это таймаут соединения; // возможен, когда соединение ожидало слишком долго // и сервер (или промежуточный прокси) закрыл его // давайте восстановим связь await subscribe(); >else if (response.status != 200) < // Какая-то ошибка, покажем её showMessage(response.statusText); // Подключимся снова через секунду. await new Promise(resolve =>setTimeout(resolve, 1000)); await subscribe(); > else < // Получим и покажем сообщение let message = await response.text(); showMessage(message); // И снова вызовем subscribe() для получения следующего сообщения await subscribe(); >> subscribe();
Функция subscribe() делает запрос, затем ожидает ответ, обрабатывает его и снова вызывает сама себя.
Сервер должен поддерживать много ожидающих соединений.
Архитектура сервера должна быть способна работать со многими ожидающими подключениями.
Некоторые серверные архитектуры запускают отдельный процесс для каждого соединения. Для большого количества соединений будет столько же процессов, и каждый процесс занимает значительный объём памяти. Так много соединений просто поглотят всю память.
Часто такая проблема возникает с бэкендом, написанными на PHP или Ruby, но технически дело не в языке, а в реализации. На большинстве современных языков можно написать подходящий сервер, но на некоторых это проще сделать.
Бэкенды, написанные с помощью Node.js, обычно не имеют таких проблем.
Демо: чат
Вот демо-чат, вы также можете скачать его и запустить локально (если вам знаком Node.js и можете поставить модули):
Длинные опросы (long poll)
Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/xhr-longpoll.
Другое название способа — «Очередь ожидающих запросов». Краткая схема такова:
- Отправляется запрос на сервер
- Соединение не закрывается сервером
- пока не появится событие
- Событие отправляется в ответ на запрос
- Клиент тут же отправляет новый ожидающий запрос

Каждый пакет данных, таким образом, означает новое (не учитывая Keep-Alive) соединение,
которое будет открыто столько, сколько нужно, пока сервер не решит прислать информацию.
На практике, соединение обычно переустанавливается раз в 20-30 секунд, чтобы избежать возможных проблем, например с HTTP-прокси.
В отличие от простого поллинга, здесь уведомление о событии приходит гораздо быстрее.
Задержка = установление соединения + передача данных
Такие задержки вполне терпимы в случае, если событий немного, и совершенно незаметны, если обновления с сервера приходят раз в минуту и реже. При активном чате и больших сетевых задержках («большой ping»), они уже более ощутимы.
- Задержки между событием и уведомлением
- . но не такие как в поллинге
- Входящий трафик на сервер
Этот метод достаточно удобен в реализации, лишь немного сложнее простого поллинга.
В качестве транспорта можно использовать что угодно — от XMLHTTPRequest до тегов script. Все просто, поэтому пример не рассматриваем.
Автор: fast, дата: 20 марта, 2008 — 10:04
Как я понимаю это способ избавиться от недостатков в реализации XMLHTTPRequest — сброс/очистка устаревшего содержимого в response?
Можно в принципи вообще не разрывать связь, а складывать все в response и при поступлении данных их обрабатывать. ну допустим если у нас «своя сеть», а для сброса переподключаться к серверу.
Автор: __Levsha (не зарегистрирован), дата: 26 января, 2011 — 07:23
Поддерживаю вопрос: уверен что возможно обрабатывать данные по мере поступления, раз браузер может принимать такие страницы.
Например в php файле в начале мы ставим:
ob_implicit_flush(); set_time_limit(0);
Таким образом скрипт может выполняться например час. И в течении часа браузер будет выводить поступающие данные.
Так почему мы не можем сделать такой запрос через ajax , и обрабатывать данные по мере поступления? Проблема в том, что тот же jquery позволяет вызывать callback функцию только после полной загрузки документа.
Автор: constantant, дата: 17 ноября, 2008 — 17:18
а можно привести пример такого запроса?
желательно на примере чата))
Автор: Илья Кантор, дата: 18 ноября, 2008 — 10:16
Обычный запрос на сервер. Запрос идет на демон чата, демон чата отвечает на запрос только когда приходит сообщение.
Автор: Алексей_hose (не зарегистрирован), дата: 12 февраля, 2009 — 14:36
А как же timeout? Если сообщений долго не приходит соответсвенно и демон чата тоже долго не отвечает, выйдет лимит времени и ошибка, прийдется переподклбючаться.
Автор: Илья Кантор, дата: 13 февраля, 2009 — 10:27
Да, именно так. Так что раз в 30 секунд по-любому новый запрос делается на сервер.
Автор: goldserg, дата: 26 апреля, 2010 — 10:28
ИМХО оптимальнее отслеживать разрыв соединения и только тогда восстанавливать связь.
30 сек. это очень маленький интервал — да когда есть прокся это оптимально, но если сервер может ждать и дольше
Автор: Ajaxy (не зарегистрирован), дата: 20 апреля, 2009 — 00:14
И сколько в среднем длится простой соединения, пока сервер его не закрывает?
и что в течении этого времени предлагается делать? циклически проверять наличие обновлений? какой же сервер это выдержит (при чате с 50-100 посетителями онлайн)?
Автор: Илья Кантор, дата: 20 апреля, 2009 — 13:04
Простоя нет: соединение закрывается, и тут же открывается новое. Задержка на установление соединения с сервером зависит от канала посетителя. Как правило, это в пределах 5-300мс.
Если идет активный чат с кучей сообщений, то такой способ не очень хорош, а если чат, например, двусторонний (два человека, поэтому не так много сообщений), или просто получение событий с сервера — он работает замечательно.
Автор: AL_ (не зарегистрирован), дата: 18 ноября, 2009 — 16:10
Я предполагаю, что имелся ввиду простой сервера в ожидании наступления события, т. е. каким образом серверу сделать паузу. Мне самому это очень интересно. Я использую ASP и не знаю, как сделать задержку — ни Sleep, ни setTimeout там нет. Допоможите, пожалуйста.
Автор: mycoding, дата: 18 января, 2010 — 17:00
А можете всё же пример написать, так проще для понимания.
Автор: Rol1k, дата: 3 февраля, 2010 — 19:19
Нужен пример!
Автор: HelpeR, дата: 15 марта, 2010 — 19:48
Илья Кантор пожалуйста приведите пример!
Автор: Илья Кантор, дата: 15 марта, 2010 — 19:57
Пример чего конкретно привести?
Это обычный XMLHTTPRequest.
Автор: nukisman (не зарегистрирован), дата: 26 апреля, 2011 — 16:12
Лично меня интересует пример обработки long-polling запроса в Java сервере (в частности Jetty). Без написания специальных Continuation-сервлетов здесь не обойтись?
PS: вообще учебник хорош — для начинающего в этом деле — само то!
Но дизайн местами убивает 🙂 Какое-то время откладывал чтение данного ресурса исключительно из-за визуального отторжения.
Автор: HelpeR, дата: 17 марта, 2010 — 09:26
можете описать вот этот момент серверного скрипта
Соединение не закрывается сервером
* пока не появится событие
И в чем тогда разница этого метода от частых опросов если каждые 20-30 секунд посылается новый xmlhttprequest?
Автор: Илья Кантор, дата: 17 марта, 2010 — 20:30
Разница в том, что событие отсылается на клиент тут же, в виде ответа на «подвисший» XmlHttpRequest.
Кроме того, это одно соединение каждые 20-30-100 секунд, а не одно раз в 5-10.
Автор: HelpeR, дата: 19 марта, 2010 — 22:15
Илья, спасибо все теперь понятно. Вы тут в комментариях писали (Если идет активный чат с кучей сообщений, то такой способ не очень хорош, а если чат двусторонний, или просто получение событий с сервера — он работает замечательно.)
Что то я тут не понял разницу чата с кучей сообщений и двусторонним чатом. Могли бы вы по подробнее описать этот момент?
Автор: Илья Кантор, дата: 20 марта, 2010 — 01:51
Поправил соответствующий комментарий.
Автор: mycoding, дата: 11 мая, 2011 — 08:19
Илья, Вы на мастер классах говорили, что timeout может быть сколько угодно по времени, даже несколько часов.
Здесь Вы пишете про 30 секунд.
Я сам пробовал на чате написанном на nodeJS сделать timeout больше 30 секунд — не вышло.
Вот пример чата от разработчика nodeJS http://chat.nodejs.org/
В нем тоже timeout 30 секунд.
Возможно я Вас неправильно понял, Вы наверное имели в виду Socket.io — да
там есть возможность держать соединение по времени столько сколько нужно.
Автор: Илья Кантор, дата: 11 мая, 2011 — 17:20
Можно дольше делать. Что у вас не вышло, напишите подробнее.
Автор: mycoding, дата: 15 мая, 2011 — 10:28
Заработало).
Я раньше тестировал в Firebug для FireFox 3.6 не работало, а сейчас работает, так здорово)))
Даже не знаю баг это был или я что-то не правильно делал.
Но всё таки я где-то слышал, что браузеры должны timeout держать 30 секунд, потом отключаться.
Возможно дело в том, что я использовать кросс-доменный запросы и в качестве транспорта script.
Может быть на script задержка в 30 секунд действует?
Автор: Гость (не зарегистрирован), дата: 24 мая, 2011 — 07:58
2. Соединение не закрывается сервером
пока не появится событие
Непонятно как завершить соединение с сервером при закрытии страницы. У меня продолжает висеть соединение после закрытия страницы. Разрывать соединение обязатетьно по таймауту или есть другие способы?
Автор: Risa (не зарегистрирован), дата: 19 ноября, 2011 — 20:29
для меня не совсем понятен вопрос с сервером. в чём разница с простым AJAX запросом? толко в том что мы тянем время за которое будет возможность получить эти данные? По сути когда запрос выполняется, то скрип так-же завершается и при новом запуске всё идёт по новой? а как быть если мы пишем свой демон на C++, то мы должны плодить под каждого пользователя поток в котором будет висеть заблокировнное соединение до момента отправки данных или разрыва?
Автор: RigFox (не зарегистрирован), дата: 4 февраля, 2012 — 21:44
Данный способ отлично подходит если необходимо наиболее скорейшее получение события с сервера. При обычном AJAX мы долбим сервер каждые 5-10 секунд. В таком случает если событие произошло в 1-ую секунду после запросы то клиент получит событие только через 4-9 секунд, а ведь в некоторых приложениях это большая задержка (чаты). А в случает с лонг поллингом задержка будет только в момент создания нового соединения что в худшем случает составляет 1-2 секунды а обычно на много меньше, а к тому же это только в том случает если событие произошло в момент создания нового соединения а шанс что это произойдет очень мал
Автор: 1lider, дата: 30 сентября, 2015 — 16:06
Добрый вечер. Хотел было воспользоваться данной технологией. Но на одном из форумов мне сообщили о том, будто Long poll съедает много ресурсов сервера. Так ли это?
Автор: Python-программист (не зарегистрирован), дата: 13 января, 2016 — 21:46
Да, long polling съедает ресурсы сервера при большом количестве одновременно использующих «сервис» (чят или что-нибудь другое) клиентов, хотя бы потому что приходится поддерживать большое количество TCP-соединений.
Вообще long polling — это плохая практика. Её не следует использовать, если только вы не уверены, что вам ну уж точно он нужен.
Автор: Дмитрий Cian (не зарегистрирован), дата: 12 марта, 2016 — 11:50
Ну ведь вконтакте использует его.
Вконтакте Api
Polling и Long polling
Мы сейчас живем в мире realtime приложений, где для актуальной доставки информации используются быстрые и удобные socket соединения, которые позволяют отправить новое событие напрямую на устройство клиента.
Но настроить socket-соединение не всегда очень просто. Иногда это не позволяет хостинг, а иногда просто нет ресурсов на поддержку инфраструктуры, тестирование отказоустойчивости и безопасности. В таком случае можно воспользоваться поллингом.
polling — голосование, опрашивание. Само название намекает на то, что будет происходить регулярный опрос чего-то. В нашем случае, сервера.

Голосовать мы, конечно же, не будем. А вот опрашивать сервер будем еще как. Идея достаточно простая — регулярно опрашивать сервер на предмет новых изменений.
Обычный polling
Например мы пишем мессенджер и хотим доставлять пользователям новые сообщения. В случае обычного polling клиентское приложение (телефон/браузер) будет кидать запрос GET /messages каждые N секунд (например каждые 2 секунды).

Решение «в лоб» и приводит к необходимому результату — пользователь получает свои сообщения не познее 2х секунд после их появления. Но минусы достаточно очевидны:
- Надо все же ждать 2 секунды до получения некоторых сообщений
- Нагрузка на сервер неоправданно высока. Необходимо каждые 2 секунды создавать соединение с сервером, инициализировать приложение и тд. Все этоприводит к накладным расходам
Тут как раз на сцену выходит long polling
Что такое long polling?
Это все тот же polling, но уже с немного другой идеей. В рамках long polling клиент отправляет запрос на сервер с заранее заданным timeout (обычно 30 сек). Сервер обрабатывает этот запрос в режиме бесконечного цикла с указанным timeout и, в рамках цикла, ожидает необходимые для пользователя обновления. Как только обновление появляется сервер сразу же отдает ответ и закрывает соединение.

Таким образом мы лишаемся двух минусов polling и отдаем пользователю обновления с минимальной задержкой. У long polling, конечно же есть свои минусы и для решения их уже есть websockets.
Очень доступным языком о Long-Polling?
2. Запрошенная страница выполняет JavaScript, который запрашивает файл от сервера.
3. Сервер НЕ реагирует на запрошенную информацию и ждет, пока не появится новой информации
4. Когда появляется новая информация, сервер отсылает ее клиенту
5. Клиент получает новую информацию и НЕМЕДЛЕННО отсылает другой запрос серверу, запуская процесс ожидания на нем снова.
Что значит 3 и 4 пункты? В каком смысле, ждет пока не появится новой информации? Я лично вижу это так:
Хочу сэмулировать, чтобы понять.
Пишет человек в чат:
First User: Привет (это сообщение отправлено на сервер, но не реагирует на это сообщение, то есть Second User это сообщение не получает?)
First User: Еще раз привет! (А вот это сообщение уже доставлено Second User, т.к. появилась новая информация, и можно отослать клиенту ее?)
Second User: Привет! (и тут также, сообщение отправлено, но не реагирует сервер на сообщение, пока он не напишет еще чего то?)
Можно немного подробнее пояснить? в таком же примере, потому что из статей я что-то ничего не понял. Спасибо)
- Вопрос задан более трёх лет назад
- 28069 просмотров
1 комментарий
Средний 1 комментарий