Прошел #Highload-2023. Я был только первый день, как обычно, публиковал заметки с докладов, но успел не все. Поэтому решил быстро собрать и опубликовать отчет. Highload вернулся в кампус Сколково из Крокуса. Участников - много, 3600+, это в полтора с лишним раза больше, чем было на нем в Сколково в предыдущий раз, когда организаторы подумали, что тесно и надо менять место. Но эксперимент с Крокусом не получился - там нельзя сделать столько маленьких залов, и в одном секторе там тоже уже становится тесно, а брать два или большое помещение для выставок дорого.
Про место могут быть разные оценки. Лично я считаю, что 11 параллельных треков - преимущество конференции, а сумбурная геометрия Сколково дает дополнительный позитивный штрих к атмосфере. В целом конференция - удалась.
У меня на этом Highload получилось погрузиться в технологические доклады, раньше так получалось только на Питерском highload. Наверное, помог обучающий трек. Кстати, на многих его докладах было переполнение зала. В общем, переполнение зала - типичная проблема конференций, но тут, возможно, организаторам стоит учесть, что многие участники на конференции хотят заглянуть в какие-то технологии на уровне общего понимания, услышать об этом интегрированное мнение от докладчика, сравнить со своим.
Дальше - мои заметки по докладам в ходе конференции, часть была опубликована постами на канале, а несколько - только здесь. При таком количестве треков это - точно не репрезентативный отбор. И на некоторых таймслотах я зависал в общении - на конференции много знакомых, и некоторых встречаешь там после длинного перерыва.
Заметки - в том порядке, что я слушал. Но я хочу отметить два доклада: про CQRS, там было удачное сочетание концептов и практичности, и про китайский open source дистрибутив linux OpenEuler, для которого сделали нашу open source локализацию OpenScaler. На конференции было еще несколько докладов про китайский open source, там интересная модель соборной разработки софта в рамках страны.
Еще последним слотом был Fail meetup, было интересно послушать про чужие фейлы и вспоминать свои. Хотя, строго говоря, в рассказах не фейл, там аварии. связанные с человеческим фактором, которые, тем не менее, успешно разгребли и восстановили. Реальный фейл восстановления не предполагает, ну или это делают другие люди в другом проекте. Но все равно, рассказы были любопытны. Заметок оттуда не будет - там были откровенные рассказы без записи и трансляции, на эту часть конференции надо ходить лично.
Итак, доклады.
Сергей Бережной. Эволюция сервисов и средств разработки
В докладе история технологий интернета примерно с 1997 года по площади - операционки, редакторы, базы данных, средства разработки и так далее, и мне эта история очень откликается, вызывает воспоминания и некоторую ностальгию. К сожалению, эволюции, то есть логики развития, а не просто истории фактов, в докладе нет, просто сборка логотипов. Это жаль, было бы очень интересно. В вопросах было: почему Яндекс ушел с FreeBSD на Ubuntu? Ответ - эксплуатационные характеристики для управления большим количеством машин в кластере. А смысл доклада - вспомнить именно open source решений, и таким образом показать их вклад в развитие ИТ, общность разработчиков, которые не зависит от конкуренции между компании. И анонс инициативы Яндекса по грантам для Open Source разработки.
Евгений Кульгин из CDEK. Mattermost на стероидах: как мы запустили и развиваем корпоративный мессенджер
Был телеграм, slack, skype для видео, проблемы что несколько мессенджеров и внешних, при этом slack - снаружи, нет контроля за работой сотрудников и помнит только 10к сообщений. Плюс важно держать мессенджер под контролем: он встраивается в бизнес-процессы, и отрубание их крэшит - поэтому решили развернуть локально mattermost. RocketChat тоже пробовали, но оказался нестабильным. И дальше был технический рассказ о проблемах, которые они решали: отказоустойчивость, хранение изображений, ограничение доступа к каналам, сервис, чтобы увидеть просмотр сообщения в виде привычных галочек и так далее. Они остаются на бесплатной версии и делают сервис подключаемыми плагинами, работают с подключением elastic search вместо собственного индексатора и своих уведомлений. Mattermost часть из этого дает в платной версии, но там, по отзывам, оно неудобно, например, для настройки доступа к каналам надо идти в консоль, для проверки что прочитали - жать кнопки, это не появляется как галочки и так далее. Поэтому все равно нужна собственная разработка. Делает все это один разработчик, который больше решает задачи devops. Если задумаетесь про mattermost - доклад точно полезен. Свои доработки они планируют публиковать без кодов (готовили к выступлению, не успели), а если будет интерес - подумают, как открыть доступ к кодам.
Виктор Бурцев из Яндекс. Слишком… много… асинхронщины…
На что обращать внимание при работе с фичей из десятка сервисов, обрабатывающих 15 000 асинхронных задач в секунду. Поверх основного функционала Яндекс-такси есть функционал live activity - обновление состояния на клиенте, включая активность разных плашек. Все это обеспечивается большим количеством сервисов асинхронной работы, которые, преимущественно, работают по таймеру, а не по событиям - чтобы сбалансировать нагрузку. И рассказ был о том, как перестраивать архитектуру, чтобы обеспечить нужную производительность. Например, запрос от задачи в core-сервис создает недопустимую нагрузку - поэтому делаем промежуточное redis-хранилище, в котором обновление по событиям, а уже к нему идет обращение за данными. В докладе был ряд конкретных кейсов, без обобщений и выделения шаблонов. Их интересно осмыслить, если вы решаете такие задачи, может, появятся идеи уже для ваших задач. И общие рекомендации, прежде всего логи и мониторинг, при чем не общий по hardware, а внутри конкретных задач. А по организации взаимодействия - понимайте, что возможны падения и самые разные ответы смежных сервисов, это надо обрабатывать и вести себя разумно, не создавая дополнительной нагрузки.
Андрей Цветцих из Тинькофф: Эволюция и мифы CQRS
Доклад на стыке концептов и практики. Концептуально CQRS был придуман, когда запросов чтения много больше, чем запросов изменения данных. В этом случае логично отделить чтение от изменения данных, чтобы по-разному их масштабировать. Но для этого надо разделить запросы на чтение - query и изменение - command. И при этом единый класс, который обрабатывает конкретную сущность, делится, как минимум, на два обработчика: для запросов и для команд, а в перспективе для каждой команды и запроса появляется свой обработчик.
Тут было интересное концептуальное утверждение, что такое разделение классов на множество отдельных обработчиков для каждой команды и запроса, упрощает код, делает компактные процедуры, вместо нескольких тысяч строк получаются сотни. Но при этом общее количество кода не меняется, он просто ложится в разные обработчики и файлы. Это интересно, потому что фактически получается откат с ООП-парадигмы на предыдущую, процедурную парадигму разработки. ООП, когда его придумывали, как раз говорил, что объединение всей логике, связанной с отдельным доменом или сущностью в один класс как раз облегчает понимание кода, обеспечивая инкапсуляцию и скрытие внутренней логики, позволяет менять реализацию, сохраняя интерфейс за счет инкапсуляции и так далее. Это интересно обдумать...
Дальше был рассказ о практической реализации, в том числе решении задач по выделению общих частей обработки команд, которые в классике реализуются за счет приватных методов. А в этом случае их делают как отдельные сервисы, но надо заблокировать обращение снаружи, в C# это internal. Первый этап - просто разделить методы в коде и работать на общей базе, и это можно делать постепенно. Когда методы разнесены. можно распилить один сервис на два: на чтение и запись, это Андрей сказал уже в ответах на вопросы. И тут не обязательно разделять код, можно начать со специализации экземпляров. Дальше можно разнести базы для чтения и запросов, настроив репликацию master-slave и по-разному масштабируя, но у этого есть понятная обратная сторона - появляется замедление обновления данных при чтении. В коде при этом возникают два соединения, в одном запрещено обновление. В коде важно для обновлений читать предыдущее состояние из write-базы, а не из read. А следующим шагом можно вместо master-slave делать разные структуры данных, оптимизированные на запись и на чтение, с трансформацией при передаче данных. Модель для чтения может обновляться синхронно, если она носит характер кэша, но, как правило, это делают асинхронно. Асинхронное обновление не дает задержки, но нарушается консистентность и повышается сложность, надо разбираться с нарушением порядка сообщений, и вообще с ошибками обновления, которые рассинхронизируют write и read базы.
Следующая часть - мифы CQRS. Они реально мешают применению.
- Query не может менять состояние и даже писать логи. Это неверно, нельзя лишь меняем основную модель. А логе, метрики и кэши - меняем. И логи - это важно, потому что работу надо мониторить.
- Query может читать только read-модель. Это не так, если нет проблем с производительностью - читайте основную, так что в read-модель можно выгружать не все.
- Command не может читать read-модель. Это действительно так, потому что в ней не актуальные данные, и можно пропустить обновление.
- Command не может возвращать значение. Это не так. Если мы создаем сущность, можно вернуть ее ключ, если что-то обновляем, то можно вернуть данные. Однако, это требует синхронной обработки команды. Если вы захотите потому перейти на асинхронное взаимодействие, то это не получится. То есть формально можно будет новый асинхронный запрос обернуть таймером до обработки, но это может отменить эффект перехода на асинхронное взаимодействие. Хотя есть разные кейсы.
Дмитрий Варенов: Опыт изучения передовых китайских СПО-решений для построения сложных IТ-инфраструктур на базе единой программной платформы OpenScaler/OpenEuler
В Китае централизованное управление open source - OpenAtom, и это стимулируется и отчасти регулируется государством. И в применении конкретно дистрибутивам linux это регулирование требует, чтобы все китайские компании, которые создают свои дистрибутивы linux, вели единую кодовую базу Open Euler. Поэтому все доработки, патчи безопасности и многое другое попадает во все дистрибутивы. получается соборная модель разработки в масштабах страны. В то время как на западе и в России политика принципиально другая: каждая компания-создатель дистрибутива ведет собственную кодовую базу, и они развивают по-разному.
Благодаря общей кодовой базе в Open Euiler много ценных технических фич, часть из них спикер перечислил. И они решили принести все это в Россию, сделали OpenScaler - локализованный российский вариант, под нужды российского enterprise. За 1.5 года 6 версий для x86 и ARM. Тоже open source, под той же лицензией, на основе которого тоже любая компания может создавать платные дистрибутивы. Основная сложность - отсутствие документации. Код - хороший, но документация в лучшем случае на китайском, а а часто отсутствует, надо взаимодействовать с сообществом, которое тоже далеко не все говорит по-английски.
Особенности дистрибутива и некоторые инновации.
- Кросс-платформенность: OpenScaler как Open Euler собирается подо все - очень много разнообразных архитектур железа.
- A-Tune оптимизация производительности с помощью ИИ статическая и динамическая оптимизация
- Kernel Live Update - смена ядра ОС без перезагрузки сервера или виртуалки. При этом можно заморозить исполнение процессов без потери данных, и разморозить после замены ядра.
- SysCare - готовить патчсеты для запущенного сервиса и обновлять без остановки
- KubeOS - упрощение управления обновлениями ОС. Умеет обновлять узлы платформы кластерной виртуализации так, что каждое обновление - новый образ, защищенный от записи. Обновили, перегрузили, пошло не так - откатили
- NestOS - минималистичная ОС для узлов контейнерной виртуализации - выпилено много функционала. И там image, используете, хотите добавить - перезаберите образ. То есть вы можете заточить под вашу безопасность и среду.
- tMem - гибкие политики управления подкачкой. Для приложений, где много ОЗУ но с ограниченным числом обращений к ней - можно настроить политики.
- ОС для embeded-устройств. Ядро реального времени и много еще функционала для них.
- iSula - своя реализация контейнерной виртуализации. Совместима с kubernetes, но переписана с нуля и более производительная. Они исследовали по сравнению с Docker 18 и 24, и podman 3.4.4. Сделали инструмент сообщества OpenEuler для ключевых операций - create, start, stop, remove, run. Выигрывает у docker 18 и podman, с docker 24 - есть выигрыши и проигрыши на разных операциях. B будут проводить тестирование на реальных конфигурациях.
Дальше была техническая часть, как это использовать, с деталями и скринами, это надо смотреть презентацию.
Дмитрий Волков из Okko. Под капотом быстрого сплитования трафика для А/B-тестирования: оптимизация производительности и инфраструктурные уроки
На платформе идет параллельно много экспериментов, и есть задача: поделить пользователей на группы так, чтобы каждый участвовал только в одном эксперименте, и для каждого эксперимента можно было получить контрольную группу. Для каждого эксперимента определяются правила - какие участники там нужны, в зависимости от атрибутов пользователя и параметров его системы, потому что эксперименты - разные, какие-то про дизайн интерфейса, какие-то связаны с рекомендациями контента и так далее.
Архитектурное решение: реализовать для этого отдельный сервис, который бы определял категорию пользователей в динамике, а не рассчитывать эту категорию заранее - потому что очень большая база пользователей, в моменте соединяется лишь небольшая часть. При этом динамика правил - высокая, продукты все время придумывают новые фичи и хотят их протестировать. Пересчитывать каждый раз по всей базе - дорого.
Этот сервис разработали, получили ответ в 15мс при целевом 10мс, поэтому дальше решали задачу оптимизации, об этом и был основной рассказ.
- Валидация запроса - 15%, но сервис - внутренний - там не надо валидировать, это исключили.
- Расчет сегмента - 60-70% работы сервиса. Расчет - алгоритмическое выражение, ты его пишешь на json и идет расчет с подстановкой конкретных значений.
- В правилах сегментах есть общие выражения - по платформам, например. Но оценки показывают, что прирост от их вынесения на их количестве правил будет небольшим.
- Правила - логический формулы с условиями и-или и можно считать не все операнды, если значение определено без них. Но тут оценки тоже дают небольшой прирост.
- json - дерево. его в python байт-код и компилятор - решили транслировать в AST и откомпилировать, и это дало ускорение вычисления выражения вдвое.
- А дальше идея глобальной оптимизации: не вычислять правила по-одному, а построить очень большое выражение, которое бы сразу вычисляло все правила. Это сработало, правило построили на json, а затем - откомпилировали.
- В результате вычисление группы сильно ускорилось и в результате занимает всего 28% профиля, и это дало ускорение вдвое.
Но при этом на графике производительности сохранилась дисперсия и остались не очень понятные импульсы. Анализ показал, что это связано со сборкой мусора: в python два механизма: подсчет ссылок и сбор по поколениям, потому что подсчет ссылок не работает при циклических ссылках между объектами. посмотрели статистику по сборке мусора, выяснили, что сборка по поколениям регулярно происходит для объектов, которые создает Kafka. Kafka используется для
Еще в python есть сборщик мусора. Подсчет ссылок + поколенья. Ссылки - не работает для циклические ссылки. Поколенья - маленький стоп. Выгрузили логи, посмотрели время - сколько идет сбор поколений. И они начали искать, где создаются такие объекты. Оказалось, что в потоке Kafka, через который отправляют логи и что-то еще.
А еще python - однопоточный, и в реализации за место под солнцем борется основной поток и поток с кафкой. И тут важно, чтобы кафка не мешала основному процессы. Поток кафки вынесли в отдельный процесс, и начали передавать через unix pipe. B это дало очень приятный прирост - снизили время ответа и дисперсию. Потому что еще и сборка мысора в основном процессе стала эффективнее.
Еще один анализ - распределение запросов по подам. Обнаружили, что оно не равномерно. Почему так? Оказалось, что на нодах поднято несколько ingress, каждый из которых балансировал независимо, и поэтому получался эффект неравномерной нагрузки. Поскольку маршрутизация запроса дешева, то ее перевели на один из ingress, а остальные два работают в active standby режиме - и нагрузка стала равномерной.
Еще одна проблема - фон таймаутов, которые случаются в одно время: 18, 25, 5, 35, 55. Оказалось это рестарт подов и джобы обновления конфигурации внутреннего балансировщика и сканирование безопасности. В теории это можно разнести по времени на разных нодах и учесть в балансировщике, но это - сложно. Применили простое решение: на ingress-контроллере снизили таймаут до 7мс и сделали retry: если запрос попал туда, где в моменте напряженно - оправим его в другое место. И это сработало, фон ушел.
Уроки.
- Влияние алгоритма - даже выше, чем язык. Надо сразу смотреть.
- Если у вас высокие требования - проверьте, что инфраструктура в принципе может с ними справится и что для этого нужна, на какой-нибудь тривиальной реализации. Если бы они это сделали, то проблемы были бы обнаружены сразу, а не при выкатке, и над ними можно было бы работать параллельно с основной разработкой.
- Над оптимизацией работали в devops-инженеры, и у них были гипотезы про потенциальные проблемы. Можно было придти к ним на этапе проектирования и учесть. Это потенциально преждевременная оптимизация. но в данном случае понятно, что требования к сервису очень жесткие.
Никита Рьянов из Тинькофф. Как с помощью event sourcing мы организовали поставку данных и актуализацию структуры для более 2000 таблиц
На этом докладе тоже было переполнение зала, я слушал стоя, поэтому записать не получилось. Но доклад интересный, поэтому кратко напишу резюме. Идея в том, чтобы перейти от стандартного способа передачи данных в хранилище, основанном на change data capture к event sourcing для того, чтобы обеспечить возможность оперативного изменения структуры без остановки процесса, при очень большом количестве таблиц, которые непрерывно дорабатываются. При изменении данных возникает событие, которое хранит новый слепок данных, описываемый с помощью json. Сами структуры данных также описываются с помощью json и встраиваются в поток событий. И это дает возможность обработать эти изменения корректно. Решение интересное, так что если вы занимаетесь задачами передачи данных динамической структуры. не обязательно в хранилище - то стоит посмотреть.
[ Хронологический вид ]Комментарии
Войдите, чтобы комментировать.