Как на самом деле выглядит дизайн-ревью
В прошлый раз мы обсуждали, как подготовить обзор дизайна как эксперт. Необходимо подготовить три элемента:
- модель С4
- Пользовательские истории и варианты использования
- Дизайнерские решения
В этой статье я использую практический пример, чтобы показать вам, как выглядит обзор дизайна. Некоторые обсуждения со слишком большим количеством деталей будут пропущены и будут демонстрировать только критически важные проекты.
Истории пользователей
Во-первых, мы собираемся определить пользовательскую историю. Как упоминалось в предыдущей статье, пользовательская история согласуется с перспективой контекста в модели C4. Поэтому мы четко записываем пользовательские истории.
На этот раз мы собираемся сделать функцию дарения подарков. А вся история такова.
- Пользователь может решить, сколько одинаковых подарков он хочет подарить другим одновременно.
- Пока у пользователя достаточно денег, количество людей, дарящих подарки, не ограничено.
- После завершения вручения подарков он должен сообщить как отправителю, так и получателю, что все сделано.
Случаи использования
Вся сцена дарения подарка четко описана в пользовательской истории, однако есть некоторые детали, которые недостаточно описаны. Например,
- Если баланса пользователя недостаточно, то все подарки будут провалены и не могут быть частично успешными.
- Завершение дарения подарков означает, что все получатели получили подарок.
- Содержание уведомления должно содержать отправителя, общую сумму подарков и всех получателей.
- Независимо от того, сколько людей нужно дать, весь процесс должен быть завершен за несколько минут.
Как мы видим выше, в юзкейсах мы добавили детали, которых не было в истории, и более детально представили всю сцену.
Модель C4
Теперь мы можем нарисовать модель C4 всей конструкции.
Контекст
На основе пользовательской истории мы рисуем контекст для описания взаимодействия между пользователем и системой. Из контекста мы знаем, что есть несколько ключевых моментов, которые необходимо подробно обсудить.
- Строка, содержащая
gift
- Поведение самого
Server
- Строка, содержащая
notify
Вы можете спросить, как насчет notification service
и receivers
. Ответ прост. Это сторонний сервис, и мы не можем контролировать его поведение. Поэтому нет необходимости обсуждать это в обзоре дизайна на тему дарения подарков. Конечно, если есть какие-либо опасения по поводу уведомления, мы можем провести еще одну проверку дизайна, чтобы разобраться.
Контейнер
Когда у нас есть контекст, мы начинаем погружаться в три пункта, перечисленных выше, и расширять их в форме контейнера.
Наконец, мы получили эту диаграмму, вот как она выглядит, когда она закончена. Здесь много конструктивных решений, но в этом разделе я только представлю смысл этой схемы. Что касается дизайнерских решений, мы оставим их подробный анализ в следующих разделах.
- Пользователи отправляют запросы на подарки, в том числе, что отправить и кому отправить.
- Получив запрос,
Server
сначала списывает кошелек пользователя и отвечает напрямуюnak
, если баланса недостаточно, затем делит запрос на пакеты фиксированного размера и записывает информацию о пакете в базу данных. После этого пакетная задача отправляется воркеру для выполнения асинхронно с порядковым номером транзакции. - Хотя процесс еще не завершен, подготовка уже завершена, поэтому ответьте пользователю
ack
, чтобы показать, что весь процесс идет. - Когда рабочий получает команду, он сосредотачивается на назначенной ему пакетной задаче, а когда она завершается, он вычитает
counter
из базы данных. Если рабочий обнаружит какие-либо ошибки, просто повторите саму задачу. - Когда после вычета счетчика будет установлено, что
counter
равно 0, что означает, что все завершили задачу, тогда последний рабочий уведомит всех получателей.
Компонент и код
С точки зрения контейнера следующие шаги — это компонент и код, но они уже связаны с некоторыми деталями реализации, и каждая система сталкивается с разными проблемами, поэтому я не буду здесь вдаваться в подробности.
Дизайнерские решения
Мы нашли много деталей на диаграмме контейнера. Как и в предыдущей статье, мы будем использовать формулу why do A instead of B
, чтобы задать много вопросов.
- Почему отправитель и сервер взаимодействуют друг с другом полуасинхронным способом (между синхронным и асинхронным)?
- Почему сервер разделен на оркестрацию и рабочие?
- Почему оркестровка и рабочие процессы полностью асинхронны?
- Почему рабочий уведомляет получателей?
- Может быть, вы сможете рассмотреть некоторые вопросы, которые я никогда не перечислял.
На все эти вопросы следует ответить из исходной архитектуры.
В начале у нас уже есть особенность дарить подарок человеку. Итак, самый простой способ — выполнить цикл for, чтобы отправить всем получателям подарок один к одному независимо от номера получателя.
Это нормально, когда есть только несколько приемников, но как только количество приемников начнет увеличиваться, производительность станет серьезной проблемой. По нашим измерениям, чтобы доставить человека, требуется около 100-200 ms
, не считая уведомлений, а это значит, что когда количество людей достигнет 10
, оно достигнет второй величины. Это явно неприемлемо.
Кажется, что пакетирование неизбежно, поэтому кто-то нарисовал первую архитектурную диаграмму.
Из схемы видно, что оркестровка уже появилась. Однако связь с воркером по-прежнему синхронизируется, и уведомление будет отправлено только тогда, когда все задачи будут выполнены. Он не ответит пользователю, пока все задачи не будут выполнены. Может показаться, что это значительно уменьшило узкие места в производительности, но совсем не улучшилось.
Давайте проведем простую математику. Предположим, мы хотим отправить подарки 1000 человек, как нам установить размер партии и количество работников?
Чтобы закончить его за секунды, максимальный размер пакета составляет 10
, поэтому 100
рабочих должно быть сгенерировано одновременно для обработки запроса на подарок. Это очень сложная задача для системы, и создать 100
воркеров за одно мгновение — непростая задача. Из-за этого трудно заставить пользователей ждать полностью синхронизированным образом. Итак, что происходит, когда он полностью асинхронизирован?
Во второй попытке мы изменили оркестровку на хореографию, чтобы пользователь мог получить ответ очень быстро, а подарок можно было отправить без проблем. Но так ли это на самом деле?
Что произойдет, если средний работник потерпит неудачу? Вся цепочка разорвана, и пользователь может не почувствовать этого без уведомления. Это хорошо, но для дарителя средний успешный работник уже списал деньги пользователя, но уведомление не отправляется. Более того, возвращаясь к первому пункту вариантов использования, частичный успех не допускается.
Хореография по сравнению с оркестровкой действительно будет иметь лучшую производительность и лучшую масштабируемость, но получит более сложное управление рабочим процессом. Таким образом, в этом случае использования оркестровка будет более подходящей.
Итак, давайте реализуем полную асинхронизацию посредством оркестровки.
Эта архитектура сразу же сталкивается с двумя проблемами.
- Кто должен отправить уведомление?
- Что делать, если денег не хватает на полпути?
Хорошо решена проблема отправки уведомлений, как и в предыдущем представлении контейнера в модели C4, фактически решена проблема кому отправлять уведомления. Тем не менее, в принципе невозможно обработать ошибку на полпути к полностью асинхронной архитектуре.
По этой причине мы наконец приняли полуасинхронный подход. Во-первых, оркестрация определяет, достаточно ли остатка для отправки, и, чтобы избежать состояния гонок, деньги списываются напрямую. Таким образом, работнику нужно только обработать отправку подарка, а не вычесть деньги у дарителя, даже не проверить баланс.
Обработка ошибок и аварийное восстановление
При такой архитектуре возникает огромная беда.
Как насчет сбоя рабочего процесса?
Если это произошло из-за перегрузки базы данных, все должно пройти после нескольких повторных попыток. В противном случае, если в реализации возникнет сбой, его не удастся устранить, даже если повторить попытку несколько раз.
В результате должна существовать дополнительная система мониторинга, чтобы периодически проверять, какие асинхронные задачи завершились сбоем, и повторять те, которые можно повторить, или уведомлять о вмешательстве человека, если они не могут восстановиться повторными попытками. Применимое решение находится в предыдущей статье, которую я уже представил, поэтому я не буду объяснять здесь слишком много.
Заключение
Подводя итог, система разбивает дарение подарков на несколько этапов.
- Пользователь отправляет запрос синхронно, а баланс списывается заранее.
- Все процессы дарения подарков выполняются асинхронно.
- Процесс дарения подарков в конечном итоге может быть последовательным, и вычитаемые деньги равны выданным деньгам.
В этой статье мы обсудим проблемы при проектировании распределенной системы на практическом примере.
- Синхронный против асинхронного
- Оркестровка против хореографии
- Атомарная и окончательная согласованность
Фактически, эти элементы также являются компромиссами в типичной распределенной транзакции. Эти аспекты могут существенно повлиять на общую модель распределенных транзакций. Я представил распределенные транзакции в моей предыдущей статье. В следующий раз я более подробно рассмотрю проблемы и проблемы, возникающие при проектировании распределенной системы.