long polling на php

Long-polling простейший пример

Доброго времени читатель.
Если тебя заинтересовал пример реализации реал-тайм приложений, и ты не мог найти нормального примера реализации long-polling то ты попал куда нужно.

Когда-то я тоже не мог найти примитивнейшего примера и таки сделал его сам.

На сколько ты знаешь, принцип long-poll простой и эффективный.
Работает по принципу. Держу соединение с сервером пока не получу каких-то данных, вот та самая избитая схема

long polling на php. Смотреть фото long polling на php. Смотреть картинку long polling на php. Картинка про long polling на php. Фото long polling на php

Приведу немного примитивного кода, показывающий основные принципы работы. Начнем реализацию примера, ограничемся 4-мя файлами

1. message.php — файл выполняющий роль клиента
2. im.php — файл выполняющий роль проверки новых данных и ответ клиенту
3. check.php — файл отдает изменения
4. im.txt — файл выполняет роль, места где мы считываем данные, новые или старые

Приведем сразу пример содержимого и его описания для первого файла
Содержимое файла message.php

На этой странице мы сразу считываем содержимое файла im.txt и выводим в блок #content. Далее объявляем js функцию check() которая с заданным интервалом в 1 секунду сама себя вызывает, но принимает каждый раз в себя аргумент timestamp. Запрос мы посылаем на im.php

Содержимое файла im.php

И здесь все предельно просто, крутим бесконечный цикл, даем условие его прекращения в двух случаях:

1. Это когда данные пришли и нужно их отдать клиенту
2. Это когда время ожидания новых данных истекло и нужно разорвать соединение

Содержимое файла check.php

Данный файл просто отдает время последнего изменения.
Почему мы делаем именно таким образом? То есть, почему вы подумали бы, мы не проверяем время последнего изменения внутри цикла в файле im.php?

Ответ: Если бы мы, делали проверку внутри, изменения не были бы в результате той функции getLastModified.
Это всё потому что, когда php запускает данный скрипт и крутит внутри себя вызов функции getLastModified(); её результат не изменяется с первого её вызова, то есть, можно сказать php кеширует её результат и отдает всё снова и снова в цикле, поэтому мы не сможем так отследить изменений.

Данный пример есть на этом сайте.
Страница на которой видно изменения «реал-тайм» здесь
Страница, на которой можно изменить содержимое здесь

Источник

Long polling на php

Send a request to the server, get an instant answer. Do this every x seconds, minutes etc. to keep your application up-to-date. But: This costs a lot of requests.

Send a request to the server, keep the connection open, get an answer when there’s «data» for you. This will cost you only one request (per user), but the request keeps a permanent connection between client and server up.

To test, simply change the URL in client/client.js to the location of your server.php file, for local testing url: ‘http://127.0.0.1/php-long-polling/server/server.php’ will do the job. Open the client/index.html to simulate the client.

While having the index.html opened in your browser, edit the data.txt on the server (and save it). You’ll see index.html instantly (!) getting the new content. Voila!

You should get a good idea how everything works by looking into the code, I think it’s self-explaining.

This would work perfectly in a real-world application, BUT

There are better tools for this, like node.js, which can handle MUCH more concurrent connections and serve data faster, with much less memory usage and afaik while using only ONE thread.

Apache2 uses 18MB afaik per thread by default, so having a «permanent connection» with 100 clients will use a lot of memory. I’m currently experimenting with lighttpd, NGINX and appserver.io to find a better solution.

🙂 Then have a look at my blog DEV METAL, at my other repos or support this and other projects by renting a server at Host1Plus. Thanks!

About

An extremely simple example of a «real-time» self-updating page using long-polling.

Источник

Простая реализация long polling механизма на PHP

Теория

Итак, что же из себя представляет Long Polling?
Выглядит это примерно следующим образом:
1) Клиент отсылает на сервер обычный ajax-запрос
2) Сервер, вместо того, чтобы быстро обработать этот запрос и отправить ответ клиенту, запускает цикл, в каждой итерации которого следит за возникновением событий (другой клиент добавил запись или удалил).
3) При возникновении события сервер генерирует ответ и отсылает его клиенту, таким образом завершая запрос.
4) Клиент, получив ответ от сервера, запускает обработчик события и параллельно отправляет очередной «длинный» запрос серверу.

То есть всё довольно просто и понятно. Однако, при реализации возникает несколько вопросов, которые нужно решить.

Шаги к практике

