Stateless и Stateful
Где живёт состояние?
Чтобы понять, насколько легко компонент масштабировать и восстанавливать после сбоя, нужно спросить: хранит ли он важное состояние между запросами? От ответа зависит, можно ли безболезненно отправить следующий запрос на другой экземпляр сервиса.
Stateless: экземпляр не привязан к прошлому запросу
Stateless-сервис не хранит локально данные, без которых нельзя обработать следующий запрос пользователя. Всё необходимое приходит в самом запросе или доступно через внешнее хранилище.
┌→ App 1 ─┐
Client ─┼→ App 2 ─┼→ Shared Storage
└→ App 3 ─┘
Если один экземпляр исчезнет, балансировщик может отправить новые запросы на другие. Это делает горизонтальное масштабирование заметно проще: экземпляры приложения взаимозаменяемы.
Но stateless не означает «у системы вообще нет состояния». Пользователи, заказы, сессии и другие данные всё равно существуют — просто важное состояние вынесено из конкретного экземпляра приложения.
Stateful: экземпляр хранит значимое состояние
Stateful-компонент сохраняет данные, связанные с предыдущими операциями, и эти данные важны для последующей работы именно этого компонента. Простой пример — база данных: содержимое диска и памяти является частью её состояния.
Client → Stateful Node
├─ local data
└─ local state
Такой узел нельзя считать полностью взаимозаменяемым с пустым новым экземпляром. Нужно понимать, как его состояние копируется, восстанавливается или переносится. Это делает масштабирование и отказоустойчивость сложнее, но stateful-компоненты совершенно нормальны и часто необходимы.
Сессия пользователя хорошо показывает разницу
Представим, что приложение хранит корзину пользователя только в памяти конкретного сервера. После первого запроса все следующие запросы этого пользователя желательно направлять туда же. Если сервер упадёт, локальная корзина исчезнет.
Если же корзина хранится во внешнем общем хранилище, любой экземпляр приложения может продолжить обработку запроса. Сам слой приложения становится stateless относительно этой корзины.
Это свойство компонента, а не ярлык всей системы
Одна система обычно содержит и stateless-, и stateful-части. Например, API-серверы могут быть stateless, а база данных — stateful. Главное не выбрать один подход «навсегда», а ясно понимать, где находится состояние и что произойдёт с ним при масштабировании или отказе.