Your SlideShare is downloading. ×
CodeFest 2014. Каплуновский Б. — Использование асинхронного I/O для снижения потребления ресурсов в движке Aviasales.ru
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

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

1,232
views

Published on

Published in: Internet

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,232
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
10
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

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