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

В настоящее время я работаю над большим проектом, который требует реализации событий, отправленных сервером. Я решил использовать для него транспорт событий-событий и начал с простого чата. В настоящее время клиентская сторона слушает только новое событие сообщения чата, но в будущем проект будет иметь гораздо больше событий. Во-первых, я действительно обеспокоен сценарием на стороне сервера и его циклом, а во-вторых, я не уверен, что использование базы данных 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 быть узким местом в этом случае?

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

Будут подключены все 1000 пользователей? И используете ли вы Apache с PHP? Если это так, я думаю, что на самом деле вы должны быть обеспокоены памятью: каждый пользователь держит открытый сокет, процесс Apache и экземпляр PHP. Вам нужно будет измерить себя, для своей собственной настройки, но если мы скажем 20 МБ каждый, то это 20 ГБ памяти для 1000 пользователей. Если вы затягиваете вещи, поэтому каждый процесс составляет 12 МБ, что по-прежнему составляет 12 ГБ на 1000 пользователей. (A 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 МБ )

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

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

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

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

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

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

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