Как выносите выполнение логики, которая не влияет на ответ клиенту?

andry

Новичок
Как выносите выполнение логики, которая не влияет на ответ клиенту?

Поясню. Практически везде есть задачи типа:
послать юзеру мыло, после какого-то действия (нотификайшены на ответы в посте)
записать статистику


и прочая радость, которая уже никак не влияет на HTTP response клиенту. Естественное желание - отдать ответ как можно быстрее, а потом сделать все остальное, причем все остальное занимает причное время (относительно конечно) . Причем в большистве случаев, у меня точно, особо не раляет, что что-то там не выполнится, почему... запишем в лог и нормально

Понятно, что можно отправить буфер, а в конце все это сделать. Но не хочется чтоб процессы висели лишее время на критичных к производительности серверах.

Мои мысли. Делаем отдельный сервак, или несколько, под не критичные задачи. Пишем компонентик, который по сокетам дергает какие-то модульки (нотификейшены, статистика...)
сокеты немного хитрые,в хедерах
"Connection: Close\r\n\r\n";
тем самым из основного приложение мы практически сразу отваливаемся от сокета и не ждем ничего. Типа асинхронно , но без колбеков, ибо не требуется.


Кто как борится с такими вещами?
 

Major

Новичок
Что мешает после закрытия соединения добавить задачу/и в очередь, которые обработает нужный скрипт(по крону?)/демон? Зачем мудрить с сокетами?
 

Фанат

oncle terrible
Команда форума
действительно, непонятно, зачем сокеты, хедеры. зачем вообще дергать напрямую? Почему нельзя подавать заявку в управление, а оно уже пусть выполняет по своим ресурсам.
 

fixxxer

К.О.
Партнер клуба
речь идет о таком
http://php-fpm.anight.org/extra_features.html#fastcgi_finish_request
?

вообще тут все зависит от того, какой sapi используется.

если задачи действительно длительные, то лучше организовать очередь (да хотя бы табличкой в mysql), и по крону выгребать.
или несколько очередей, по очереди на тип события.
 

andry

Новичок
Автор оригинала: Major
Что мешает после закрытия соединения добавить задачу/и в очередь, которые обработает нужный скрипт(по крону?)/демон? Зачем мудрить с сокетами?
Так для этого пост и создал - обсудить, поразмышлять!

В твоем случае оверхед на добавление в очередь, в моем сокет. Если очередь в БД не гут лишние 0.0* повисит процес, мемкеш трогать такими вещами не хочется.

-~{}~ 08.10.08 12:42:

Автор оригинала: *****
Почему нельзя подавать заявку в управление
Это что значит?
 

MiksIr

miksir@home:~$
andy, в твоем случае ты никак не можешь рулить нагрузкой, т.е если пошел пик и у тебя этот апликейшн не готов ее принять, то юзер приложение будет или ждать готовности или отвалится. В случае очереди, юзер приложение быстро выплюнуло задачу, а с другого конца очередь уже разгребается в силу возможностей. Очередь так же упрощает обслуживание этого апликейшн сервера - можно его остановить, запустить и он пойдет дальше по очереди. Так же, гораздо легче все это балансируется. В общем, одни плюсы, минус - разве что чуток подумать над реализацией придется ;)
А реализаций очереди много... в конце концов почему бы и не база - один инсерт будет может даже быстрее чем коннект к апликейшн серверу.
 

andry

Новичок
Автор оригинала: MiksIr
andy, в твоем случае ты никак не можешь рулить нагрузкой, т.е если пошел пик и у тебя этот апликейшн не готов ее принять, то юзер приложение будет или ждать готовности или отвалится. .
Почему же, очень даже?
Я ж не говорил, что против очереди. Только добавние в очередь я хочу тоже вынести на другой сервак/и, туда кидаю все парамерты по сокету. Идея в том, чтоб совсем не висеть, отдал ответ, быстро дернул какие модульки, сразу вывалился.
 

MiksIr

miksir@home:~$
Ну это уже вопрос реализаций очереди. Но все же если "на костылях", то база лучше, если без - то готовые решения есть давно. Делать свое решение я бы не стал - слишком много граблей валяется, а выигрыша не получить никакого - все-равно это лишь транспорт, а обработчик свой уже.
 

Angerslave

Новичок
Чтобы не ждал инсерта, можно в MYISAM заюзать INSERT DELAYED - один фиг раз задача в очередь добавляется, то лишних пара сотен мс разницы не сыграют, а управление скрипту вернётся сразу.
 

Black Raven

Новичок
Практически везде есть задачи типа:
послать юзеру мыло, после какого-то действия (нотификайшены на ответы в посте)
записать статистику

и прочая радость
а можно пример "прочей радости", а то мне кажется, что проблема несколько надумана. в том плане, что бд+крон вполне хватит.
 

MiksIr

