Successfully reported this slideshow.
Использование асинхронного I/O для
снижения потребления ресурсов в
движке aviasales
Каплуновский Борис
aviasales.ru
facebo...
Agenda
● Скриптовые языки и ресурсы
● Асинхронная модель выполнения
● Оптимизации и отзывчивость
● Странности Tornado
● Ст...
Что делает движок aviasales
Модель памяти нативной
программы
process one
stack
data
process two
code
libdl
libc
data
stack
● Одна и та-же память с
исп...
Модель памяти скрипта
runtime code
libdl
libc
stackstack
data data
script libs script libs
● Нативные код и
библиотеки
раз...
Сферический CGI Сервер в
вакууме
stack
data
native code
libc
libdl
datadatadatadata data
stack stackstack stack stack stack
Скриптовый CGI Сервер
stackstack
data data
script libs script libs
code code
stackstack
data data
script libs script libs
...
Оптимизации над CGI
● fastcgi - Не порождаем отдельный процесс для каждого
запроса – экономим процессорного времени на
заг...
Copy on write
● После вызова fork() состояние
памяти и родителя и потомка
одинаковые
● Делать полную копию
адресного прост...
Copy on write
● Как только один из процесс
записывает данные –
операционная система делает
личную копию страницы в
простра...
master process & copy on write
● После старта мастер процесс
грузит библиотеки и
подготавливает всё для
исполнения скрипта...
Copy on write
НЕ РАБОТАЕТ!
COW не работает потому что
● GC скриптовой среды
меняют данные
неиспользуемых обьектов в
ходе своей работы
● Скриптовые яз...
COW не
просто заставить работать
● В ruby 2.0 обещали сделать
cow friendly gc. Не
получилось!
● COW работает у google в
da...
Типичное web приложение
Значительную часть времени веб приложения
ждут ответов внешних сервисов таких как
– SQL сервер
– В...
Rails приложение aviasales
● Ожидание ответа внешних API до 30 секунд
● Работа с SQL ~1 секунда
● Потребляемая память ~300...
Синхронная модель VS
Асинхронная модель
cgi worker
stack
data
code
script
libs
cgi worker
stack
data
code
script
libs
asyn...
Асинхронная модель
Минусы
● Кооперативная многозадачность
● Если падает процесс падают все потоки
● Не для всего есть библ...
Асинхронная модель
Плюсы
● Эффективное использование памяти
● Эффективное использование памяти
● Эффективное использование...
Почему Python
– Большое и доброе community
– Обилие библиотек
– Tornado живёт в python
– Реклама google
– Хотелось попробо...
Почему Tornado
– Низкий порог вхождения
– Асинхронный
– @gen.coroutine – отличная альтернатива
колбекам
– Казался зрелым
Приложение на python/tornado
● Один процесс:
– занимает 267mb памяти
– из них 162mb разделяемой
– обрабатывает до 10
однов...
При работе с tornado помни!
● Как только вы начинаете использовать синхронный IO
всё останавливается
● Переключение контек...
При работе с tornado помни!
● Декоратор @gen.coroutine не бесплатен
● Tornado/Python приложение может умирать
● У Tornado/...
Странности Tornado
● Из коробки нет способа остановить приложение без
обрыва соединений
● Есть рецепты костылей на StackOv...
Резольвер
www.aviasales.ru → 194.87.255.204
● “Родные” резольверы операционных систем
синхронны
● Для асинхронный модели и...
Странности Tornado – Резольвер
● tornado.netutil.BlockingResolver
– Используется по умолчанию
– Использует синхронный geta...
Странности Tornado – Резольвер
● tornado.netutil.ThreadedResolver
– Вызывает getaddrinfo в отдельном потоке
python
– Overh...
Странности Tornado – Резольвер
● Мы написали простой
асинхронный резольвер для
Tornado IOLoop
– Только TCP
– Только записи...
Странности Tornado – HTTPClient
● HTTPClient создаёт не больше 10 исходящих
соединений по умолчанию
● HTTPClient умеет стр...
Странности Tornado
● Документация зачастую избегает описывать узкие
места
● Будьте готовы читать исходный код tornado чтоб...
Странности Python
● Сторонние библиотеки с нативным кодом текут и валят
приложение через одну
● Найти утечку памяти в нати...
tornado/python в production
MONIT
– Убивает рабочие процессы если они
выедают CPU
– Убивает рабочие процессы если они
прев...
tornado/python в production
HAPROXY
– Раскидывает приходящие запросы по
доступным рабочим процессам
– Балансирует нагрузку...
tornado/python в production
BENCHMARKS
– Тотальное логирование времени выполения участков
кода
– Визуализация бенчмарков н...
Что делать с ожиданием ответов
SQL сервера
Удалённый сервер DB
request
response
worker remote db
parse request
load value
build response
recv(syscall)
send(syscall)
Файловое key-value хранилище
● Содержимое файла должно быть смаплено в
адресное пространство процесса mmap
● Рабочий обьём...
Быстрее чем redis и memcached
worker
load value
Плюсы
● Не нужен внешний
сервер
● Непревзойдённая
скорость
● Не нужно пере...
Быстрее чем redis и memcached
worker
load value
Минусы
● Медленный update
данных
● Избыточность при
работе в кластере
● Ра...
Q&A
facebook.com/boris.kaplounovsky
@bskaplou
Используйте потоковую обработку
для разбора XML
● Опция streaming_callback у AsyncHTTPClient
fetch позволяет получать данн...
tornado/python в production
Приоритеты
● У разных запросов разные требования к скорости ответ
● Рабочие процессы привязыва...
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения потребления ресурсов в движке Aviasales.ru
Upcoming SlideShare
Loading in …5
×

CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения потребления ресурсов в движке Aviasales.ru

2,024 views

Published on

Published in: Internet
  • Be the first to comment

  • Be the first to like this

CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения потребления ресурсов в движке Aviasales.ru

  1. 1. Использование асинхронного I/O для снижения потребления ресурсов в движке aviasales Каплуновский Борис aviasales.ru facebook.com/boris.kaplounovsky @bskaplou
  2. 2. Agenda ● Скриптовые языки и ресурсы ● Асинхронная модель выполнения ● Оптимизации и отзывчивость ● Странности Tornado ● Странности Python ● Tornado/Python в production ● И ещё пару советов по повышению производительности...
  3. 3. Что делает движок aviasales
  4. 4. Модель памяти нативной программы process one stack data process two code libdl libc data stack ● Одна и та-же память с исполняемым кодом используется всеми процессами ● Разделяемые библиотеки грузятся в память один раз ● Не разделяются другими процессами только сегменты данных и стек
  5. 5. Модель памяти скрипта runtime code libdl libc stackstack data data script libs script libs ● Нативные код и библиотеки разделяются ● AST и байткод скриптовых библиотек хранятся в сегменте данных и поэтому НЕ разделяются ● Скриптовый код не так компактен как нативный и обычно занимает в разы больше памяти code code
  6. 6. Сферический CGI Сервер в вакууме stack data native code libc libdl datadatadatadata data stack stackstack stack stack stack
  7. 7. Скриптовый CGI Сервер stackstack data data script libs script libs code code stackstack data data script libs script libs code code stackstack data data script libs script libs code code native code libc libdl
  8. 8. Оптимизации над CGI ● fastcgi - Не порождаем отдельный процесс для каждого запроса – экономим процессорного времени на загрузку скриптов j2ee/rails/etc ● process pool - запуск и инициализация процесса до прихода запроса – снижение времени отклика ● master -Запуск родительского процесса загружающего код и делающего инициализацию. Родительский процесс порождает обработчиков клонируя себя. Процесс обрабатывающий запрос уже имеет в памяти всё необходимое. unicorn/dalvik/etc
  9. 9. Copy on write ● После вызова fork() состояние памяти и родителя и потомка одинаковые ● Делать полную копию адресного пространства при fork() расточительно ● В момент вызова fork() страницы данных родителя и потомка метятся как read-only parent childcode libdl libc data stack fork()
  10. 10. Copy on write ● Как только один из процесс записывает данные – операционная система делает личную копию страницы в пространстве процесса ● Страницы памяти в которые не пишут могут разделяться вечно parent child data stack stack data clone pages
  11. 11. master process & copy on write ● После старта мастер процесс грузит библиотеки и подготавливает всё для исполнения скрипта ● По мере необходимости мастер порождает рабочие процессы клонируя себя ● Так как в мастере уже были загружены все библиотеки дочерний процесс готов к работе мгновенно ● COW позволяет не создавать собственную копию кода в памяти master child stackstack data data script libs code native code libc libdl
  12. 12. Copy on write НЕ РАБОТАЕТ!
  13. 13. COW не работает потому что ● GC скриптовой среды меняют данные неиспользуемых обьектов в ходе своей работы ● Скриптовые языки со счётчиками ссылок модифицируют счётчики ссылок при создании новой ссылки на обьект, даже если сам обьект неизменен master child stackstack data data script libs code native code libc libdl code script libs
  14. 14. COW не просто заставить работать ● В ruby 2.0 обещали сделать cow friendly gc. Не получилось! ● COW работает у google в dalvik, но для этого им пришлось заменить jvm на dalvik master child stackstack data data script libs code native code libc libdl code script libs
  15. 15. Типичное web приложение Значительную часть времени веб приложения ждут ответов внешних сервисов таких как – SQL сервер – Внешний API – Файловый ввод вывод Всё это время ничего не происходит! Но память занята... запрос ответ logic SQL logicAPI
  16. 16. Rails приложение aviasales ● Ожидание ответа внешних API до 30 секунд ● Работа с SQL ~1 секунда ● Потребляемая память ~300mb (одним процессом) ● Разделяемая память ~4mb (код интерпретатора) ● ~300 одновременных поисков 87GB RAM/6 серверов И вся эта память простаивала! запрос ответ logic SQL logicAPI
  17. 17. Синхронная модель VS Асинхронная модель cgi worker stack data code script libs cgi worker stack data code script libs async worker stack script libs code native code libc thread data thread data thread data cgi worker stack data code script libs native code libc
  18. 18. Асинхронная модель Минусы ● Кооперативная многозадачность ● Если падает процесс падают все потоки ● Не для всего есть библиотеки ● Отсутствие изоляции ● Примитивный планировщик ● Нет готовых решений
  19. 19. Асинхронная модель Плюсы ● Эффективное использование памяти ● Эффективное использование памяти ● Эффективное использование памяти ● Эффективное использование памяти ● Эффективное использование памяти
  20. 20. Почему Python – Большое и доброе community – Обилие библиотек – Tornado живёт в python – Реклама google – Хотелось попробовать
  21. 21. Почему Tornado – Низкий порог вхождения – Асинхронный – @gen.coroutine – отличная альтернатива колбекам – Казался зрелым
  22. 22. Приложение на python/tornado ● Один процесс: – занимает 267mb памяти – из них 162mb разделяемой – обрабатывает до 10 одновременных запросов – больше не ждёт SQL сервер, все данные в адресном пространстве процесса – ~ 500 одновременных исходящих соединений – 2 сервера/8GB памяти async worker stack script libs code native code libc data data data
  23. 23. При работе с tornado помни! ● Как только вы начинаете использовать синхронный IO всё останавливается ● Переключение контекста происходит ТОЛЬКО на I/O и yield внутри @gen.coroutine ● Неделимый кусок кода не должен исполняться больше XXXms (мы выбрали 100ms)
  24. 24. При работе с tornado помни! ● Декоратор @gen.coroutine не бесплатен ● Tornado/Python приложение может умирать ● У Tornado/Python приложения может течь память ● Только профилировщик точно покажет кто ест CPU ● Python используется как клей для нативных библиотек, сложные алгоритмы на python реализовывать не надо
  25. 25. Странности Tornado ● Из коробки нет способа остановить приложение без обрыва соединений ● Есть рецепты костылей на StackOverflow ● Но этого мало – пришлось изобретать ещё костылей
  26. 26. Резольвер www.aviasales.ru → 194.87.255.204 ● “Родные” резольверы операционных систем синхронны ● Для асинхронный модели исполнения нужен асинхронный резольвер
  27. 27. Странности Tornado – Резольвер ● tornado.netutil.BlockingResolver – Используется по умолчанию – Использует синхронный getaddrinfo – Не кеширует результаты – Обращение к DNS при каждом HTTP запросе – Пока DNS сервер не ответил всё стоит
  28. 28. Странности Tornado – Резольвер ● tornado.netutil.ThreadedResolver – Вызывает getaddrinfo в отдельном потоке python – Overhead на потоки: память, cpu, GIL – Работает но выглядит как костыль
  29. 29. Странности Tornado – Резольвер ● Мы написали простой асинхронный резольвер для Tornado IOLoop – Только TCP – Только записи A и CNAME – Кеширование ответов DNS по TTL – Большинство преобразований делается без системных вызовов
  30. 30. Странности Tornado – HTTPClient ● HTTPClient создаёт не больше 10 исходящих соединений по умолчанию ● HTTPClient умеет стримить ответ сервера только если ответ chunked
  31. 31. Странности Tornado ● Документация зачастую избегает описывать узкие места ● Будьте готовы читать исходный код tornado чтобы понять поведение системы
  32. 32. Странности Python ● Сторонние библиотеки с нативным кодом текут и валят приложение через одну ● Найти утечку памяти в нативном коде крайне сложно ● Встроенная библиотеку xml.etree может приводить к SEGFAULT, мы используем lxml ● Сложные регулярные выражения могут остановить приложение busy-wait
  33. 33. tornado/python в production MONIT – Убивает рабочие процессы если они выедают CPU – Убивает рабочие процессы если они превысили лимит по памяти – Стартует рабочие процессы если те умерли сами или были убиты – Простой и удобный web интерфейс
  34. 34. tornado/python в production HAPROXY – Раскидывает приходящие запросы по доступным рабочим процессам – Балансирует нагрузку отправляя запросы к процессам с наименьшим количеством активных соединений – Адски быстрый и простой – Простой и удобный веб интерфейс
  35. 35. tornado/python в production BENCHMARKS – Тотальное логирование времени выполения участков кода – Визуализация бенчмарков на видном месте – Немедленная реакция на аномалии в скорости ответов сервера
  36. 36. Что делать с ожиданием ответов SQL сервера
  37. 37. Удалённый сервер DB request response worker remote db parse request load value build response recv(syscall) send(syscall)
  38. 38. Файловое key-value хранилище ● Содержимое файла должно быть смаплено в адресное пространство процесса mmap ● Рабочий обьём должен умещаться в оперативной памяти ● База должна позволять нескольким процессам одновременно читать данные без блокировок ● Мы используем kyoto cabinet и он прекрасен
  39. 39. Быстрее чем redis и memcached worker load value Плюсы ● Не нужен внешний сервер ● Непревзойдённая скорость ● Не нужно переключать контекст и делать syscall ● Высокая отказоустойчивость
  40. 40. Быстрее чем redis и memcached worker load value Минусы ● Медленный update данных ● Избыточность при работе в кластере ● Работает только для небольшого кол-ва данных
  41. 41. Q&A facebook.com/boris.kaplounovsky @bskaplou
  42. 42. Используйте потоковую обработку для разбора XML ● Опция streaming_callback у AsyncHTTPClient fetch позволяет получать данные по мере поступления ● Метод lxml.etree.XMLParser.feed позволяет парсить xml по кускам ● Если и это не помогает, делаем IOLoop.instance().add_timeout(time()) чтобы разбить поток исполнения
  43. 43. tornado/python в production Приоритеты ● У разных запросов разные требования к скорости ответ ● Рабочие процессы привязываются к одной или нескольким группа приоритета ● Haproxy отправляет запросы в соответствующую группу рабочих процессов

×