SlideShare a Scribd company logo
Баннерокрутилка:
как это было на Erlang
Задача
●   Выдача ссылок на новости
    ●   База из тысяч новостей
    ●   Ссылки выбираются произвольно
    ●   Распределение – неравномерное
●   Форматирование ссылок перед выдачей
    ●   Ссылки должны иметь формат блока
    ●   Дополнительные поля: идентификатор блока, …
Ta-da!
●   Требования: >= 5000 запросов в секунду
    ●   Не справляемся – должны быстро выдавать
        пустую страницу, чтобы не «ломать» вёрстку
    ●   Никакого кэширования
●   Срок выполнения: полтора месяца.
    ●   При этом у разработчика баннерокрутилки
        есть и другие задачи
Структура решения
●   Структуры в памяти, хранящие новости
    и статистику показов
●   Множество потоков, обрабатывающих
    HTTP-запросы

●   Контроллер
    ●   добавляющий и удаляющий новости
    ●   собирающий статистику
    ●   на основе TCP-сокетов
Структура решения


Множество потоков
Erlang
●   DSL для многопоточных приложений
●   Встроенные в язык примитивы для посылки
    и приёма сообщений
●   Встроенные в язык шаблоны поведения
●   Встроенная в язык in-memory БД
Структура решения
●   Структуры в памяти, хранящие новости и
    статистику показов: mnesia/ets/dict!
●   Множество потоков, обрабатывающих HTTP-
    запросы: gen_server!

●   Контроллер: gen_tcp!
Структура решения
●   Структуры в памяти, хранящие новости и
    статистику показов: mnesia/ets/dict!
●   Множество потоков, обрабатывающих HTTP-
    запросы: gen_server!
    ●   который уже написан за нас! mochiweb/misultin
●   Контроллер: gen_tcp!


    Batteries included
Batteries included, though not all
Основная часть решения
●   HTTP-сервер Mochiweb получает запрос,
    создаёт поток
●   Поток забирает из Mnesia данные
●   Поток производит выборку, сортирует
    данные, форматирует строки, выдаёт,
    умирает.
    Все счастливы.


            «In theory, there's no difference between
            theory and practice. In practice, there is».
                           L. A. van der Snepscheut.
Факап #1
    У каждой новости есть коэффициент
    важности. В соответствии с этим
    коэффициентом необходимо выдавать
    новость чаще или реже остальных.

●   Перед выдачей нужно назначать
    взвешенные произвольные числа каждой
    новости и делать по ним выборку.
●   Новостей много.
Факап #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.
Эврика!
●   Вместо ETS напишем собственную структуру
    данных на основе gen_server, dict, queue,
    blackjack и hookers.
●   Повесим её в виде отдельного потока
●   Будем делать там грубую предвыборку
    новостей, которые потом быстро
    скопируются в рабочий поток
●   Результат:
        рост производительности в 3 раза


●   Вывод:
    ●   всегда думай, какие объёмы данных копируешь!
    ●   профилируй!
Основная часть решения v0.2
●   HTTP-сервер Mochiweb получает запрос,
    создаёт поток
●   Поток отправляет запрос в gen_server
●   gen_server производит предвыборку
    новостей и присылает результат
●   Поток производит выборку, сортирует
    данные, форматирует строки, выдаёт,
    умирает.
    Все счастливы.
Факап #2
    Новости – это текст.
    Текст – это строки.

●   Строки в Erlang – это связные списки
    символов
●   IO в Erlang – это очень медленно
Эврика!
●   Если вы пишете на Erlang,
    то строка символов записывается так:

                  "Hello world!~n"

●   Конкатенация записывается так:

           "Hello " ++ Username ++ "!~n"
Эврика!
●   Если вы пишете на Erlang веб-приложения,
    то строка символов записывается так:

               <<"Hello world!~n">>

●   Конкатенация записывается так:

        [<<"Hello ">>, Username, <<"!~n">>]
Почему:
●   <<>> – встроенный бинарный тип
●   <<"">> – бинарная строка
●   Списки символов нужно обрабатывать
    перед выдачей
●   Вывод бинарных данных – это просто
    вызов writev(2)
    ●   Blazingly Fast
