От форков к событиям и сопрограммам
DESCRIPTION
От форков к событиям и сопрограммам. 2 ноября 2008 г Ростов-на-Дону South Perl Евгений Торопов ИП Торопов Е.В. Skoosh - как это работает ? (вид снаружи). Поиск Фильтры Карта Отель Номер Заказ Оплата Счастье есть!. Skoosh - как это работает ? (вид изнутри). - PowerPoint PPT PresentationTRANSCRIPT
От форков к событиям и сопрограммам
2 ноября 2008 г
Ростов-на-Дону
South Perl
Евгений Торопов
ИП Торопов Е.В
Skoosh - как это работает?(вид изнутри)
Фронтенд: nginx – проксирование и балансирование
Бэкенд: Apache + mod_perl + MySQL 90% нагрузки и 3 сервера из 4 – под поиски (от
посетителей и партнеров)
Сентябрь – Октябрь 2008: 350 – 400 поисков в минуту Средняя продолжительность поиска: 3 секунды
Поиск
Ищем у себя и сапплаеров Каждому сапплаеру по копии процесса
(fork) Результат родителю через Storable::freeze
и временный файл Аггрегация и отдача клиенту
Многозадачность, альтернативы
Параллельные процессы (fork)
Потоки (use threads)
Событийные машины (POE, EV, Event, IO::Async, etc.)
Что-то еще?
Event loop VS Fork
Системный планировщик Ядро событийной машины
Системные процессы Сессии в рамках одного процесса
Квантование времени Логика, описанная программистом
AnyEvent
Унифицированный интерфейс к другим событийным машинам (POE, EV, Event, IO::Async, etc.)
Набор модулей для повседневных нужд (AnyEvent::Handle, AnyEvent::Socket, AnyEvent::HTTP, etc.)
Интеграция с Coro (неожиданный, но приятный сюрприз)
EV, а не POE
Подробно: http://search.cpan.org/~mlehmann/AnyEvent-4.31/lib/AnyEvent.pm#BENCHMARKS
Кратко: EV в среднем в 1000 раз быстрее POE и расходует в 30 раз меньше памяти.
AnyEvent: watchers
Перловый объект, хранящий информацию о нашей реакции на события
События: I/O, timers, signals, child process Аналог сессии в POE
my $w = AnyEvent->timer(after => 7, cb => sub { warn "timeout\n"});
# to cancel the timer:
undef $w;
AnyEvent: условные переменные
# создаем условную переменнуюmy $got_response = AnyEvent->condvar;
http_request "http://www.ya.ru", sub { $got_response->send;};
# входим в событийный цикл и обслуживаем другие# watcher-ы, пока не будет получен ответ$got_response->recv;
Поиск: было
foreach my $supplier_id (keys %$search_suppliers) {
my $pid = fork();
if ($pid) {
$search_suppliers->{$supplier_id} = $pid;
next;
}
my $results = $supplier->check_availability(...);
Stuffed::System::File->new($filename, 'w', {is_binary => 1})
->print(nfreeze($results))->close;
}
Поиск: стало
my $search_finished = AnyEvent->condvar;
foreach my $supplier_id (keys %$search_suppliers) {
my $w = AnyEvent->timer(after => 0, cb => sub {
my $results = $supplier->check_availability(...);
$supplier_results->{$supplier_id} = {
xml => $xml_log,
results => $results,
};
delete $not_finished->{$supplier_id};
$search_finished->send if not %$not_finished;
});
}
$search_finished->recv;
Inside check_availability
my $request_ready = AnyEvent->condvar;AnyEvent::HTTP::http_request( POST => $url, headers => {
... }, timeout => $HTTP_TIMEOUT, body => $xml, sub { $request_ready->send(@_) },);my ($content, $hdr) = $request_ready->recv;
Проблема
$condvar->recv, стоящий в коллбэке, блокирует выполнение до окончания работы коллбэков, запущенных во время ожидания
Решения
Отказ от $condvar->recv, но тогда последовательный код превращается в кашу из колбэков
Coro – да прибудет с вами сила сопрограмм!
Coro
Один процесс – несколько упрощенных perl-интерпретаторов
Сопрограмма определяется уникальным набором: callchain + lexical variables + @_ + $_ + $@ + $/ + C stack
Отсутствие проблем с синхронизацией и блокировками, как в случае с потоками
Coro::AnyEvent
Делает все необходимое для того, чтобы во время событийного цикла происходило переключение не только между watcher-ами, но и между сопрограммами
Поиск – финальный релизmy $search_finished = AnyEvent->condvar;foreach my $supplier_id (keys %$search_suppliers) {
$not_finished->{$supplier_id}->{coro} = new Coro sub {my $results;$results = $supplier->check_availability(...);$supplier_results->{$supplier_id} = {
xml => $xml_log,results => $results,
};delete $not_finished->{$supplier_id};$search_finished->send if not %$not_finished;
}$not_finished->{$supplier_id}->{coro}->ready;
}$search_finished->recv;
Результаты и замечания
Это работает! Нагрузка на поисковых серверах упала в
3-4 раза Coro segfault-ит под mod_perl 2.0.4, зато с
2.0.3 все замечательно Установка Coro, если “повезет”,
сопровождается танцами с бубном (зависит от ОС и версии perl)