Баннерокрутилка на Erlang
-
Upload
artyom-gavrichenkov -
Category
Technology
-
view
1.786 -
download
1
Transcript of Баннерокрутилка на Erlang
![Page 1: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/1.jpg)
Баннерокрутилка:как это было на Erlang
![Page 2: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/2.jpg)
Задача
● Выдача ссылок на новости● База из тысяч новостей ● Ссылки выбираются произвольно● Распределение – неравномерное
● Форматирование ссылок перед выдачей● Ссылки должны иметь формат блока● Дополнительные поля: идентификатор блока, …
![Page 3: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/3.jpg)
Ta-da!
● Требования: >= 5000 запросов в секунду● Не справляемся – должны быстро выдавать
пустую страницу, чтобы не «ломать» вёрстку● Никакого кэширования
● Срок выполнения: полтора месяца.● При этом у разработчика баннерокрутилки
есть и другие задачи
![Page 4: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/4.jpg)
Структура решения
● Структуры в памяти, хранящие новостии статистику показов
● Множество потоков, обрабатывающихHTTP-запросы
● Контроллер● добавляющий и удаляющий новости● собирающий статистику● на основе TCP-сокетов
![Page 5: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/5.jpg)
Структура решения
Множество потоков
![Page 6: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/6.jpg)
Erlang
● DSL для многопоточных приложений● Встроенные в язык примитивы для посылки
и приёма сообщений● Встроенные в язык шаблоны поведения● Встроенная в язык in-memory БД
![Page 7: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/7.jpg)
Структура решения
● Структуры в памяти, хранящие новости и статистику показов: mnesia/ets/dict!
● Множество потоков, обрабатывающих HTTP-запросы: gen_server!
● Контроллер: gen_tcp!
![Page 8: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/8.jpg)
Структура решения
● Структуры в памяти, хранящие новости и статистику показов: mnesia/ets/dict!
● Множество потоков, обрабатывающих HTTP-запросы: gen_server!● который уже написан за нас! mochiweb/misultin
● Контроллер: gen_tcp!
Batteries included
![Page 9: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/9.jpg)
Batteries included, though not all
![Page 10: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/10.jpg)
Основная часть решения
● HTTP-сервер Mochiweb получает запрос, создаёт поток
● Поток забирает из Mnesia данные● Поток производит выборку, сортирует
данные, форматирует строки, выдаёт, умирает.Все счастливы.
«In theory, there's no difference betweentheory and practice. In practice, there is».
L. A. van der Snepscheut.
![Page 11: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/11.jpg)
Факап #1
У каждой новости есть коэффициент важности. В соответствии с этим коэффициентом необходимо выдавать новость чаще или реже остальных.
● Перед выдачей нужно назначать взвешенные произвольные числа каждой новости и делать по ним выборку.
● Новостей много.
![Page 12: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/12.jpg)
Факап #1
● Mnesia построена на основе ETS● http://www.erlang.org/doc/man/ets.html:
«In the current implementation, every object insert and look-up operation results in a copy of the object.»
● Т. е. в копировании сотен новостей из потока в поток. Slow as hell.
![Page 13: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/13.jpg)
Эврика!
● Вместо ETS напишем собственную структуру данных на основе gen_server, dict, queue, blackjack и hookers.
● Повесим её в виде отдельного потока● Будем делать там грубую предвыборку
новостей, которые потом быстро скопируются в рабочий поток
![Page 14: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/14.jpg)
● Результат:
рост производительности в 3 раза
● Вывод:● всегда думай, какие объёмы данных копируешь!● профилируй!
![Page 15: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/15.jpg)
Основная часть решения v0.2
● HTTP-сервер Mochiweb получает запрос, создаёт поток
● Поток отправляет запрос в gen_server● gen_server производит предвыборку
новостей и присылает результат● Поток производит выборку, сортирует
данные, форматирует строки, выдаёт, умирает.Все счастливы.
![Page 16: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/16.jpg)
Факап #2
Новости – это текст.
Текст – это строки.
● Строки в Erlang – это связные списки символов
● IO в Erlang – это очень медленно
![Page 17: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/17.jpg)
Эврика!
● Если вы пишете на Erlang,то строка символов записывается так:
"Hello world!~n"
● Конкатенация записывается так:
"Hello " ++ Username ++ "!~n"
![Page 18: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/18.jpg)
Эврика!
● Если вы пишете на Erlang веб-приложения,то строка символов записывается так:
<<"Hello world!~n">>
● Конкатенация записывается так:
[<<"Hello ">>, Username, <<"!~n">>]
![Page 19: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/19.jpg)
Почему:
● <<>> – встроенный бинарный тип● <<"">> – бинарная строка● Списки символов нужно обрабатывать
перед выдачей● Вывод бинарных данных – это просто
вызов writev(2)● Blazingly Fast
![Page 20: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/20.jpg)
Почему:
● "Hello" ++ "!\n" => "Hello!\n" => строка● Конкатенация списков – O(n)● Вывод строки => цикл по списку из 7 символов
● [<<"Hello">>, <<"!\n">>] – тип iolist().● Добавление в начало списка – O(1)● Вывод => цикл по списку из 2 элементов● Встроенным функциям I/O всё равно, что
выводить
![Page 21: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/21.jpg)
Кроме того
● Строковые операции, наподобие обработки регулярных выражений, всё равно дорогие
● Впрочем, они вообще не очень дешевы.
Обработку данных нужно делать не на этапе выдачи, а на этапе помещения в базу – до тех пор, пока позволяют объёмы памяти и специфика решаемой задачи.
● В данном случае кэширование конструируемых URL новостей и т. п. позволило отыграть 15%
![Page 22: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/22.jpg)
● Результат:
рост производительности в 10 (десять) раз
● Вывод:● всегда думай, как обрабатывать строки!● профилируй!
![Page 23: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/23.jpg)
Основная часть решения v0.3
● HTTP-сервер Mochiweb получает запрос, создаёт поток
● Поток отправляет запрос в gen_server● gen_server производит предвыборку
новостей и присылает результат● Поток производит выборку, сортирует
данные, форматирует iolist()'ы, выдаёт, умирает.
![Page 24: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/24.jpg)
профилируй!
![Page 25: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/25.jpg)
Основная часть решения v0.4
● HTTP-сервер Misultin получает запрос, создаёт поток
● Поток отправляет запрос в gen_server● gen_server производит предвыборку
новостей и присылает результат● Поток производит выборку, сортирует
данные, форматирует iolist()'ы, выдаёт, умирает.
![Page 26: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/26.jpg)
Misultin
● Реализация gen_server, как и Mochiweb● Интерфейс, абсолютно аналогичный
Mochiweb● Стабильно на 10-15% быстрее
![Page 27: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/27.jpg)
Результат
● Один человекомесяц● Быстрое веб-приложение
# ab -qc 7200 -n 450000 http://localhost/block/35237| grep Requests\ per\ secRequests per second: 7693.35 [#/sec] (mean)#
![Page 28: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/28.jpg)
Killing feature!
Начиная со второй недели разработки (как только был написан каркас), приложение было готово к работе.
В любой момент не работал только тот функционал, который не был дописан.
● Ни отладки● Ни непредусмотренного поведения● Только профилирование
![Page 29: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/29.jpg)
Killing feature!
● Ни отладки● Ни непредусмотренного поведения
В Erlang есть концепция «Let it crash».Близкий перевод – «Ну и хрен с ним».
![Page 30: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/30.jpg)
Let it crash
● На обычном языке программирования:
res = web_server.start_link(callback = Fun)
if res == web_server.port_in_use: raise Exception("Port in use")elif res == web_server.socket_error: raise Exception("Socket error")elif res == errno.EACCES: raise Exception("Not enough privileges")
![Page 31: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/31.jpg)
Let it crash
● На Erlang:
{ok, Pid} = misultin:start_link([{loop, Fun}]).
![Page 32: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/32.jpg)
Let it crash
● На Erlang:
{ok, Pid} = misultin:start_link([{loop, Fun}]).
● Если что-то шандарахнется, то предположениеmisultin:start_link/1 => {ok, _}
окажется неверным, и поток вылетит самс сообщением, например, таким:{badmatch, {error, eacces}}
![Page 33: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/33.jpg)
Результат
● Один человекомесяц● Быстрое веб-приложение● Стабильное веб-приложение – за счёт eunit
и «Let it crash»● Минимум кода – за счёт множества
встроенных примитивов и «Let it crash»● Минимум требуемого опыта – Erlang
изучается за 2 недели
![Page 34: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/34.jpg)
Уровень программиста
● С одной стороны, Erlang учится за 2 недели● С другой стороны, нужно иметь навыки
программирования. Not all batteries included
Для написания баннерокрутилки в разное время требовался то JSON-декодер, то перекодировка из UTF8 в CP1251, то htmlspecialchars(). Ничего этого в stdlib нет, нужно брать сторонние библиотеки, оценивать их работоспособность и производительность.
![Page 35: Баннерокрутилка на Erlang](https://reader033.fdocument.pub/reader033/viewer/2022052412/557cb1cbd8b42a9c528b4a0d/html5/thumbnails/35.jpg)
Напутствие
● Предобрабатывай данные, пока это дёшево!● Не выполняй одни и те же операции дважды!● Используй «Let it crash» в интерфейсах
собственного кода!● Профилируй!● Переписывай медленные операции на C,
PHP, OCaml, whatever: существуют открытые биндинги