Pull to refresh

PHP-AMQP Что нового у Друзей?

Website development *
При построении социальной сети по типу шардинга встает проблема обмена данными между шардами. Традиционная репликация в данном случае не подходит по разным причинам. Тема шардинга — это отельная большая тема и не является предметом данной статьи.
В данной архитектуре для реализации «ленты Друзей» или «Новостной ленты» лучше использовать сервер очередей используя систему: Подписка-Уведомление. В качестве брокера обмена предлагается использовать сервер очередей RabbitMQ, реализующий протокол AMQP, который был выбран по причине хорошей масштабируемости. Серверная часть реализована на PHP, используя расширение php-rabbit (описание АПИ).

Архитектура Подписка-Уведомление представляет собой событийный подход. Кто-либо подписывается на Ваше события. Как только на Вашей персональной странице произошли какие-либо изменения: загрузилась новая фотография или видео, Вы — написали в блоге, получили подарок или просто пукнули, т.е. возникло некое событие, то Подписчики, сразу об этом узнают через уведомления.

Как это ложится на «ленту Друзей»: Пусть все Друзья по умолчанию подписаны на некоторое событие: загрузка фото, загрузка видео, запись в блоге, обновление профиля, создание опроса и т.д. При возникновения некоторого события, например загрузка фото, в Ваш личный Обмен (Exchange) пишется сообщение, например время загрузки, с ключом «photo».
Все, кто Подписан на Ваши события, получают данные сообщения, которые читают из своих личных Очередей ( Queue). Более подробно о брокере обмена можно почитать в статье «AMQP по русски»

Все на много проще если мы рассмотрим простой Пример:
Пусть Пользователь ХабраМозг имеет двоих друзей: ХабраЛох, ХабраЛузер. Пользователи ХабраЛох и ХабраЛузер подписаны на все события. Пусть третий Пользователь ХабраЧайник решил подписаться только на обновление блога.

При заведении логина ХабраМозг мы создаем в брокере Обмен «HabraGuru». Также необходимо создать очередь, чтоб наш Пользователь мог читать ленты его друзей.

$cnn = new APMQConection();
$exchange = new AMQPExchange($cnn);
$exchange->declare('HabraGuru', 'topic',AMQP_DURABLE );
$queue = new AMQPQueue($cnn);
$queue ->declare('HabraGuru', AMQP_DURABLE)

параметр AMQP_DURABLE дает указание брокеру не сбрасывать (сохранять) очередь /обмен при перезагрузки.

При возникновение отношения «Дружба» мы автоматически подписываем пользователей ХабраЛох и ХабраЛузер на все события Пользователя ХабраМозг.

$cnn = new APMQConection();
$queue = new AMQPQueue($cnn, 'HabraLoh');
$queue->bind('HabraGuru', '*' );

и
$cnn = new APMQConection();
$queue = new AMQPQueue($cnn, 'HabraLuser');
$queue->bind('HabraGuru', '*' );


А Пользователь ХабраЧайник зашел на блог Пользователя ХабраМозг, блог ему понравился и он нажал на ссылку «Подписаться». При этом выполнится следующий код:

$cnn = new APMQConection();
$queue = new AMQPQueue($cnn, 'HabraTeapot');
$queue->bind('HabraGuru', 'blog' );


Пусть Пользователь ХабраМозг выложил презентацию «Как устроена лента Друзей», следствии чего по окончании загрузки выполнилась следующая часть кода, которое публикует сообщение $msg:

$msg = json_encode( array('date'=> date('d-m-y'), 'type'=> 'presentation' , title => 'FriendLenta. How to.'));
$cnn = new APMQConection();
$exchange = new AMQPExchange($cnn, 'HabraGuru');
$exchange->publish( $msg, 'presentation');

в данном примере в качестве обмена данными используется JSON, но это не принципиально, можно использовать и csv, и xml, и к чему душа ближе лежит

Раз в 1/2/4/6/12/24 часов (кто как настроит) отрабатывает кронскрипт, который для каждого пользователя строит его личную ленту.
$queue = new AMQPQueue(APMQConection(), 'HabraLuser');

while (1){
$res = $queue->get();
if ($res['count']<0) break;
// echo "$i.{$res['msg']}";
// call QueueItemCallBack( $res['msg'] );
// обрабатываем принятые сообщения и строим "ленту Друзей"
}


Сообщение (title => 'FriendLenta. How to.') от Пользователя ХабраМозг получат пользователи ХабраЛох и ХабраЛузер. А Пользователь ХабраЧайник данного сообщения не получит. Однако, если Пользователь ХабраМозг решит выложить в блог, рассказ о "Ленте Друзей", то выполнится код:

$msg = json_encode( array('date'=> date('d-m-y'), 'type'=> 'blog' , title => 'How to developed the FriendLenta'));
$cnn = new APMQConection();
$exchange = new AMQPExchange($cnn, 'HabraGuru');
$exchange->publish( $msg, 'blog');


то при чтении своей очереди ( $queueName = 'HabraTeapot' ), Пользователь ХабраЧайник получит сообщение (title => 'How to developed the FriendLenta'), так как оно было отправлено с ключом «blog», на которое он был подписан: $queue->bind('HabraGuru', 'blog' );

Решили отписаться от сообщений — нет проблем:
$cnn = new APMQConection();
$queue = new AMQPQueue($cnn, 'HabraTeapot');
$queue->unbind('HabraGuru', 'blog' )
;

Сервер очередей работает по принципу FIFO: первый пришел, первый вышел. Так что может возникнуть ситуация, когда пришло слишком много сообщений, например после долгого отпуска, то мы хотим сосчитать только последнюю тысячу (из 15-ти тысяч). В этом случае:
— берем длинну очереди $len = $queue->declare();
— если $len > MAXQUEUELEN то $dummy = $len — MAXQUEUELEN;
— считываем $dummy элементов очереди (в нашем случае 14 тыс );
— оставшиеся MAXQUEUELEN обрабатываем (1 тыс сообщений );

Как видно все работает надежно и просто. По такому же принципу устроена «Доска объявлений» и «Новостная лента».
Подписываемся на все новости в Москве:

$queue = new AMQPQueue(new APMQConection(), 'HabraGuru');
$queue->bind('HabraGuru', '*.msk' );


Подписываемся на новости о недвижимости в Москве:

$queue = new AMQPQueue(new APMQConection(), 'HabraGuru');
$queue->bind('HabraGuru', 'realty.msk' );


Подписываемся на все новости о недвижимости

$queue = new AMQPQueue(new APMQConection(), 'HabraGuru');
$queue->bind('HabraGuru', 'realty.*' );


Подписываемся на объявления о недвижимости, «детские вещи» и авто:

$queue = new AMQPQueue(new APMQConection(), 'HabraGuru');
$queue->bind('HabraGuru', 'realty.notes.spb' );
$queue->bind('HabraGuru', 'children.notes.spb' );
$queue->bind('HabraGuru', 'auto.notes.spb' );


Подписываемся на погоду:

$queue = new AMQPQueue(new APMQConection(), 'HabraGuru');
$queue->bind('HabraGuru', 'weathe.spb' );


Очевидно, примеры кода наглядно показывают как просто реализовать «ленту Друзей,» новостную ленту, объявления или
Tags:
Hubs:
Total votes 29: ↑22 and ↓7 +15
Views 6.5K
Comments Comments 62