Производительность источника событий

В настоящее время я работаю над большим проектом, который требует реализации событий, отправленных сервером. Я решил использовать для этого транспорт источника событий и начал с простого чата. В настоящее время клиентская сторона слушает только новое событие сообщения чата, но в будущем в проекте будет намного больше событий. Во-первых, меня очень беспокоит сценарий на стороне сервера и цикл в нем, а во-вторых, я не уверен, что использование базы данных mySQL в качестве хранилища (в данном случае для сообщений чата) на самом деле хорошая практика. Текущий цикл выдает новые сообщения по мере их появления в базе данных:

$statement = $connect->prepare("SELECT id, event, user, message FROM chat WHERE id > :last_event_id");
while(TRUE) {
    try {
        $statement->execute(array(':last_event_id' => $lastEventId));
        $result = $statement->fetchAll();
        foreach($result as $row) {
            echo "id: " . $row['id'] . "\n";
            echo "event: " . $row['event'] . "\n";
            echo "data: |" . $row['user'] . "| >>> \n";
            echo "data: " . $row['message'] . "\n\n";
            $lastEventId++;
        }
    } catch(PDOException $PDOEX) {
        echo $PDOEX->getMessage();
    }
    ob_flush();
    flush();
    usleep(10000);
}

Из того, что я читал, такой цикл неизбежен, и моя задача - оптимизировать его производительность. В настоящее время я использую подготовленный оператор вне while() и разумный (?) usleep().

Итак, вопросы к тем, у кого есть опыт серверных ивентов:

  1. Целесообразна ли такая методика для использования на умеренно загруженных сайтах (1000-5000 пользователей в сети)?
  2. Если да, то есть ли способ повысить производительность?
  3. Может ли в этом случае база данных mySQL стать узким местом?

Ценю любую помощь, так как вопрос довольно сложный, и поиск информации не даст мне никаких советов или способов проверить это.


person Damaged Organic    schedule 09.12.2013    source источник


Ответы (2)


Будут ли одновременно подключены все 1000+ пользователей? А вы используете Apache с PHP? Если это так, я думаю, что вам действительно следует беспокоиться о памяти: каждый пользователь держит открытым сокет, процесс Apache и экземпляр PHP. Вам нужно будет измерить себя для своей собственной настройки, но если мы скажем по 20 МБ для каждого, это 20 ГБ памяти для 1000 пользователей. Если вы усложните ситуацию, чтобы каждый процесс занимал 12 МБ, это все равно 12 ГБ на 1000 пользователей. (Инстанс m2.xlarge EC2 имеет 17 ГБ памяти, поэтому, если вы запланируете один из них на 500–1000 пользователей, я думаю, все будет в порядке.)

Напротив, при 10-секундном времени опроса загрузка ЦП очень низкая. По той же причине я не думаю, что опрос базы данных MySQL будет узким местом, но на этом уровне использования я бы подумал о том, чтобы каждая запись в БД также выполняла запись в memcached. В принципе, если вы не возражаете против того, чтобы добавить немного аппаратного обеспечения, ваш подход выглядит выполнимым. Это не самое эффективное использование памяти, но если вы знакомы с PHP, это, вероятно, будет наиболее эффективным использованием времени программиста.


ОБНОВЛЕНИЕ: Только что увидел комментарий ОП и понял, что usleep(10000) равно 0,01 с, а не 10 с. Ой! Это все меняет:

  • ваша загрузка ЦП теперь высока!
  • Вам нужен set_time_limit(0) вверху ваш сценарий: вы очень быстро достигнете 30-секундного использования ЦП по умолчанию с этим жестким ограничением.
  • Вместо опроса БД вы должны использовать службу очереди уведомлений.

Я бы использовал службу очереди вместо memcached, и вы могли бы либо найти что-то готовое, либо довольно легко написать что-то нестандартное на PHP. Вы по-прежнему можете оставить MySQL в качестве основной базы данных, а служба очередей будет опрашивать MySQL; разница здесь в том, что у вас есть только один процесс, интенсивно опрашивающий его, а не тысяча. Служба очереди — это простой сервер сокетов, который принимает соединение от каждого из ваших внешних PHP-скриптов. Каждый раз, когда его опрос находит новое сообщение, он передает его всем клиентам, которые к нему подключились. (Существуют разные способы его создания, но я надеюсь, что это дало вам общее представление.)

На фронтальном PHP-скрипте вы используете вызов socket_select() с 15-секундным тайм-аутом. Он просыпается только при отсутствии данных, поэтому в остальное время использует нулевой ЦП. (15-секундный тайм-аут позволяет отправлять сообщения проверки активности SSE.)


(Источник данных о 20 МБ и 12 МБ)

person Darren Cook    schedule 10.12.2013
comment
Спасибо за ответ. Как предложил Дэйв, в настоящее время я пытаюсь заставить memcache работать для моих целей, и в этом примере он служит временным хранилищем для входящих сообщений чата. «Время опроса» указано в микросекундах, но это всего лишь задержка, поэтому цикл не будет выполнять слишком много итераций. И источник события открывает новое соединение каждые 30 секунд - я полагаю, когда php-скрипт достигает max_execution_time. Я думал, что особенность event-source - он устанавливает keep-alive соединение и сбрасывает на него все, что ему эхом отдает сервер, или я ошибаюсь? Сеть также показывает запрос каждые 30 секунд. - person Damaged Organic; 10.12.2013
comment
Ваш ответ был очень... поучительным, так сказать, и действительно дал мне идею. В настоящее время я пытаюсь настроить один связанный сокет, который прослушивает несколько одноранговых узлов и опрашивает таблицу базы данных mySQL с типом MEMORY, и все это должно быть доставлено клиенту через event-source. Это на данный момент ответ мне. Благодарю вас! :) - person Damaged Organic; 11.12.2013

  • целесообразно ли использовать такую ​​методику на умеренно загруженных сайтах (1000-5000 пользователей в сети)?

Практически единственный способ сделать это, если вы не установите таймер обновления на стороне клиента и не будете использовать серверную часть только в качестве веб-сервисов. Нагрузка будет высокой с таким количеством пользователей, но вы ограничены, выполнив решение только на чистом php. Я бы предпочел посмотреть на демон c/c++ на сервере и необработанные сокеты.

  • если да, то есть ли способ повысить производительность?

memcached как временное хранилище, а затем внутренний процесс для ежечасной/ежеминутной передачи архива в базу данных mysql

  • может ли база данных mySQL быть узким местом в этом случае?

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

надеюсь, это поможет

person Dave    schedule 09.12.2013