При проектировании программных систем одной из важных вещей является возможность доступа к производительности системы, чтобы определить, соответствует ли она нефункциональным требованиям. Один из способов, которым обычно это делают, — создать простую версию, а затем применить к ней несколько нагрузочных тестов. Таким образом, вы не знаете, как будет работать ваша система, пока не потратите время на разработку, сделав какую-нибудь простую версию для тестирования производительности. Пока это работает, петля обратной связи работает медленно, и, следовательно, время разработки увеличивается. Что вы хотите сделать, так это иметь возможность анализировать производительность вашей системы на уровне проектирования, не создавая ничего еще.

В этом посте я представляю простую модель, которую вы можете использовать для многократного анализа производительности вашей системы на бумаге, прежде чем что-либо создавать. Я называю это моделью писателя/читателя (WR) для анализа архитектуры программного обеспечения. При таком подходе вы можете оценить свои проекты и почти сразу же получить обратную связь о том, насколько они соответствуют нефункциональным требованиям системы. Нефункциональные требования обычно относятся к производительности системы при определенных обстоятельствах. Например, если вы разрабатываете систему загрузки изображений, в нефункциональных требованиях будет указан максимальный и минимальный разрешенный размер изображения, а также ожидаемое количество загрузок в определенный период (день, час и т. д.).

WR-моделирование

Модель писатель/читатель — это очень простой способ моделирования проектов программного обеспечения. Он удаляет много деталей, но сохраняет достаточно информации, чтобы обеспечить хороший способ измерения производительности архитектур. Вы сможете сделать множество выводов на бумаге, не создавая прототип. Обычно программная архитектура состоит из компонентов, которые взаимодействуют друг с другом для удовлетворения требований системы. Компонент, вызывающий или делающий запрос к другому компоненту, является писателем, а компонент, отвечающий на вызывающий компонент, является читателем. Например, в приложении для ведения блога мы могли бы выделить три компонента;

  1. Веб-сайт, с которым взаимодействуют обычные пользователи.
  2. Сервер API, с которым взаимодействует веб-сайт.
  3. База данных, с которой взаимодействует сервер API.

Грубо говоря, у нас может получиться такая схема:

В модели WR направление стрелки показывает, какой компонент является записывающим, а какой читающим. Следовательно, компонент веб-сайта записывает на сервер API, а сервер API записывает в базу данных. Написать здесь в смысле написания запросов. В этой модели я предполагаю, что сервер API никогда не пишет запросы к веб-сайту, он только отвечает на запросы, которые он читает с веб-сайта. Аналогичная связь существует между базой данных и сервером API. Если бы, скажем, сервер API также мог писать запросы к веб-сайту, у нас также была бы стрелка, перемещающаяся от сервера API к веб-сайту.

На изображении выше компонент A может писать в B и наоборот. Однако в примере с программным обеспечением для ведения блогов мы предполагаем, что ни в какой момент времени жизни приложения сервер API никогда не отправит запрос приложения на веб-сайт.

Давайте рассмотрим программную проблему, которую мы собираемся проанализировать, используя подход WR.

Постановка задачи

Давайте создадим систему для постоянного отслеживания местоположения парков транспортных средств и отображения данных о движении в режиме реального времени на приборной панели. Он должен удовлетворять следующим функциональным требованиям;

  1. Постоянно получайте данные о местоположении, отправленные с устройств GPS-слежения, установленных на транспортных средствах.
  2. Обеспечьте подачу движения почти в реальном времени на приборной панели.
  3. Предоставлять статистику данных, собранных на панели мониторинга.

Помимо этих функциональных требований, он также должен удовлетворять следующим нефункциональным требованиям.

  1. Он должен быть в состоянии обрабатывать не менее 10 000 и не более 100 000 устройств GPS одновременно.
  2. Устройства GPS будут отправлять данные о местоположении с интервалом в 2 секунды.
  3. Панель управления движением в реальном времени должна обновляться каждые 5 секунд с обновленным местоположением транспортных средств.
  4. Панель статистики должна обновляться не реже одного раза в 15 минут.
  5. Обе панели мониторинга должны одновременно обрабатывать не менее 100 и не более 1000 пользователей.

Первое решение

Ниже приведена диаграмма модели WR предлагаемого решения. Это показано стрелками, которые строго показывают, какой компонент записывает какие компоненты. Еще одной особенностью являются числа, отображающие скорость записи запроса. /s означает количество запросов на запись в секунду, а /m — количество минут.

Из диаграммы должно быть ясно, какие компоненты являются Создателями запросов и Считывателями.

GPS-устройства → API трекера

Во-первых, давайте сосредоточимся на устройствах GPS и API Tracker. Мы не будем интересоваться протоколами, по которым устройства отправляют данные о местоположении, а скорее предположим, что данные надежно дойдут до наших API. Как указано в нефункциональных требованиях, ожидается, что 10 000–100 000 устройств будут отправлять данные о местоположении каждые 2 с. На диаграмме это показано как 5000/s — 50 000/s, где /s означает число запросов на запись в секунду. Чтобы справиться с этой нагрузкой, API трекера разработан для горизонтального масштабирования с помощью балансировщика нагрузки с автоматическим масштабированием перед несколькими небольшими серверами API.

