Узлы, рёбра, направления
Запрос как ход на графе
Каждый компонент, с которым взаимодействует запрос, является вершиной: клиент, разрешающий сервер DNS, крайний узел CDN, обратный прокси, реплика сервера backend, база данных, кэш.
Каждое соединение между двумя узлами - это направленное ребро: запросы текут вперед, ответы текут назад. Ребро, идущее вперед, представляет собой открытый TCP-соединение плюс протокол, наложенный сверху.
Один запрос - это путь по этому графу. Общая работа системы, которую она выполняет для ответа на запрос, равна сумме работы на каждой вершине, плюс задержка каждого ребра.
Почему это важно? Когда вы нарисуете граф, свойства, которые не видны в коде, всплывают:
- Количество переходов: количество ребер в пути. Каждый переход добавляет задержку (круговая задержка сети + обработка узла). Меньше переходов = ниже базового уровня задержки.
- Входной градус: количество ребер, указывающих ВНУТРИ узла. Высокий входной градус означает, что узел получает запросы от многих источников и должен масштабироваться или защищаться.
- Выходной градус: количество ребер, указывающих ВНУТРИ узла. Высокий выходной градус означает, что узел зависит от многих downstream и имеет много путей к отказу.
- Точка разрыва: одиночная вершина, удаление которой разрывает граф. Обратный прокси без пирата - это точка разрыва; удаление его удаляет все доступ к его источникам.
Где сосредотачивается трафик
Концентрация (Fan-In)
Степень входа узла = количество входящих к нему ребер. В графе запросов, степень входа = количество исходящих источников запросов.
Схема концентрации: много клиентов -> один CDN; много CDN-ребер -> несколько origin-прокси; много прокси -> меньше бэкенд-реплик; много бэкендов -> одна база данных.
Важно, что узел с наивысшей степенью входа видит наибольшую суммарную нагрузку. База данных в конце цепи может видеть запросы от каждого активного запроса в системе, даже если ни один пользователь не генерирует много.
Зависимость (Fan-Out)
Степень выхода узла = количество исходящих из него ребер. Высокая степень выхода означает много downstream-зависимостей.
Бэкенд, вызывающий базу данных, два кэша, три внешних API и очередь, имеет степень выхода 7. Его вероятность успеха примерно равна произведению каждой downstream-вероятности успеха (если все необходимы для успешного ответа).
0.999 ^ 7 ≈ 0.993: бэкенд с 7 downstream, каждый с вероятностью 99,9% надежности, может достичь только ~99,3% надежности самого себя, даже при отсутствии собственных ошибок.
Уменьшить степень выхода: кэширование результатов downstream, делая необязательными некритичные downstream (мягкая децентрализация), параллелизация того, что можно параллелизировать.
Асимметрия
Концентрация увеличивает нагрузку; зависимость умножает риск. Граф с правильной формой минимизирует оба параметра на узлах с наибольшим влиянием.
База данных (наивысшая концентрация): кэширование агрессивно, чтобы уменьшить нагрузку. Чтение реплик бэкенда для распределения концентрации между несколькими узлами.
Оркестратор (наивысшая зависимость): циркулярные разрывы для каждой зависимости, мягкая децентрализация, bulkheads.
Вставленный узел приобретает гибкость
Индирект = Добавление промежуточного узла
Без прокси-устройства, граф выглядит так: client -> backend. Клиент должен знать о адресе backend. Перемещение backend требует обновления клиента (через DNS или конфигурацию). Это тугое связывание.
С прокси-устройством, граф становится: client -> proxy -> backend. Клиент знает только о прокси. Перемещение backend требует обновления конфигурации прокси-устройства вверх, а не клиента.
Графовая операция: вставить узел вдоль существующего ребра. Новое ребро client -> proxy устойчиво; новое ребро proxy -> backend теперь в компетенции команды.
Геометрическое чтение: индирект добавляет слой, который декуплирует изменение вверху от изменение внизу. Каждый слой может независимо перенастраиваться.
Стоимость индиректа
Каждый слой добавляет:
- Один прыжок задержки (ребро от клиента к прокси)
- Еще одну вершину разрыва на пути (прокси-устройство)
- Еще одно место, где может произойти ошибочная конфигурация
Польза (перенастройка, масштабирование, защита, завершение TLS, распределение нагрузки) обычно перевешивает затраты для любого неочень сложного системы. Но есть предел: каждый слой индиректа добавляет еще один прыжок и еще одного кандидата на SPOF.
Фольклорное правило: любую проблему можно решить, добавив слой индиректа (кроме проблемы слишком большого количества слоев индиректа).
Прочитайте архитектуру как граф
Синтез
Теперь вы можете читать архитектуру системы как граф: считать прыжки, идентифицировать вершины разрыва, измерять концентрацию fan-in, вычислять потолки доступности от fan-out и оценивать торговые предложения индиректа.
Примените все четыре.
Новый сервис имеет такую архитектуру: клиенты -> CDN -> обратный прокси (2 реплики) -> backend слой (8 реплик) -> { основной база данных, кластер кэша (3 узла), внешний API }.
Дополнительные примечания
Дополнительные примечания
Эта геометрия-урок преобразует основной урок Proxies & Origins в анализ направленного графа.
Следующее дополнение в этом курсе, geometry_of_stateless_horizontal_scaling, берет математический анализ реплик из основного урока масштабирования и выводит кривую очереди, закон Литтла и колено геометрического 80% использования.
Хорошая работа.