PHP libevent Daemons. A high performance and reliable solution. Practical experience

Post on 19-Jun-2015

3.338 views 2 download

Tags:

description

It is considered (rigthly in most cases) that it's a bad manner to implement a daemon using PHP. It's OK for prototyping but no-no for production. That were our thoughts when we've started to implement a new version of browser game. It was planned to describe an interface to communicate with daemon that will be then rewritten in pure C. However, fist libevent PHP daemon performance tests forced us to think if we really need to rewrite it in C. What was the performance? Are there memory leaks? All these will be covered in the speech. You will also learn about problems, features and how a real project results.

Transcript of PHP libevent Daemons. A high performance and reliable solution. Practical experience

Демоны на PHP и libevent

как высокопроизводительное и надежное решение

Опыт практического применения

Международная конференция веб‑разработчиков

Кто мы такие?

• Вадим Крючков [Long], руководитель группы разработки

• Андрей Голубев [440hz], ведущий разработчик• Евгений Прудников, ведущий разработчик

Разработка браузерной MMORPG

http://chaosroad.ru/

• Вадим Крючков [Long], руководитель группы разработки

• Андрей Голубев [440hz], ведущий разработчик• Евгений Прудников, ведущий разработчик

• Вадим Крючков [Long], руководитель группы разработки

• Андрей Голубев [440hz], ведущий разработчик• Евгений Прудников, ведущий разработчик

Обычная архитектура

(mem)cached

Наша архитектура — включаем демоны

Демонизация. Что есть такое libevent?

• Предоставляет простой механизм для запуска callback функций, при наступлении определенного события на дескрипторе:

– READ

– WRITE

– TIMEOUT

– SIGNAL• http://www.monkey.org/~provos/libevent/• http://ru.php.net/manual/en/intro.libevent.php

Плюшками балуемся?

Пишем демона, работающего с сокетом

// Создаем сокет - event вешается на дескриптор

$rSocket = stream_socket_server (

'tcp://127.0.0.1:666',

$errno, $errstr,

STREAM_SERVER_BIND | STREAM_SERVER_LISTEN );

// далем его не блокирующим, что бы позволить принимать другие коннекты

stream_set_blocking ( $rSocket, 0 );

Пишем демона — подключаем libevent

// создаем событийную базу

$rBaseEvent = event_base_new ( );

// создаем новое событие для сокета

$rSocketEvent = event_new ( );

/**

* ловим события "чтение" и после операции чтения возвращаем событие в базу

* EV_READ - чтение

* EV_PERSIST - вернуть событие в базу после выполнения

*/

event_set ( $rSocketEvent, $rSocket, EV_READ | EV_PERSIST, 'onAcceptEvent' );

// устанавливаем событие в базу событий

event_base_set ( $rSocketEvent, $rBaseEvent );

// запускаем отслеживание

event_add ( $rSocketEvent );

// запускаем цикл

event_base_loop ( $rBaseEvent );

Метод обработки

function onAcceptEvent ( $rSocket, $rEvent, $args ) {

global $rBaseEvent; // удобнее сделать через объект ;)

static $iConnect = 0; // идентификатор конекта

$iConnect++;

// Примем коннект

$rConnection = stream_socket_accept ( $rSocket );

// далем коннект не блокирующим, что бы позволить принимать еще коннекты

stream_set_blocking ( $rConnection, 0 );

// создадим буфер обмена данными

$buf = event_buffer_new ( $iConnect, 'onReadEvent', 'onWriteEvent', 'onFailureEvent', $iConnect);

// подключаем буфер к базе событий

event_buffer_base_set ( $buf, $rBaseEvent );

// включаем буфер на события и возвращаем события назад после выполнения

event_buffer_enable ( $buf, EV_READ | EV_WRITE | EV_PERSIST );

}

Метод чтения

$iBufferReadLenght = 1024; // размер буфера чтения

function onReadEvent($rStream, $args) {

global $iBufferReadLenght;

$tmp = '';

do {

$tmp .= event_buffer_read ( $hBuffer, $this->iBufferReadLenght );

if( $iBufferReadLenght > strlen($tmp) ) {

break;

}

} while ( true );

return $tmp;

}

Легким движением руки, превращаем демона в ...

Таймеры

Стандартный таймер libevent'а УЖЕ работает :) (thx Tony2001)

• Но выход есть, даже в «плохом» раскладе!

– событие можно повесить на «любой» дескриптор

– event_add ( resource $event, int $timeout )

ДЕМОНстрация

http://cyberdot.ru/src/socket.phps

Подводные камни

• Очень мало информации и примеров

• Следить за ресурсами, не забываем их освобождать

• Хитрости при чтении данных, превышающих размер буфера

• Входных данных — много и они бывают «чужие» :)

• Проблемы с отслеживанием сигналов (EV_SIGNAL) — УЖЕ решены (thx Tony2001)

• Дедлоки в случае общения между демонами

DeadLock

DeadLockПричины возникновения

Самое неприятное — столкнуться только под нагрузкой

Users Combats

Update User A

Create User A

DeadLockМетоды борьбы

• Реорганизация приложения• Форк процессов

• Устранение асинхронности — введение блокировок

DeadLockУстраняем асинхронность

Users CombatsLOCK

Create User A

Update User A

Даем нагрузку

Тестирование ботами

Имитируем … пользователей в on-line:

• Воспользовались API• Написали приложение,

генерирующее ботов

Результаты

Сервер Xeon 8х2.66GHz, RAM 8Gb:

• Около 2.5 тысяч запросов в секунду (не Hello, World)• На 1 пользователя в online расходуется около 1.5Мб

памяти

• Память не «течет» (если того не захочет разработчик)

Советы

• Научитесь «мыслить параллельно»

– Процесс не завершается

– Чужие данные

– Асинхронные режимы• Читайте исходники — в них много полезного• Если демон будет не один — напишите

простенький фреймворк• Документируйте код + протокол

взаимодействия• Напишите хороший логгер - без него

отлаживать приложение будет сложно

– Сделайте несколько уровней логгирования

Выводы

• Можно рекомендовать к использованию на продакшене

• Позволяет держать хорошие нагрузки (при этом оставляя LA в разумных пределах )

• Требует ОЧЕНЬ аккуратной работы

Вопросы?

long13@gmail.comSkype: v.kruchkov

http://l-o-n-g.livejournal.com/