Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Трудимся асинхронно в PHP либо история ещё одного чата

Anna | 29.05.2014 | нет комментариев
Меня дюже радует, как буйно прогрессирует PHP последние несколько лет. Вероятно и вас тоже. Возникают непрерывно новые вероятности, удерживающие энтузиастов оставаться на данной платформе. Чего только стоит недавняя новость о релизе Hack.

Наверно кто-то прочитав даже заголовок этой статьи ухмыльнется и подумает «мсье знает толк в извращениях!». Споры о крутости того либо другого языка никогда не утихают, но как бы там ни было, лично я для себя вижу не так уж и много условий смены языка, от того что люблю выжимать все вероятности, раньше чем решительно сменить каждый стек. Незадолго была публикация о создании чата на Tornado и мне захотелось рассказать о том, как схожую задачу я решал при помощи PHP.

Предыстория

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

Соединение устанавливается непрерывным и двунаправленным, а на стороне клиентской части работа сводится к обработке 4-х событий: onopenoncloseonerror и безусловно же onmessage. Никаких огромнее запросов через setInterval, излишнего трафика и нагрузки на сервер.

Дозвольте тут сделать малое отхождение для тех, кто не понимает о чём речь.
Те, кто знаком с рунетом начала 2000-х может помнят разнообразие чат-сервисов, где всё тормозило и неуклюже работало.
Чуть позднее возник AJAX и стало значительно отличнее, впрочем в сущности правило не изменился. Клиентская часть всё так же с некоторой заданной частотой по таймеру опрашивала сервер, разве что сейчас дозволено было отказаться от применения iframe и снизить немножко нагрузку на сервер за счёт меньшего объема отдаваемых данных.
Собственно, упомянутый чат-сервис был типичным ajax-чатом.

Есть правда и обратная сторона медали в избранном подходе:

  • неимение поддержки на ветхих браузерах
  • ручное управление поддержанием соединения
  • применение демона в серверной части

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

  1. Обновить код дозволено только перезапустив демона — соответственно для «чатлан» это происходит в той либо другой степени приметно
  2. Неизбежные ошибки и необработанные исключения приводят к падению демона — код необходимо писать «пуленепробиваемым»
  3. Демон должен применять выделенный вольный порт — это задача для тех, кто сидит за суровым фаерволом
  4. Применять неблокирующие функции

Те, кто никогда не слышал о том, что такое «резидентная программа», а писал лишь код для web-страницы, работающий по тезису «запустился-отработал-скончался», испытывают обрыв образца при написании демона в 1-й раз. Скажем, выясняется, что инстанцированные объекты могут «жить» длинно и беречь информацию без применения хранилища типа базы данных, и доступ к которой дозволено получать из различных подключений к демону. Вероятно именно при его написании особенно остро дозволено натолкнуться на задачу блокирующих функций и легко отсутствия заточенности PHP под асинхронность.

Что есть вообще асинхронность? Если по-простому, то это способность кода «распараллеливаться», исполнять несколько кусков кода самостоятельно друг от друга. Я верю, что читатель знаком правда бы с азами JavaScript. Множество хоть раз писали что-то как бы:

var myDomElement.onclick = function() {
    alert("I'm hit!");
}

Элементарно, да? Определяется обработчик события клика на какой-то элемент страницы. А что, если мы испробуем что-то сходственное сделат if ($len >= $this->softLimit && $len — $sent < $this->softLimit) {
$this->emit(‘drain’);

 

  • Удержание соединений — вероятно довольно значимая задача. На обыкновенных подключениях через проводную сеть либо порядочный wi-fi всё было отлично. Впрочем, при заходе с мобильного интернета было выявлено, что операторы мобильной связи не любят непрерывные соединения и обрезают их, судя по каждому, в зависимости от нескольких условий. Скажем, если БС слабо загружена и в чате все молчат, то могло выкинуть через 30 секунд. А могло и не выбрасывать даже. Так, что для профилактики я добавил циклическую посылку команды «пинг» на сервер, Дабы создавать активность. Но как оказалось, при большей загруженности БС и это не прокатывало.
    Вообще, давным-давно навязывалась реализация алгорифма: отложенное отключение пользователя из массива присутствующих пользователей по истечении таймаута. Видимо, что это требует применения асинхронной работы кода. Безусловно никакой sleep() здесь не годился. Я прикидывал всевозможные варианты реализации, включая даже сервер очередей. Решение нашлось и оказалось простым и грациозным: ReactPHP разрешает применять таймеры, вешающиеся на EventLoop. Выглядит это приблизительно так:

    private function handleDisconnection(User $user)
    {
    	$loop = MightyLoop::get()->fetch(); // получили одиночку EventLoop, на котором также работают сокеты
    	$detacher = function() use ($user) {
    		// обработка удаления пользователя из реестра посетителей в онлайне
    		...	
    	};
    
    	if ($user->isAsyncDetach()) {
    		$timer = $loop->addTimer(30, $detacher); // 30 секунд
    		$user->setTimer($timer);
    	} else {
    		$detacher($user);
    	}
    
    	$user->getConnection()->close();
    }
    
  • Соединение с БД в режиме демона есть толк удерживать открытым из соображений продуктивности и минимизации захламления логов ошибками соединения. В любом случае пришлось добавить в обёртку для PDO костыльный способ, вызываемый перед всяким запросом, Дабы гарантировать соединение с БД:
    protected function checkConnection()
    {
    	try {
    		$this->dbh->query('select 1');
            } catch (Exception $e) {
    		$this->init(); 
    	}
    }
    

    Увы, я не нашёл больше изысканного решения. Нужно всё же поэкспериментировать с Redis, тем больше, что есть готовый пакетpredis-async

  • Всякая вкладка браузера генерирует новое соединение. А разрешать пользователю размножаться клонированием как-то не хотелось. Пришлось воспрещать соединения с идентичной сессией. Это поведения отличается от классических чатов, которые разрешают легко трудиться единовременно в произвольном числе окон либо вкладок с одной сессией.
Что теперь может чат и чему ещё обучится

Из основных особенностей:

  • Чат-демон занимает в памяти порядка 20мб и эта цифра стабильна. Это хорошо.
  • Неимение непременной регистрации, пользователь заходит в чат сразу
  • Регистрация, авторизация и поправление пароля
  • Может делать приватные сессии и приватные сообщения (без создания отдельного канала)
  • Персональный чёрный список
  • Чат-рулетка на основе соционического типа
  • Неприметно для пользователя при обрыве соединения делается переподключение
  • Предотвращение дублирования соединений
  • Осуществляется флуд-контроль

Что нехорошо:

  • Нет порядочного ORM, самопал
  • Обработчик сессий тоже самопальный
  • Нет тестов
  • Нет многопоточности

Что ожидается доработать:

  • Поэкспериментировать с NoSQL БД, скажем Redis
  • Отдельные комнаты-каналы
  • Загружаемые аватары
  • Настройка разных видов нотификаций
  • Установка личных заметок на пользователей
  • Индикация «теперь печатает» в приватных каналах

Какие итоги дозволено сделать по прошествии 2-х месяцев разработки плана? У PHP всё ещё есть потенциал. По крайней мере предисловие работы с событийно-ориентированной парадигмой положено. Но увы, пока что язык пытается догнать, а не стать во главе движения. Если сравнить Ratchet и Tornado, то по вероятностям они ещё не ровня. Будем верить, что становление в этом направлении продолжится с позитивным убыстрением.

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

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB