Логика очередей в проектах

20 views
Skip to first unread message

Rauan Maemirov

unread,
Sep 18, 2010, 10:37:06 AM9/18/10
to rabbit...@googlegroups.com
Всем добрый вечер.
Хотелось бы узнать, кто как организовывал логику системы очередей.

Примерные мысли:
1). Сервисный класс App_Service_Queue с методом sendToQueue, например. В этом классе находится коннект к кролику, ну и прочая логика.
2). Дальше идут разные сервисные классы, которые наследуют исходный, и в зависимости от направления задачи (mail, offline processing, cache cleaning и т.д.) устанавливают свой exchange.
3). В приложении вызываем соответствующий сервис, и отсылаем сообщение в очередь.
4). Есть куча мелких скриптов, которыми управляет supervisord (или запускаются в кроне). В них получаем данные, и работаем с ними.

В одной презентации видел, что сперва в очередь отсылается действие "отправить сообщение", которое в свою очередь генерирует письмо и отправляет в соответствующую очередь, и уже затем отсылает его на почту. http://www.slideshare.net/mwillbanks/art-of-message-queues (27-слайд)

Зачем такой overhead в случае, когда письмо одно и email один? (т.е. нам не нужно вытягивать какую-то группу адресов, не нужно проверять преференции пользователя - получать письмо поразово, партиями и т.д.)


Еще был вариант:
Отправлять в очередь вместе с данными название соответствующего сервиса, метода и т.д.:
array('App_Service_Mail', 'sendNotification', array('email'=>'john...@gmail.com', 'subject' => 'New message from user Vasya Pupkin', 'message' => 'Hi...'));

В данном случае кол-во бекграунд-скриптов можно будет снизить до кол-во типов задач (утрированно). Но, на мой взгляд, минус: бекенд привязывается.

Помогите советами, плиз. Какие минусы в предложенных подходах, какие есть еще, что гибче и так далее. Что менее болезненно переписывается/расширяется.

Еще были бы интересны реальные примеры кода. (люблю учиться на примерах, а не определениях типов exchange'ей)

Alexandre Kalendarev

unread,
Sep 18, 2010, 7:06:14 PM9/18/10
to rabbit...@googlegroups.com
Доброй ночи...

18 сентября 2010 г. 18:37 пользователь Rauan Maemirov <ra...@maemirov.com> написал:

Всем добрый вечер.
Хотелось бы узнать, кто как организовывал логику системы очередей.

Примерные мысли:
1). Сервисный класс App_Service_Queue с методом sendToQueue, например. В этом классе находится коннект к кролику, ну и прочая логика.

все зависит от логики, надо 7 раз подумать прежде чем  делать лишнюю обвязку. Есть куча лит-ры по ООП.
 
2). Дальше идут разные сервисные классы, которые наследуют исходный, и в зависимости от направления задачи (mail, offline processing, cache cleaning и т.д.) устанавливают свой exchange.

имеет смысл использовать наследование, если есть какой-нибудь общий код.  Может имеет смысл сделать синглентон. Смотри на использование класса Queue как на класс Db. Ты же не делаешь наследников от Db : DbUser, DbQuest, DbGroup... хотя все они используют БД.   

Наследование используется, когда мы можем с уверенностью заявить "является под-видом чего-то". Мэтры ООП советуют использовать композицию. т.е. в классе твоего сервиса есть св-во Queue. Хотя возможна сл. структура:

baseService - базовый абстрактный класс, имеет св-во Queue.
наследники:
emailService 
videoConverterService
imageConverterService
. . .
 
3). В приложении вызываем соответствующий сервис, и отсылаем сообщение в очередь.
 
ок

4). Есть куча мелких скриптов, которыми управляет supervisord (или запускаются в кроне). В них получаем данные, и работаем с ними.


понятно, так и делаем
 
В одной презентации видел, что сперва в очередь отсылается действие "отправить сообщение", которое в свою очередь генерирует письмо и отправляет в соответствующую очередь, и уже затем отсылает его на почту. http://www.slideshare.net/mwillbanks/art-of-message-queues (27-слайд)

вполне разумная логика для высоконагруженных систем
 
Зачем такой overhead в случае, когда письмо одно и email один? (т.е. нам не нужно вытягивать какую-то группу адресов, не нужно проверять преференции пользователя - получать письмо поразово, партиями и т.д.)

если одно, то конечно логика дурная, а если по 10 писем в сек или чаще? Очередь нужна для того, чтоб по максимуму разгрузить фронтэнд и любой код, который можно выполнить в бэдграунде-там его и выполнять.
 
Еще был вариант:
Отправлять в очередь вместе с данными название соответствующего сервиса, метода и т.д.:
array('App_Service_Mail', 'sendNotification', array('email'=>'john...@gmail.com', 'subject' => 'New message from user Vasya Pupkin', 'message' => 'Hi...'));
 
если организовать приложение таким образом, то на принимающей стороне нужен какой-то контроллер, который будет это все разруливать, Лично я не сторонник усложнений. Заведи очередь на отправку писем, очередь на обработку фото/видео, очередь на обмен сообщениями и разгребай очередь своими специализированными скриптами. Чем легче сделаешь систему, тем более производительней она будет.