Предположим, что каждый сервер API был протестирован для обработки 100/с, тогда, чтобы соответствовать минимальному требованию 5000/с, служба уже должна быть инициализирован с 50 серверами API. При автомасштабировании по мере увеличения числа /s количество серверов будет автоматически увеличиваться для соответствия нагрузке. Это крайне важно для обеспечения того, чтобы API работал большую часть времени без ручного вмешательства.

API трекера → Основная БД

Предполагается, что Tracker API выполняет две функции: 1) проверяет данные, которые он получает от устройств, и 2) записывает достоверные данные в основную базу данных. С этого момента можно с уверенностью предположить, что Tracker API должен будет передавать 5000/s — 50 000/s в основную БД по мере получения от устройств. Следовательно, это означает, что основная БД должна быть в состоянии справиться с такой нагрузкой. Если основная БД не может идти в ногу со службой отслеживания, служба отслеживания не сможет идти в ногу с устройствами GPS.

Автоматическое масштабирование будет продолжать добавлять больше серверов API для обработки нагрузки, но, поскольку основная БД не может справиться с нагрузкой, будет добавлено слишком много серверов только для обработки отставания в ожидании ответа основной БД. Это означает, что приложение будет тратить больше денег, чем должно, невыполненные запросы будут накапливаться, и в конечном итоге система будет отставать на часы или даже дни. Главный урок здесь заключается в том, что API здесь такой же медленный, как и база данных, независимо от того, насколько быстрым должен быть API.

Следовательно, вопрос заключается в том, может ли текущая выбранная БД справиться с этой нагрузкой при записи. Чтобы оценить это, может потребоваться обратиться к системным ограничениям рассматриваемой базы данных.

В общем, вам необходимо знать об ограничениях сторонних решений, используемых как часть вашей системы.

В нашем текущем проекте мы решили использовать базу данных SQL. Простой ответ на вопрос — нет, он не сможет справиться с такой нагрузкой. Это просто слишком много, и вы будете раздвигать границы базы данных таким образом, что это замедлит весь ваш дизайн. Если база данных не может справиться с этой нагрузкой, это означает, что база данных реплики также не может справиться с этой нагрузкой. Давайте оценим наш выбор и посмотрим на второе решение.

Прежде чем рассматривать второе решение, давайте быстро пройдемся по другим компонентам. Существует процесс ETL (извлечение, преобразование, загрузка), который загружает данные из базы данных реплики каждые 15 минут, вычисляет необходимую статистику и сохраняет ее в базе данных статистики. Нагрузка на базу данных статистики минимальна, тем не менее, БД-реплика не может справиться с нагрузкой со стороны основной БД, извлечение данных будет медленным, а извлеченные данные будут старыми. Это влияет на Statistic API, так как он начнет показывать статистику для очень старых данных. Точно так же API местоположения не сможет вернуться к местонахождению транспортных средств в реальном времени. Из нашего анализа видно, что критическая часть системы сосредоточена вокруг Tracker API → Основная БД.

Второе решение

Ниже приведена диаграмма модели WR предлагаемого решения. Это показано стрелками, которые строго показывают, какой компонент записывает какие компоненты. Еще одной особенностью являются числа, отображающие скорость записи запросов. /s означает количество запросов на запись в секунду, а /m — количество минут.

По сравнению с первым решением, это решение использует Redis DB вместо основной БД и реплики БД. Процесс ETL теперь делает две вещи;

  1. Вычислять статистику каждые 5 минут. Он загружает данные непосредственно из базы данных Redis.
  2. Сбросьте 5-минутные старые данные в базу данных архива, которая использует базу данных NoSQL.

Использование Redis решает нашу первоначальную проблему, связанную с тем, что база данных SQL не сможет идти в ногу с Tracker API. Ограничения нашей системы находятся в пределах досягаемости Redis. Однако нам пришлось скорректировать время процесса ETL с 15 минут до 5 минут, чтобы иметь возможность вычислять статистику, а также сбрасывать старые данные в архивную БД. Нам нужно сбросить данные, потому что Redis — это база данных в памяти, и мы не хотим, чтобы все эти данные хранились в памяти. Предположим, что мы можем уместить все данные отслеживания, скажем, в 64 байта, тогда каждую секунду мы можем ожидать 5000 x 65–50000 x 64, то есть 0,0032 ГБ — 0,032 ГБ. данных в секунду. Это составляет около 0,96 ГБ — 9,6 ГБ каждые 5 минут. Поэтому возникает необходимость сбросить куда-то 5-минутные старые данные. Дисковая база данных NoSQL — хороший выбор, потому что объем данных огромен: терабайты данных о местоположении в год. Возможно, было бы целесообразно определить период хранения.

Это не идеальный дизайн, но он намного лучше, чем первое решение. Я рад услышать, что вы придумали, просто напишите мне или прокомментируйте, и давайте поговорим об этом.

Заключение

Вы видели, что, используя нашу простую технику моделирования WR, мы смогли сразу же выявить недостатки в нашем первоначальном проекте, а также предложить и утвердить обновленный второй проект.