200 likes | 337 Views
От форков к событиям и сопрограммам. 2 ноября 2008 г Ростов-на-Дону South Perl Евгений Торопов ИП Торопов Е.В. Skoosh - как это работает ? (вид снаружи). Поиск Фильтры Карта Отель Номер Заказ Оплата Счастье есть!. Skoosh - как это работает ? (вид изнутри).
E N D
От форков к событиям и сопрограммам 2 ноября 2008 г Ростов-на-Дону South Perl Евгений Торопов ИП Торопов Е.В
Skoosh - как это работает?(вид снаружи) • Поиск • Фильтры • Карта • Отель • Номер • Заказ • Оплата • Счастье есть!
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.) • Что-то еще?
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)
Спасибо за внимание! Торопов Евгений jt@aaanet.ru