В первую очередь возникает вопрос о том, как скрипты будут взаимодействовать друг с другом. Ведь на каждый запрос клиента к серверу создаётся независимая копия PHP-скрипта. То есть нужна какая-то общая память для хранения данных о событиях.
Традиционный вариант — запускается демон, который оперирует событиями и держит соединение с клиентами. Использование демона естественным образом решает проблему памяти. Клиенты обращаются для получения событий не на веб-сервер, а к демону. В то же время, клиент, инициализирующий событие, также сообщает демону, что произошло событие. И таким образом всё крутится в памяти этого демона. который вообще может быть написан на чём угодно.
Но нам, в виду шила в одном месте и желания обойтись без сторонних приложений, демон на чём угодно не подойдёт. А писать демона на PHP, на мой вкус, не слишком интересно. У хостера могут быть проблемы с изменением максимального времени работы скрипта, а ещё продумывать интерфейсы взаимодействия клиентов с этим демоном… Нет, мы пойдём другим путём.
А остаётся только решить, как реализовать общую память в самом PHP. В голову приходят варианты хранения событий в файлах и БД, но хочется-то, чтобы всё было поближе — в памяти. Memcache нам, увы недоступен, а что тогда делать? А есть в php такое понятие как shared memory, точнее понятие-то не является особенностью этого языка, но нам дают возможность этим пользоваться. Механизмы этой технологии позволяют нам создать ячейку в оперативной памяти и использовать её в своих целях. Вот её я и буду использовать.

Практика

Попробуем представить интерфейс класса, который будет реализовывать нужный нам функционал.
Какие нужны методы?
1) Естественно, метод «прослушки», который будет вызываться, когда клиенты будут посылать polling-запросы, и следить за возникающими событиями. Я назвал этот метод listen.
2) Кроме того нам понадобится метод, который будет создавать событие. Этот метод носит имя push.
3) Вполне вероятно, что мы захотим обработать данные о событии, прежде чем отправить результат клиенту. Поэтому я добавил метод, который регистрирует событие и связывает с ним обработчик. Называется метод registerEvent.

В итоге получаем такой интерфейс класса:

Несколько примечаний:
1) В методе registerEvent поддерживается только по одному обработчику на событие по причине того, что возвращаемое обработчиком значение возвращается клиенту как данные события. В одном обработчике можно вызвать несколько функций и из результата их работы собрать ответ клиенту. Впрочем переделать код для поддержки нескольких обработчиков можно без особых затруднений.
2) В методе listen используется параметр lastQueryTime, который позволяет обработать события, возникшие раньше, чем поступил запрос на прослушку. Это может пригодиться когда нагрузка на сеть высока и между завершением одного polling-запроса от клиента и началом другого может пройти заметный промежуток времени, в который возникают события.
3) В приведённом коде не учитывается одновременный доступ к памяти. А вообще. для более надёжной работы желательно использовать семафоры.
4) В методе listen используется вызов функции sleep(1). Это нужно для того, чтобы уменьшить количество холостых итераций. Частоты обновления раз в секунду вполне достаточно для имитации реалтайма.

Ну и на клиенте всё также предельно просто. Нам нужен метод, который будет посылать polling-запрос серверу, ждать ответ, запускать обработчики событий и заново посылать запрос. Ну и метод, регистрирующий сами обработчики событий.

Исходники:
Описанный класс Polling на PHP.
Если кому-нибудь понадобится, приведу в порядок и выложу клиентскую часть.

Источник

Решение проблемы длительных PHP операций и интерактивное отображение статуса

long polling на php. Смотреть фото long polling на php. Смотреть картинку long polling на php. Картинка про long polling на php. Фото long polling на php

Время чтения: 11 минут

Отправим вам статью на:

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

Поэтому в ситуации, когда перед вами стоит нестандартная задача, возможно, стоит отойти от привычных методов, а попробовать рассмотреть несколько вариантов, пусть даже они поначалу кажутся не слишком удачными или к ним просто неохота прибегать, потому что “обычно мы так не делаем”. Тут главное не бояться экспериментировать, потестировать каждый из методов, и наконец, выбрать наиболее подходящий, отвечающий всем вашим запросам и обеспечивающий требуемый результат.

Нам, разработчикам, с такими проблемами приходиться бороться очень часто. Об одной такой сложной и трудоемкой задаче, потребовавшей перебора нескольких подходов к ее решению, и пойдет речь ниже.

Предыстория

На одном из PHP проектов среднего размера возникли проблемы, связанные с выполнением тяжеловесных операций и интерактивном отображении статуса исполняемой операции. Кроме блокировки исполнения других запросов и перехода по другим ссылкам портала пользователь пребывал в неведении и не догадывался, что же происходит в данный момент времени, когда же закончится операция, возможно, произошла ошибка и т.д. Проект изначально носил статус экспериментального, требования к выполняемым фазам часто менялись на лету, изменялись приоритеты и вектор развития системы. Для формирования большего представления хотелось бы описать стек используемых технологий на проекте:

В качестве операционной системы была выбрана: CentOS

Выбор фреймворка был обусловлен опытом его использования командой разработчиков, выбор же реляционной СУБД MySQL в качестве data storage является довольно стандартным решением и обусловлено стабильностью и распространенностью использования этой СУБД в качестве “движка” хранения данных.

