Это заметка для чтения по теме Проектирование приложений, интенсивно использующих данные
Обзор
Разработчик, работающий в коммерческой компании, чаще всего сталкивается с системами, интенсивно использующими данные. У разных компаний разный бизнес, но за разными бизнес-моделями стоит набор общих способностей. Например, код приложения, кэш в памяти, первичная база данных, очередь сообщений, например, у коммерческой компании недостаточно ресурсов, чтобы позволить разработчикам реализовать все это; поэтому мы принимаем архитектурные решения в соответствии с масштабами проблемы. Веб-сайт с 2000 посещений в день не нуждается в кэшировании, но для веб-сайта с 2000 посещений в секунду все становится совершенно иначе.
Прежде чем углубляться в детали систем, интенсивно использующих данные, нам нужно знать, как оценивать систему. Или, другими словами, когда мы говорим хорошая система, о чем именно мы говорим? Как правило, мы будем оценивать систему по трем аспектам: надежность, масштабируемость и ремонтопригодность. Мы обсудим некоторые методы, архитектуры или алгоритмы для достижения этих целей. Но перед этим нам нужно четко понимать эти термины. Последовательное и четкое понимание терминов — первый шаг в обсуждении дизайна системы.
Надежность
Мы часто используем этот термин при обсуждении системной архитектуры, но люди часто неправильно его понимают. Стоит отметить, что надежность не описывает систему, возвращающую правильное значение, которое ожидал пользователь, или терпимость к ошибкам, допущенным пользователями; это означает продолжать работать правильно, даже когда что-то идет не так. Поэтому, когда мы говорим о надежности, мы фактически говорим об отказоустойчивости. Эта тема рассматривается в хорошей статье под названием Концептуальная основа системной отказоустойчивости. Таким образом, мы сосредоточимся на этой статье в этом разделе.
Неисправности и неудачи
Точное понимание этих двух терминов является первым шагом к пониманию надежности системы.
Сбой обычно определяется как отклонение одного компонента системы от его спецификации, тогда как сбой — это когда система в целом перестает предоставлять требуемые услуги пользователю. Система может продолжать предоставлять свои услуги даже при обнаружении неисправности. Такая система называется отказоустойчивой.
Свести вероятность неисправности к нулю невозможно. Поэтому обычно лучше всего разрабатывать механизмы отказоустойчивости, которые предотвращают возникновение сбоев из-за сбоев.
Зависимости и области отказа
Если правильность поведения компонента требует корректного поведения второго компонента, мы говорим, что первый компонент зависит от второго компонента. Реальная система может иметь набор возможных зависимостей, образующих граф. Граф является ациклическим, если он является частью дерева, и циклическим, если часть зависимостей соединяется сама с собой. Вторую ситуацию лучше описать как ориентированный циклический граф.
Когда мы проектируем отказоустойчивую систему, важно определить зависимости между компонентами системы. Зависимости могут быть статичными или изменяться.
Ошибка, возникающая в компоненте, может передаваться в графе зависимостей. Таким образом, важно понимать области отказа. Мы определяем область сбоя как ограничение рассмотрения сбоев и сбоев в части системы и ее окружения.
Механизмы отказоустойчивости
Основываясь на обсуждении выше, мы вводим три механизма отказоустойчивости.
Во-первых, это управление резервированием. Он предоставляет резервные ресурсы для системы и автоматически заменяет компонент в случае его сбоя, чтобы он мог продолжать предоставлять услуги пользователям. Во-вторых, это методы приемочных испытаний. Компоненты системы выполняют проверку информации от других компонентов перед ее использованием. Этот механизм может работать в системе без резервирования. В-третьих, это методы сравнения. Несколько процессоров используются для выполнения одной и той же программы и сравнения результатов между процессорами.
Добейтесь надежности
Основываясь на обсуждении надежности и отказоустойчивой системы, легко понять, что первым шагом в реализации надежной системы является точное понимание вашей системы. Вам необходимо знать требования системы, чтобы иметь достаточно информации для определения частей, которые могут пойти не так. Определите подходящие области сдерживания сбоя, чтобы справиться с сбоем и найти компромисс между временем и пространством.
Масштабируемость
Надежность описывает, может ли система надежно работать в данный момент, но это не означает, что надежная система сможет обеспечивать надежное обслуживание в будущем. Количество пользователей может увеличиваться, что увеличивает нагрузку на систему. Поэтому мы используем масштабируемость для описания способности системы справляться с возросшей нагрузкой.
Сначала обсудим нагрузку и производительность. Хорошее понимание этих двух концепций может дать нам достаточно информации, чтобы найти компромисс при разработке системного решения. После этого мы кратко расскажем о некоторых подходах. В статье сложно рассказать обо всех деталях этих подходов; таким образом, мы говорим только о некоторых концептуальных вещах.
Описание нагрузки
Краткое описание нагрузки — первый шаг к повышению производительности нашей системы. Для этого требования мы всегда используем несколько чисел, называемых параметрами загрузки.
Выбор параметров нагрузки зависит от архитектуры вашей системы. Если основная функция требует интенсивного чтения и записи в базу данных, соотношение этого поведения является хорошим кандидатом. Не существует универсального показателя нагрузки на систему; вам нужно понять вашу систему или интересующую вас функцию, прежде чем выбирать их. Книга использует Twitter в качестве примера; Вы можете прочитать это для вдохновения.
Описание производительности
Как правило, нам нужна наилучшая производительность при наименьшем количестве ресурсов. Поэтому у нас есть два способа исследовать производительность нашей системы при увеличении нагрузки.
- Сохраняйте ресурс неизменным при увеличении параметра нагрузки и наблюдайте, как это влияет на производительность.
- Увеличьте ресурс, чтобы сохранить производительность неизменной. Обратите внимание, сколько ресурсов вам нужно для увеличения.
Как и в случае с нагрузкой, нам нужно использовать несколько чисел для описания производительности нашей системы. Мы всегда используем время отклика онлайн-системы для описания производительности. Он описывает время между отправкой клиентом запроса и получением ответа.
Очень важно понимать, что время отклика — это не просто число, а распределение значений. Поэтому смотреть на среднее становится бессмысленным со статистической точки зрения. Система с нестабильным временем отклика может иметь то же среднее значение, что и стабильная система. Но в результате пользовательский опыт совершенно другой.
Мы всегда используем процентили, чтобы обойти фундаментальную проблему со средними значениями. Идея состоит в том, чтобы брать данные за определенный период времени и сортировать их, затем отбрасывать 1% худших и наблюдать за оставшимся значением. Например, мы можем найти наибольшее значение, которое возникает в 99% времени отклика. На практике мы часто выбираем 99,9%, 99%, 95% и 90%.
Почему мы не можем использовать 99%? Предположим, что наибольшее значение, которое встречается при 99% отклика, составляет 400 мс. Это означает, что 99% времени отклика в течение этого времени лучше, чем 400 мс, но мы не знаем распределения этих лучших значений. Может быть, у нас есть 100 записей времени отклика; 80 из них 398 мс. Поэтому нам нужно просмотреть процентили времени отклика, чтобы распознать распределение.
В то же время необходимо знать, что то, о чем мы говорили выше, — это исследование только одного запроса. На современных веб-сайтах веб-странице может потребоваться отправить сотни запросов. Поэтому, хотя вы, возможно, приложили много усилий для улучшения данных о производительности, пользователи все равно с большой вероятностью столкнутся с худшей ситуацией.
Если вам нужна дополнительная информация по этой теме, я предлагаю вам прочитать Почему процентили не работают так, как вы думаете и Все, что вы знаете о задержке, неверно.
Способы справиться с нагрузкой
Основываясь на обсуждении нагрузки и производительности, мы можем сказать, что когда мы говорим о масштабируемости системы, мы говорим о том, как поддерживать хорошую производительность при возрастающих параметрах нагрузки.
У нас уже есть некоторые общие методы для удовлетворения этого требования в современной разработке программного обеспечения. Например, автоматическое масштабирование, балансировка нагрузки, кэширование (включая CDN) и распределенные системы. Мы поговорим о них в будущем, потому что каждая тема требует большого обсуждения и имеет массу деталей.
Другое дело – компромисс при архитектурном проектировании. Хотя у нас есть много способов улучшить производительность, мы не можем реализовать их все из-за ограниченных ресурсов. Поэтому. умный способ — использовать самый простой способ и рефакторить его до тех пор, пока текущая стратегия или требования высокой производительности не заставят вас изменить его. Но с улучшением инфраструктуры облачных сервисов некоторые подходы, такие как распределенные базы данных, в будущем могут стать стандартными.
Ремонтопригодность
Большая часть стоимости разработки программного обеспечения заключается в его текущем обслуживании, а не в первоначальной разработке. Хотя мы не можем устранить боль, связанную с поддержкой старого проекта, мы должны использовать некоторые способы ее уменьшения. Чтобы достичь этого, мы должны обратить внимание на три принципа дизайна: удобство использования, простота и эволюционируемость.
работоспособность
Разработка программного обеспечения включает не только написание кода; это также включает в себя его плавную работу.
Например, нам нужно следить за состоянием системы, собирая логи и данные; нам нужно использовать CI/CD для плавного выпуска новой версии; нам нужно поддерживать программное обеспечение в актуальном состоянии; нам нужно делиться знаниями о программном обеспечении с членами команды, которые не участвуют; мы должны установить передовой опыт для развития. Большинство из этих требований можно удовлетворить с помощью некоторых автоматических инструментов.
Хорошая оперативность облегчает жизнь команде. Команда может сосредоточиться на более ценных вещах.
Простота
Управление сложностью программного обеспечения является основной темой разработки. Со временем в систему добавляется все больше и больше функций, и система также может накопить много технического долга. Эти факторы делают систему сложной и трудной для понимания.
Двумя эффективными методами являются абстрагирование и расслоение. Он может скрыть сложность за чистым фасадом. В этой книге будут представлены некоторые подходы к разделению огромной системы на четко определенные повторно используемые компоненты. Если вы хотите узнать, как сделать то же самое с кодом, стоит прочитать книгу Структура и интерпретация компьютерных программ.
эволюционируемость
Как мы упоминали ранее, система не статична. Мы не только рефакторим код и архитектуру для поддержки большего количества пользователей, но также добавляем новые функции и изменяем существующие функции. Следовательно, эволюционируемость является важным аспектом измерения системы.
Рабочие шаблоны agile обеспечивают хорошую основу для решения этой проблемы. Некоторые технические инструменты и шаблоны от сообщества agile полезны для системы. Тест-драйвовая разработка и рефакторинг, вероятно, являются наиболее известными методологиями.
Краткое содержание
В этой статье говорилось о некоторых фундаментальных принципах измерения приложений, интенсивно использующих данные. Хорошее понимание этих принципов — первый шаг к погружению в глубокие технические детали. Хотя мы будем внедрять больше технологий, чтобы сделать приложения надежными, масштабируемыми и удобными в сопровождении, у нас никогда не будет универсального решения. Краткое понимание вашей системы всегда является первым шагом к решению проблем и улучшению приложения.