С другой стороны - это небольшая обвязка и появляется некая универсальность. Разгребаем одну большую очередь одним (несколькими одинаковыми) скриптом. Думаю такой подход больше подходит для более простых серверов очередей.

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

Помогите советами, плиз. Какие минусы в предложенных подходах, какие есть еще, что гибче и так далее. Что менее болезненно переписывается/расширяется.

предложенный подход  "суперочередь" более гибок, можно наклонировать кучу скриптов и запускать их на разных серверах. Я, например делал сложную систему мониторинга  бэдграундовских процессов, С одной стороны - задача упрощается, а с другой, нам тяжее отследить, что там у нас происходит, например, на сервере конвертации? сколько заданий в очереди и нужно-ли задействовать дополнительные ресурсы? т.е. тяжеелее монипулировать очередями.

мое мнение может быть не совсем объективным


Александр

Rauan Maemirov

unread,
Sep 19, 2010, 4:10:50 AM9/19/10
to rabbit...@googlegroups.com
Спасибо, за ответ. Я тоже больше склоняюсь к варианту с разными скриптами для разной логики.

Меня также интересует, кто как обычно делает consumers-скрипты?
Через while(true) или выловом pcntl_signal и аккуратным прекращением работы скрипта?


2010/9/19 Alexandre Kalendarev <aka...@gmail.com>

Alexandre Kalendarev

unread,
Sep 19, 2010, 6:25:49 AM9/19/10
to rabbit...@googlegroups.com


19 сентября 2010 г. 12:10 пользователь Rauan Maemirov <ra...@maemirov.com> написал:

Спасибо, за ответ. Я тоже больше склоняюсь к варианту с разными скриптами для разной логики.

Меня также интересует, кто как обычно делает consumers-скрипты?
Через while(true) или выловом pcntl_signal и аккуратным прекращением работы скрипта?


ну, на вкус и цвет - приятелей нет
у меня есть так называемые "вечные" скрипты,  которые "живут" по 3-5 мин, а далее он сам себя перезапускает, используя php-forker http://code.google.com/p/php-forker/

код следующий:

$tbeg=time();

$app = new Application('converter'); 
$app->init();
while ( time() < $tbeg + self::liveTime ) {
   $app->check(); // проверяет очередь и выполняет необходимые действия   
   $app->save() // сохраняет состояние процесса
}

$app->finish(); // финализируется процесс; если необходимо, то в этом методе делаем перезапуск
командой system( /usr/local/bin/php-forker  __FILE__ $num  );

переменная $num - это номер скрипта, т.е. одноименных скриптов может быть запущено несколько (например 3 скрипта на конвертацию видео, два скрипта репликации и один на пере-индексацию)

каждый скрипт порождает свой пид-фацл, чтоб предотвратить повторные запуски. Для нескольких копий одного скрипта - используется цифровой префикс: video-convert.1.pid, video-convert.2.pid ...

Однако, раз в 3-5 мин запускается "скрипт-монитор", который отслеживает скрипт, смотрит сколько он времени работает (вдруг зациклился), и если болше чем задано, то мягко прибивает его. А если необходимого скрипта нет, то просто перезапускает его - опять же используя форкалку. так же сисадмин может отслеживать через нагиус "не нормальную" ситуацию с бэдграундовскими скриптами и при необходимости посмотреть "служебный лист запущенных скриптов" с более подробной информацией ( на какой машине какой скрипт запущен,pid, сколько времени в работе и сколько по плану и тд ).

можно использовать технологию "скрипт-демон", но нужно постоянно отслеживать чтоб не текла память, по этому и сделали с холодным перезапуском. Вроде как надежней, не исключено что есть более оптимальный вариант

Хочу заметить что данная технология  использована на площадке от 40 серверов. Я сейчас там не работаю, так что не скажу точно. Возможно для более меньшего проекта это будет не оптимальным.


Александр

Stas Lozitskiy

unread,
Sep 20, 2010, 2:46:17 AM9/20/10
to rabbit...@googlegroups.com


19 сентября 2010 г. 14:25 пользователь Alexandre Kalendarev <aka...@gmail.com> написал:


можно использовать технологию "скрипт-демон", но нужно постоянно отслеживать чтоб не текла память, по этому и сделали с холодным перезапуском. Вроде как надежней, не исключено что есть более оптимальный вариант


Здравствуйте. В качестве демона можно использовать phpDaemon http://github.com/kakserpom/phpdaemon
Судя по отзывам, он не течет, но в силу своей асинхронности требует особого подхода к построению приложений. Я сам его не использовал, но хотелось бы.

Alexandre Kalendarev

unread,
Sep 20, 2010, 4:50:05 AM9/20/10
to rabbit...@googlegroups.com


20 сентября 2010 г. 10:46 пользователь Stas Lozitskiy <lozit...@gmail.com> написал:

проще самому  демона написать
Reply all
Reply to author
Forward
0 new messages