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