2018-03-26 Technopolis Typical Architecture.md
Лекции Технополиса. Проектирование высоконагруженных систем (осень 2017).
Tags: читательский дневник, высоконагруженные системы, конспект, спойлеры
Лекция #2. HIGHLOAD. Типовые архитектуры | Технострим
Докладчик проходится по следующим темам построения архитектуры бэкенда: - развитие от толстых клиентов (приложение) в тонкие (web) и обратно в толстые (web) - ресурсы серверов, способы оптимизации одного ресурса за счет другого - переход от вертикального масштабирования серверов к горизонтальному, как распределить бэкенд при горизонтальном масштабировании - работа с централизованной БД и переход к распределенным БД - ACID, CAP, проблемы кешей - очереди - микросервисная архитектура
Перечислены плюсы, минусы и стоимость перехода на более сложную архитектуру, описаны типовые проблемы обеспечения консистентности и их решения.
Доклад хорошо воспринимается на скорости х1,5
Далее вольный конспект.
Сравнение архитектур
Толстый клиент | Тонкий клиент(web) | Толстый клиент(web) | |
---|---|---|---|
Трафик | + | - | + |
Задержки | + | - | |
Кэширование | + | - | + |
Постоянное соединение | + | - | + |
Пуши | + | - | + |
Работа в оффлайн | + | - | + |
Сервер без состояния | + | - | - |
Обновление | - | + | + |
Совместимость API | - | + | + |
Консистентность состояния | - | + | + |
Локализация | - | + | + |
Эксперименты | - | + | + |
Скорость разработки | + | + |
Проблемы производительности (при росте продукта):
- Пропускная способность. Увеличивается объемы трафика, нужно чтобы сеть и оборудование могло с объемом справляться
- Ошибки. Пользователь не должен получать ошибки, при горизонтальном масштабировании нужно уметь повторять запросы и уметь определять в каком месте произошла ошибка (что трудно, когда много серверов, либо когда один запрос обрабатывает несколько сервисов)
- Время обслуживания. В 99.9 процентиле, запрос пользователя должен обрабатываться менее, чем за 300мс. При внедрении микросервисов нужно учитывать повышение латентности обработки запроса, по сравнению с монолитом
Нужно уметь считать потребление основных ресурсов сервера: память, процессор, диск, сеть и понимать, что один ресурс можно менять на другой.
Нужно понимать сколько пользователей вашего сервиса поместится в память (актуально для демонов с БД в памяти), за счет применения других структур данных можно сократить потребление памяти за счет потребления процессора. Память работает не так быстро, как процессор, за счет больших вычислений и меньшего обращения к памяти - можно повысить утилизацию процессора.
У процессора можно определить процент полезной работы вашего кода от системного времени ОС(system) и времени ожидания IO (iowait). Возможно, стоит ускорить работу за счет тюнинга/настройки ОС, либо определить что алгоритм не оптимально обращается к системе (слишком много ждет).
Так как гигагерцы уже так сильно не растут, следует уделять внимание многопоточному программированию.
Стоит учитывать возможные объемы дискового пространства сервера, латентность доступа к диску и количество операций/байт с диском.
У сети тоже нужно знать латентность и объем доступной полосы пропускания, в ДЦ она на порядок ниже, чем доступ к диску. Также не стоит забывать о возможном лимите на количество сетевых пакетов, т.к. это нагружает сетевой стек.
Оптимизации утилизации ресурсов. Можно обменивать в разные стороны:
- процессор/память. С помощью более вычислительно-емких структур данных сократить объем данных в памяти (например, применить сжатие). С помощью избыточных данных в памяти производить меньше расчетов на CPU (например, расширить структуру данных предрасчитанными полями или кешем).
- память/диск. Кэшировать данные с диска в памяти. Переносить редко-используемые данные на диск.
- процессор/объем. Пропускная способность/задержка. Обрабатывать запросы/данные пачками (векторные операции в cpu, запись нескольких запросов на диск одним большим блоком, группировка запросов, перед отправкой в сеть) - увеличит пропускную способность системы, но повысит время ожидания обработки одного запроса. Часто за счет этих оптимизаций происходит неверное трактование результатов бенчмарков. Например, БД отдает данные быстрее, чем может их читать с диска (или записать), либо пакетная обработка запросов работает быстрее, чем способен выдержать сетевой стек при одном запросе на пакет. Нужно учитывать эти допущения при исследовании.
Разделение бэкенда:
- Веб сервер сервер логики. Позволит делать разные "фронтенды" к логике (веб-сервер, API-сервер, клиент для мобилок), проще тестировать, поможет в архитектурном разделении кода.
- Шардирование по веб-серверам. Средняя сложность. Требуется маршрутизирование до веб-сервера. Если на веб-сервере есть состояние, то клиента нужно закреплять за веб-сервером, либо выносить состояние на внешний сервис.
- Функциональный сервис. Малая сложность. Если БД будет внешняя, то можно реализовать собственную функцию шардирования на веб-сервере и больше проблем не будет.
- БД. Максимальная сложность. Проблема единой точки отказа, лага в репликации, отсутствия атомарности при обновлении нескольких БД.
Проблема отказов. Если сервер, при выполнении запроса клиента возвращает ошибку, что показывать клиенту?
- Сдаваться, нельзя повторять (at most once) - отобразить ошибку клиенту, пусть он сам решит что с ней делать
- Сдаваться нельзя, повторять (at least once) - повторять запрос, пока он не выполнится Проблема в том, что при отказе непонятно, выполнился запрос (например, была ли запись в БД) или нет. Возможно, что дублирующий запрос продублирует данные. Для этого нужно реализовать идемпотентные операции (повтор которых не приводит к порче или дублировании информации). Например, перед отправкой запроса на добавление нового сообщения в чат, получить от сервера ID нового сообщения. В этом случае, при повторной отправке, по ID сообщения, БД сможет понять есть ли у нее уже это сообщение.
Оптимизации работы с СУБД:
- построить индексы по всем использующимся запросам
- использовать только короткие транзакции (не выполнять долгие операции в коде, между началом и окончанием транзакции, например сетевой запрос)
- денормализировать данные для более быстрого доступа к ним
- минимализировать логику, хранящуюся в СУБД
- минимум Foreign Key (???)
Проблемы распределенных БД:
- Мастер-слейв. При синхронной репликации, отказ слейва приводит к неработоспособности мастера, либо к неконсистентности слейва. При асинхронной репликации данные на слейве будут запаздывать. Возможна проблема, когда после запроса на запись к мастеру идет запрос на чтение к слейву и данные не находятся, лечится отправкой близжайших запросов на чтение к мастеру, а лаг других клиентов, выполняющих чтение считается допустимым.
- Мастер-мастер. Проблема конфликтов. Лечится кворумом (большинство право), либо работой с разрешением конфликтов: автоматически last win в Cassandra, либо вручную: Vector Clock в Voldemort/Riak, с помощью оптимистичной блокировки (CAS, compare and swap), paxos, структурами данных без конфликтов (Conflict-free replicated data type CRDT, умеет не все).
Проблемы кэширования:
- холодный старт. При рестарте кеша с потерей состояния БД может не справиться с количеством запросов. Решение: система кэширования должна сохранять состояние на диск.
- отказы при обновлении кеша. В БД информацию обновили, а при обновлении кеша произошла ошибка. Решается отдельным процессом обновления значений в кеше: сохранение timestamp изменений в БД и вычитывании свежих изменений фоновым процессом и применение его в кеш, либо посылка лога репликации на сервер кеша и его применение к кешу. Допускается лаг обновления данных в кеше, зато данные в конечном счете придут.
Очереди.
- низкое время обработки запросов
- масштабируемость обработчиков
- эластичность. Скачки количества запросов не приведут к отказам, просто увеличится время их выполнения
- отказоустойчивость
- последовательность обработки. Для исключения проблем конкуренции, можно использовать очередь для последовательной обработки запросов
Фишки: - cпособ хранения: память, диск, распределенные - гарантии доставки - queue/Topic. Можно читать по событию из очереди, а можно подписываться на события - отложенная доставка - протухание. Иногда необработанные события теряют свою актуальность - пакетная обработка. Сбор пачки событий для более производительной обработки
Проблемы: - не избавляет нас от проблем атомарности в распределенных системах: если агент обработки очереди сделал запись в БД и БД отпала - непонятно обработалась ли запись в очереди. Все аналогично с проблемой отказов - требуется мониторить очередь, отследить ее переполнение и ситуации, когда обработка очереди не поспевает за новыми запросами (есть хорошая лекция по поводу проблем очередей https://www.youtube.com/watch?v=CvT1v7xiRS0)
Микросервисы
Фишки: - масштабирование - простота разработки: быстрая компиляция и запуск, меньше связанность, проще тестирование, гибкий цикл релизов - простота развертывания - изоляция отказов
Минусы: - производительность - сложность разработки: удаленные вызовы, discovery, сложная отладка - сложность развертывания - больше точек отказа
Боль: - конфигурация - сбор логов - сбор статистики - мониторинг
Выводы:
- отказы всегда есть и будут, нужно учитывать их при проектировании архитектуры. Другими словами, если нет необходимости дробить сервисы - не дробите, не привнесете туда проблем
- физические ограничения нужно знать и понимать. При завышенных показателях бенчмарков - нужно понимать за счет чего это достигается
- CAP теорема - не бывает идеальных распределенных БД
- при усложнении архитектуры бэкенда и технологий, проблемы не уходят, а трансформируются в другие, более сложные. Но при росте проекта это необходимо
~~OWNERAPPROVE~~
Обсуждение