Почему:
●   "Hello" ++ "!n" => "Hello!n" => строка
    ●   Конкатенация списков – O(n)
    ●   Вывод строки => цикл по списку из 7 символов
●   [<<"Hello">>, <<"!n">>] – тип iolist().
    ●   Добавление в начало списка – O(1)
    ●   Вывод => цикл по списку из 2 элементов
    ●   Встроенным функциям I/O всё равно, что
        выводить
Кроме того
●   Строковые операции, наподобие обработки
    регулярных выражений, всё равно дорогие
●   Впрочем, они вообще не очень дешевы.
    Обработку данных нужно делать не на этапе
    выдачи, а на этапе помещения в базу – до
    тех пор, пока позволяют объёмы памяти и
    специфика решаемой задачи.
●   В данном случае кэширование
    конструируемых URL новостей и т. п.
    позволило отыграть 15%
●   Результат:
        рост производительности в 10 (десять) раз


●   Вывод:
    ●   всегда думай, как обрабатывать строки!
    ●   профилируй!
Основная часть решения v0.3
●   HTTP-сервер Mochiweb получает запрос,
    создаёт поток
●   Поток отправляет запрос в gen_server
●   gen_server производит предвыборку
    новостей и присылает результат
●   Поток производит выборку, сортирует
    данные, форматирует iolist()'ы, выдаёт,
    умирает.
профилируй!
Основная часть решения v0.4
●   HTTP-сервер Misultin получает запрос,
    создаёт поток
●   Поток отправляет запрос в gen_server
●   gen_server производит предвыборку
    новостей и присылает результат
●   Поток производит выборку, сортирует
    данные, форматирует iolist()'ы, выдаёт,
    умирает.
Misultin
●   Реализация gen_server, как и Mochiweb
●   Интерфейс, абсолютно аналогичный
    Mochiweb