На этапе проектирования системы сразу было трудно предугадать, что в системе появятся очень “тяжелые” операции, процесс выполнения которых придется пересматривать, реализовывать дополнительные компоненты и в общем перерабатывать архитектуру приложения. К тому же система была неким прототипом, где требования часто менялись, как и то, куда будет расти система в дальнейшем.

На одном из этапов был реализован ряд операций, которые оказались довольно “тяжеловесными” (highweighted operations) и мало того, что они блокировали выполнение других операций в рамках той же сессии, но и вообще процесс перехода по ссылкам системы становился невозможным до тех пор, пока “тяжелая” операция не была завершена. При рассмотрении проблемы на более низком уровне оказалось, что любой запрос блокировал файл сессии при его открытии, соответственно, пока операция не выполниться полностью и процесс не сбросит (flush) сессионные данные в файл сессии и не освободит его, сняв блокировку, другие запросы будут терпеливо ждать в очереди. Действительно, по умолчанию в PHP в качестве механизма хранения сессий используется файл. При открытии сессии срабатывает функция, наподобие fopen(), которая и блокирует файл на чтение и запись для других процессов. Сразу напрашивается решение о смене session storage для снятия блокировки, но поговорим об этом попозже.

Подходы

Коротко рассмотрим каждый из этих подходов для решения задачи выполнения тяжеловесных операций и интерактивного оповещения пользователя о прогрессе выполнения.

Разбиение операции на шаги

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

В выполняемом проекте данный подход оказался не слишком удачным по причине того, что тяжеловесные операции были практически не делимы на шаги. Кроме того использовались сторонние и запущенные на сервере сервисы, для работы с которыми приходилось создавать и подготавливать соответствующие объекты во время исполнения запроса. После исполнения запроса, которые представлял 1 шаг операции, объекты “прибивались”. Возможно стоило подумать о их переиспользовании, но до мы этого не дошли.

Ajax Polling

Следующее решение, которое напрашивается, заключается в запуске операции на сервере и постоянном опросе сервера о статусе выполняемой операции путем посылки серии ajax-запросов через определенные интервалы времени. На клиенте же можно проанализировать такой ответ сервера (например, это может быть JSON, содержащий “message”, “percentage”, “error” и “redirect”) и отрисовать progress-bar, отображающий статус выполнения текущей операции.

Была попытка использования Polling подхода на проекте с двумя разными способами хранения результата выполнения операции:

Хранение результатов в БД, в соответствующей таблице – high_weight_operations

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

Первое решение возникшей проблемы заключалась в освобождении файла сессии. Для этого в PHP предусмотрена функция – session_write_close(), с помощью которой можно закрыть сессию на запись. Действительно, можно запустить операцию, прочесть данные сессии, сделать в них изменения и закрыть сессию на запись.

Но в реалиях проекта, с учетом устоявшейся архитектуры, данное решение применить не удалось. Требовалась запись сессии во многих местах длительной операции. К тому же такое решение нельзя назвать “чистым”, т.к. пользователю, возможно, понадобиться ”попутешествовать” по сайту или запустить еще одну тяжелую операцию параллельно. Поэтому нужна была альтернатива.

Решение заключалось в смене Session Storage, что обеспечивало работу с сессией без блокировки при ее открытии. В качестве нового хранилища сессии можно выбрать один из следующих вариантов:

Для того, чтобы сменить Session Storage в PHP предусмотрена функция – session.save_handler(), которая устанавливает пользовательские функции хранения сессии. Они используются для хранения и получения данных, ассоциированных с сессией. В сети можно найти множество примеров её использования, существуют уже реализованные классы, служащие для переноса сессии в БД или memcached.

Примечание: Для работы функции необходимо установить опцию session.save_handler в значение user в вашем файле конфигурации php.ini.

На проекте в качестве эксперимента было использовано хранение сессий в mysql и mongodb.

Примечание: Если понадобится, чтобы только что записанный в сессию параметр был доступен для других запросов, то сессию необходимо “сбосить (flush)” в mysql или mongodb, чтобы отработала функция записи сессии. Для этого необходимо закрыть её на запись и переоткрыть снова:

Примечание: Для использования mongodb в PHP необходимо установить mongodb как сервис, а также драйвер работы на PHP.

Пример установки на CentOS :

Источник

Высокопроизводительный long polling чат

Предыстория

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

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

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

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

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

Реализация

От слов к делу

long polling на php. Смотреть фото long polling на php. Смотреть картинку long polling на php. Картинка про long polling на php. Фото long polling на php

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

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

Проблемы

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

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

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

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

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

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

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

long polling на php. Смотреть фото long polling на php. Смотреть картинку long polling на php. Картинка про long polling на php. Фото long polling на php

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

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

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

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

long polling на php. Смотреть фото long polling на php. Смотреть картинку long polling на php. Картинка про long polling на php. Фото long polling на phpСервисное приложение успешно скомпилировалось, но как же его теперь запустить?

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

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

Источник

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

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