miksir@home:~$
- рассылка писем
- обработка статистики
- обработка тяжелых медийных файлов (ресемплинг видео, аудио, фото)
- пересчет всяких связанных данных при поступлении новой информации (именно по поступлении, а не тупо по крону)
- репликация =))

andry что то ляпнул, а вспомнить не получается. Ничего кроме сайповской очереди для постгреса в голову не идет.. но не думаю, что ее можно отдельно использовать. Мож немaмбa что-то скажет? ;)

А, вспомнилось из давних времен WebSphere MQ хотя бы. Еще во времена перла была какая-то поделка, но она субд использовала для хранения. А если в пределах одной машины, но в UNIX IPC есть очереди
 

fixxxer

К.О.
Партнер клуба
Не знаю, какие это должны быть супер-очереди, чтобы пришлось использовать memcacheq. Сам же memcached не гарантирует, что ничего не проэкспайрится, его здесь можно использовать только для не очень важных действий, типа сбора некритичной по точности статистики.

Но вообще можно прекрасно обойтись обычным MySQL. Будем считать, что событий не особо много, не больше одного в секунду. Делаем табличку вида

PHP:
CREATE TABLE `process_queue` (
  `id` int(10) NOT NULL auto_increment,
  `subscriber_id` int(10) NOT NULL,
  `status` enum('QUEUED','LOCKED','FAILED','DELETED') NOT NULL default 'QUEUED',
  `error_count` smallint(6) NOT NULL default '0',
  `parameters` text NOT NULL,
  `created` timestamp NOT NULL default '0000-00-00 00:00:00',
  `updated` timestamp NOT NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `updated` (`updated`,`subscriber`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
По крону раз в минуту запускаем для каждого из subscriber-во "разгребалку" (чтобы не запускать две сразу, можно использовать стандартную технику с pid file, этот момент я опущу). Делаем цикл вида

Код:
do {
  begin transaction
  if have last_element {
     $status = last_error ? ERROR : OK
     update queue set status=$status where subscriber=$my_id and element = last_element
  }
  unset last_element
  element = select from queue where subscriber=$my_id and status=QUEUED order by updated limit 1
  if have element {
     update queue set status = LOCKED where subscriber=$my_id and element = element
     last_element = element
  }
  commit

  if have last_element {
    last_error = handle_element(last_element)
  }
} while have last_element
я не описал, что статус error стоит ставить при нескольких ошибках подряд, а для начала просто откидывать в конец очереди.

ну и не помешает отдельный скрипт для подчистки залипших locked (мало ли вылетело что) - понять можно по статусу locked и now() - updated достаточно большому чтобы успеть все сделать
 

andry

Новичок
Автор оригинала: MiksIr

andry что то ляпнул, а вспомнить не получается. Ничего кроме сайповской очереди для постгреса в голову не идет.. но не думаю, что ее можно отдельно использовать. Мож немaмбa что-то скажет? ;)

А, вспомнилось из давних времен WebSphere MQ хотя бы. Еще во времена перла была какая-то поделка, но она субд использовала для хранения. А если в пределах одной машины, но в UNIX IPC есть очереди
Что я ляпнул? =)

Ну если так, то самое известное - JMS+ActiveMQ, там все по умному. Только на пыхе надо ведь. под ZF народ пишет что-то похожее, сильно урезанное, но пока в инкубаторе.

-~{}~ 08.10.08 20:04:

Автор оригинала: korchasa
memcacheq?

Хотя там тоже база.

А почему не хочется тискать сам memcache?
Кхех, если я не ошибаюсь о транзакции не тянет. Представь ситуацию когда очередь разгребают несколько процессов!
 

fixxxer

К.О.
Партнер клуба
>>Кхех, если я не ошибаюсь о транзакции не тянет. Представь ситуацию когда очередь разгребают несколько процессов!

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

andry

Новичок
короче решиния свелись к двум вещам
- инсертим в БД, добавляем в мемкеш... после отправки респонса
- дергаем сокет после отправки респонса, а на другой тачке инсертим в БД, добавляем в мемкеш...

Мне кажется, что разницы особо нет, если это что-то(работа после отправки респонса) не занимает значимое время.
 

MiksIr

miksir@home:~$
я ляпнул, я ;) для ibm встречал экстеншены для php

memcacheq возможно будет хорошим решением... ;)

Не знаю, какие это должны быть супер-очереди, чтобы пришлось использовать memcacheq
е знаю, какие это должны быть супер-очереди, чтобы пришлось использовать СУБД
=)
 

Angerslave

Новичок
fixxxer
Можно ложить всё задание в один элемент и только после выполнения - затирать.
 

MiksIr

miksir@home:~$
а если посреди обработки скрипт вылетел, то "ой".
Тут канечна субд удобнее, факт...
С другой стороны, это нетипичная проблема... и решать ее можно на уровне логов
 
Сверху