●   Стабильно на 10-15% быстрее
Результат
●   Один человекомесяц
●   Быстрое веб-приложение

    # ab -qc 7200 -n 450000 http://localhost/block/35237
    | grep Requests per sec
    Requests per second:    7693.35 [#/sec] (mean)
    #
Killing feature!
    Начиная со второй недели разработки (как
    только был написан каркас), приложение
    было готово к работе.
    В любой момент не работал только тот
    функционал, который не был дописан.
●   Ни отладки
●   Ни непредусмотренного поведения
●   Только профилирование
Killing feature!
●   Ни отладки
●   Ни непредусмотренного поведения
    В Erlang есть концепция «Let it crash».
    Близкий перевод – «Ну и хрен с ним».
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")
Let it crash
●   На Erlang:

    {ok, Pid} = misultin:start_link([{loop, Fun}]).
Let it crash
●   На Erlang:

    {ok, Pid} = misultin:start_link([{loop, Fun}]).


●   Если что-то шандарахнется, то
    предположение
    misultin:start_link/1 => {ok, _}

    окажется неверным, и поток вылетит сам
    с сообщением, например, таким:
    {badmatch, {error, eacces}}
Результат
●   Один человекомесяц
●   Быстрое веб-приложение
●   Стабильное веб-приложение – за счёт eunit
    и «Let it crash»
●   Минимум кода – за счёт множества
    встроенных примитивов и «Let it crash»
●   Минимум требуемого опыта – Erlang
    изучается за 2 недели
Уровень программиста
●   С одной стороны, Erlang учится за 2 недели
●   С другой стороны, нужно иметь навыки
    программирования. Not all batteries included

    Для написания баннерокрутилки в разное время
    требовался то JSON-декодер, то перекодировка из
    UTF8 в CP1251, то htmlspecialchars(). Ничего этого
    в stdlib нет, нужно брать сторонние библиотеки,
    оценивать их работоспособность и
    производительность.
Напутствие
●   Предобрабатывай данные, пока это дёшево!
●   Не выполняй одни и те же операции дважды!
●   Используй «Let it crash» в интерфейсах
    собственного кода!
●   Профилируй!
●   Переписывай медленные операции на C,
    PHP, OCaml, whatever: существуют открытые
    биндинги

More Related Content

PDF
"Производительность MySQL: что нового?"
PPTX
Быстрый старт iOS приложения на примере iOS Почты Mail.Ru / Николай Морев (Ma...
PDF
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)
PDF
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
PPT
Node.JS: возможности для РНР-разработчика
PDF
Профилирование кода на C/C++ в *nix системах
PDF
Erlang railsclub - 1
PDF
"Производительность MySQL: что нового?"
Быстрый старт iOS приложения на примере iOS Почты Mail.Ru / Николай Морев (Ma...
Отладка производительности приложения на Erlang / Максим Лапшин (Erlyvideo)
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Node.JS: возможности для РНР-разработчика
Профилирование кода на C/C++ в *nix системах
Erlang railsclub - 1

What's hot (20)

KEY
Erlang&rails
PDF
Database First! О распространённых ошибках использования РСУБД
PDF
Максим Лапшин — введение в Erlang
PDF
ELK: менеджмент логов, быстрая локализация проблем / Сергей Шумов (News360)
PDF
Эффективная отладка репликации MySQL / Света Смирнова (Percona)
PPTX
Оптимизация высоконагруженных ASP.NET приложений, работающих с MS SQL Server ...
PDF
Быстрый рендеринг с DOM шаблонизаторами / Борис Каплуновский (aviasales.ru)
PDF
Асинхронная репликация без цензуры, Олег Царёв (Mail.ru Group)
PDF
"Мы два месяца долбались, а потом построили индекс" (c) Аксенов
PDF
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
PDF
#noBackend, или Как выжить в эпоху толстеющих клиентов
PDF
Опыт использования Erlang в разработке многопользовательской игры
PDF
My talk on PgDay Russia 2014
PDF
Конструктор / Денис Паясь (Яндекс)
PDF
JPHP - О проекте на простом языке
PDF
My talk on administering PostgreSQL
PDF
Доклад Виталия Котова на конференции LoveQA. "Selenium тесты. От RC и одного ...
PDF
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения ...
PDF
libfpta — обгоняя SQLite и Tarantool / Леонид Юрьев (Positive Technologies)
PPTX
мир без Jsp. thymeleaf 2.0
Erlang&rails
Database First! О распространённых ошибках использования РСУБД
Максим Лапшин — введение в Erlang
ELK: менеджмент логов, быстрая локализация проблем / Сергей Шумов (News360)
Эффективная отладка репликации MySQL / Света Смирнова (Percona)
Оптимизация высоконагруженных ASP.NET приложений, работающих с MS SQL Server ...
Быстрый рендеринг с DOM шаблонизаторами / Борис Каплуновский (aviasales.ru)
Асинхронная репликация без цензуры, Олег Царёв (Mail.ru Group)
"Мы два месяца долбались, а потом построили индекс" (c) Аксенов
#RuPostges в Yandex, эпизод 3. Что же нового в PostgreSQL 9.6
#noBackend, или Как выжить в эпоху толстеющих клиентов
Опыт использования Erlang в разработке многопользовательской игры
My talk on PgDay Russia 2014
Конструктор / Денис Паясь (Яндекс)
JPHP - О проекте на простом языке
My talk on administering PostgreSQL
Доклад Виталия Котова на конференции LoveQA. "Selenium тесты. От RC и одного ...
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения ...
libfpta — обгоняя SQLite и Tarantool / Леонид Юрьев (Positive Technologies)
мир без Jsp. thymeleaf 2.0
Ad

Similar to Баннерокрутилка на Erlang (20)

ODP
GetDev.NET: Снова Эрланг
KEY
Отличие Erlang от объектных языков
PDF
Erlang and OCaml Experience at Echo
PDF
Лев Валкин — Кое-что про Erlang
PDF
Дмитрий Грошев, Фёдор Гоголев. Erlang и Haskell в production: проблемы и решения
KEY
Erlang for Yandex
PDF
Erlang мгновенное просветление
KEY
Что и почему писать на Erlang
PDF
Operden1
PDF
Erlang tasty & useful stuff
PDF
Введение в Python и Django
PPT
что и почему вы должны программировать на Erlang.максим лапшин. зал 4
PDF
Erlang: прагматичный рассказ про прагматичный язык
PDF
PDF
Erlang, который мы потеряли
PDF
Erlang ruby
PDF
“Чем хорош Erlang вообще и для веб-разработки в частности?”

PDF
Максим Лапшин. Erlang production
PDF
Там, где Rails не справляются
PDF
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
GetDev.NET: Снова Эрланг
Отличие Erlang от объектных языков
Erlang and OCaml Experience at Echo
Лев Валкин — Кое-что про Erlang
Дмитрий Грошев, Фёдор Гоголев. Erlang и Haskell в production: проблемы и решения
Erlang for Yandex
Erlang мгновенное просветление
Что и почему писать на Erlang
Operden1
Erlang tasty & useful stuff
Введение в Python и Django
что и почему вы должны программировать на Erlang.максим лапшин. зал 4
Erlang: прагматичный рассказ про прагматичный язык
Erlang, который мы потеряли
Erlang ruby
“Чем хорош Erlang вообще и для веб-разработки в частности?”

Максим Лапшин. Erlang production
Там, где Rails не справляются
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Ad

Баннерокрутилка на Erlang

  • 2. Задача ● Выдача ссылок на новости ● База из тысяч новостей ● Ссылки выбираются произвольно ● Распределение – неравномерное ● Форматирование ссылок перед выдачей ● Ссылки должны иметь формат блока ● Дополнительные поля: идентификатор блока, …
  • 3. Ta-da! ● Требования: >= 5000 запросов в секунду ● Не справляемся – должны быстро выдавать пустую страницу, чтобы не «ломать» вёрстку ● Никакого кэширования ● Срок выполнения: полтора месяца. ● При этом у разработчика баннерокрутилки есть и другие задачи
  • 4. Структура решения ● Структуры в памяти, хранящие новости и статистику показов ● Множество потоков, обрабатывающих HTTP-запросы ● Контроллер ● добавляющий и удаляющий новости ● собирающий статистику ● на основе TCP-сокетов
  • 6. Erlang ● DSL для многопоточных приложений ● Встроенные в язык примитивы для посылки и приёма сообщений ● Встроенные в язык шаблоны поведения ● Встроенная в язык in-memory БД
  • 7. Структура решения ● Структуры в памяти, хранящие новости и статистику показов: mnesia/ets/dict! ● Множество потоков, обрабатывающих HTTP- запросы: gen_server! ● Контроллер: gen_tcp!
  • 8. Структура решения ● Структуры в памяти, хранящие новости и статистику показов: mnesia/ets/dict! ● Множество потоков, обрабатывающих HTTP- запросы: gen_server! ● который уже написан за нас! mochiweb/misultin ● Контроллер: gen_tcp! Batteries included
  • 10. Основная часть решения ● HTTP-сервер Mochiweb получает запрос, создаёт поток ● Поток забирает из Mnesia данные ● Поток производит выборку, сортирует данные, форматирует строки, выдаёт, умирает. Все счастливы. «In theory, there's no difference between theory and practice. In practice, there is». L. A. van der Snepscheut.
  • 11. Факап #1 У каждой новости есть коэффициент важности. В соответствии с этим коэффициентом необходимо выдавать новость чаще или реже остальных. ● Перед выдачей нужно назначать взвешенные произвольные числа каждой новости и делать по ним выборку. ● Новостей много.
  • 12. Факап #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.
  • 13. Эврика! ● Вместо ETS напишем собственную структуру данных на основе gen_server, dict, queue, blackjack и hookers. ● Повесим её в виде отдельного потока ● Будем делать там грубую предвыборку новостей, которые потом быстро скопируются в рабочий поток
  • 14. Результат: рост производительности в 3 раза ● Вывод: ● всегда думай, какие объёмы данных копируешь! ● профилируй!
  • 15. Основная часть решения v0.2 ● HTTP-сервер Mochiweb получает запрос, создаёт поток ● Поток отправляет запрос в gen_server ● gen_server производит предвыборку новостей и присылает результат ● Поток производит выборку, сортирует данные, форматирует строки, выдаёт, умирает. Все счастливы.
  • 16. Факап #2 Новости – это текст. Текст – это строки. ● Строки в Erlang – это связные списки символов ● IO в Erlang – это очень медленно
  • 17. Эврика! ● Если вы пишете на Erlang, то строка символов записывается так: "Hello world!~n" ● Конкатенация записывается так: "Hello " ++ Username ++ "!~n"
  • 18. Эврика! ● Если вы пишете на Erlang веб-приложения, то строка символов записывается так: <<"Hello world!~n">> ● Конкатенация записывается так: [<<"Hello ">>, Username, <<"!~n">>]
  • 19. Почему: ● <<>> – встроенный бинарный тип ● <<"">> – бинарная строка ● Списки символов нужно обрабатывать перед выдачей ● Вывод бинарных данных – это просто вызов writev(2) ● Blazingly Fast
  • 20. Почему: ● "Hello" ++ "!n" => "Hello!n" => строка ● Конкатенация списков – O(n) ● Вывод строки => цикл по списку из 7 символов ● [<<"Hello">>, <<"!n">>] – тип iolist(). ● Добавление в начало списка – O(1) ● Вывод => цикл по списку из 2 элементов ● Встроенным функциям I/O всё равно, что выводить
  • 21. Кроме того ● Строковые операции, наподобие обработки регулярных выражений, всё равно дорогие ● Впрочем, они вообще не очень дешевы. Обработку данных нужно делать не на этапе выдачи, а на этапе помещения в базу – до тех пор, пока позволяют объёмы памяти и специфика решаемой задачи. ● В данном случае кэширование конструируемых URL новостей и т. п. позволило отыграть 15%
  • 22. Результат: рост производительности в 10 (десять) раз ● Вывод: ● всегда думай, как обрабатывать строки! ● профилируй!
  • 23. Основная часть решения v0.3 ● HTTP-сервер Mochiweb получает запрос, создаёт поток ● Поток отправляет запрос в gen_server ● gen_server производит предвыборку новостей и присылает результат ● Поток производит выборку, сортирует данные, форматирует iolist()'ы, выдаёт, умирает.
  • 25. Основная часть решения v0.4 ● HTTP-сервер Misultin получает запрос, создаёт поток ● Поток отправляет запрос в gen_server ● gen_server производит предвыборку новостей и присылает результат ● Поток производит выборку, сортирует данные, форматирует iolist()'ы, выдаёт, умирает.
  • 26. Misultin ● Реализация gen_server, как и Mochiweb ● Интерфейс, абсолютно аналогичный Mochiweb ● Стабильно на 10-15% быстрее
  • 27. Результат ● Один человекомесяц ● Быстрое веб-приложение # ab -qc 7200 -n 450000 http://localhost/block/35237 | grep Requests per sec Requests per second: 7693.35 [#/sec] (mean) #
  • 28. Killing feature! Начиная со второй недели разработки (как только был написан каркас), приложение было готово к работе. В любой момент не работал только тот функционал, который не был дописан. ● Ни отладки ● Ни непредусмотренного поведения ● Только профилирование
  • 29. Killing feature! ● Ни отладки ● Ни непредусмотренного поведения В Erlang есть концепция «Let it crash». Близкий перевод – «Ну и хрен с ним».
  • 30. 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")
  • 31. Let it crash ● На Erlang: {ok, Pid} = misultin:start_link([{loop, Fun}]).
  • 32. Let it crash ● На Erlang: {ok, Pid} = misultin:start_link([{loop, Fun}]). ● Если что-то шандарахнется, то предположение misultin:start_link/1 => {ok, _} окажется неверным, и поток вылетит сам с сообщением, например, таким: {badmatch, {error, eacces}}
  • 33. Результат ● Один человекомесяц ● Быстрое веб-приложение ● Стабильное веб-приложение – за счёт eunit и «Let it crash» ● Минимум кода – за счёт множества встроенных примитивов и «Let it crash» ● Минимум требуемого опыта – Erlang изучается за 2 недели
  • 34. Уровень программиста ● С одной стороны, Erlang учится за 2 недели ● С другой стороны, нужно иметь навыки программирования. Not all batteries included Для написания баннерокрутилки в разное время требовался то JSON-декодер, то перекодировка из UTF8 в CP1251, то htmlspecialchars(). Ничего этого в stdlib нет, нужно брать сторонние библиотеки, оценивать их работоспособность и производительность.
  • 35. Напутствие ● Предобрабатывай данные, пока это дёшево! ● Не выполняй одни и те же операции дважды! ● Используй «Let it crash» в интерфейсах собственного кода! ● Профилируй! ● Переписывай медленные операции на C, PHP, OCaml, whatever: существуют открытые биндинги