Ответ
В микросервисной архитектуре используется множество паттернов для решения типичных проблем распределенных систем. Вот некоторые из ключевых:
-
API Gateway: Единая точка входа для всех клиентских запросов. Он маршрутизирует запросы к нужным сервисам, а также может выполнять аутентификацию, логирование, кэширование и rate limiting.
-
Service Discovery: Механизм для автоматического обнаружения сетевого местоположения (IP-адрес и порт) экземпляров сервисов. Позволяет сервисам находить друг друга без жестко прописанных адресов. Примеры: Consul, etcd, Zookeeper.
-
Circuit Breaker (Предохранитель): Защищает систему от каскадных сбоев. Если сервис-зависимость начинает возвращать ошибки, Circuit Breaker "размыкается" и перестает отправлять на него запросы на некоторое время, возвращая ошибку немедленно. Это дает сбойному сервису время на восстановление.
-
Database per Service: Каждый микросервис управляет своей собственной базой данных. Это обеспечивает слабую связанность (loose coupling), так как изменения в схеме данных одного сервиса не влияют на другие.
-
Saga Pattern: Паттерн для управления распределенными транзакциями. Вместо одной большой ACID-транзакции используется последовательность локальных транзакций в каждом сервисе. Если одна из них сбоит, выполняются компенсирующие транзакции для отката изменений.
-
CQRS (Command Query Responsibility Segregation): Разделение операций на команды (изменение данных) и запросы (чтение данных). Это позволяет независимо масштабировать и оптимизировать модели для чтения и записи.
Пример реализации Circuit Breaker в Go:
import (
"time"
"github.com/sony/gobreaker"
)
var cb *gobreaker.CircuitBreaker
func init() {
settings := gobreaker.Settings{
Name: "my-external-service",
MaxRequests: 1,
Timeout: 5 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
return counts.ConsecutiveFailures > 3
},
}
cb = gobreaker.NewCircuitBreaker(settings)
}
func CallMyService() (interface{}, error) {
body, err := cb.Execute(func() (interface{}, error) {
// Логика вызова внешнего сервиса
return externalAPI.GetData()
})
if err != nil {
return nil, err
}
return body, nil
} Ответ 18+ 🔞
А, микросервисы, блядь! Ну, это ж как в коммуналке жить, только на уровне кода. Всем по отдельной комнатке, но если один сосед в сортире застрял — всем пиздец, света нет. Вот чтобы такого не было, умные дядьки придумали паттерны, чтобы не охуевать каждый раз.
Смотри, какие штуки есть, чтобы этот цирк с конями хоть как-то работал:
-
API Gateway (Швейцар-хуевцар): Это такой главный хаб для всех, кто с улицы лезет. Все запросы — к нему. А он уже решает: тебе к Петровичу в сервис-аутентификацию, а тебя — к Машке в каталог товаров. Ещё и проверяет, не пьяный ли ты, и считает, сколько раз заходил. Удобно, блядь.
-
Service Discovery (Где блядь Петрович?): Сервисы-то плодятся как тараканы, их адреса меняются. Этот паттерн — такая общая тетрадка, где все пишут: «Я, Петрович, сейчас живу вот тут». И когда Машке надо Петровича найти, она не по всей общаге бегает, а в эту тетрадку смотрит. Consul, etcd — это такие ответственные за тетрадь.
-
Circuit Breaker (Автомат-ебанут): Самое важное, ёпта! Представь, сервис «Оплата» лег и не встает. А ты ему всё новые запросы суёшь. Он их тупо жрёт и сдыхает. Так вот этот предохранитель — он смотрит: если три раза подряд хуйня, он говорит «ВСЁ, БЛЯДЬ!» и разрывает цепь. Перестаёт слать запросы, даёт сервису отдышаться. А клиентам сразу говорит: «Сорян, не работает, иди нахуй». Потом, через время, осторожно пробует снова. Гениально и просто, как топор.
-
Database per Service (Свой сортир — своя крепость): Это святое! Каждый сервис сосёт со своей собственной базы. Не лезь в мой горшок, я не лезу в твой. Иначе один накосячит со схемой, а все остальные срут кирпичами. Слабая связанность, ёба!
-
Saga Pattern (Длинная и печальная сага): А как, сука, деньги списать с одного счёта и зачислить на другой, если это два разных сервиса? Одна большая транзакция не прокатит. Вот и делают цепочку: списали в одном сервисе — ок, зачислили в другом — ок. Если на втором шаге пиздец, то запускают обратный шаг (компенсирующую транзакцию), чтобы откатить первый. Как в кино: одно ебаное приключение за другим.
-
CQRS (Разделяй и властвуй): Читать и писать — это две большие разницы, как говорят в Одессе. Вот и разделили: один сервис (или даже модель) только команды принимает — «создай», «измени», «удали». А другой — только отдаёт данные для чтения. Их можно независимо масштабировать. Тот, что читает, может хоть из пяти кэшей данные собирать, главное — быстро. А тот, что пишет, пусть себе в одной транзакции ковыряется.
Вот, смотри, как этот автомат-ебанут (Circuit Breaker) на Go выглядит, чтоб ты понимал, что это не магия, а код:
import (
"time"
"github.com/sony/gobreaker"
)
var cb *gobreaker.CircuitBreaker
func init() {
settings := gobreaker.Settings{
Name: "my-external-service",
MaxRequests: 1,
Timeout: 5 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
return counts.ConsecutiveFailures > 3
},
}
cb = gobreaker.NewCircuitBreaker(settings)
}
func CallMyService() (interface{}, error) {
body, err := cb.Execute(func() (interface{}, error) {
// Логика вызова внешнего сервиса
return externalAPI.GetData()
})
if err != nil {
return nil, err
}
return body, nil
}
Видишь? Настроили: если три раза подряд косяк (ConsecutiveFailures > 3) — всё, автомат вырубается, состояние Open. Потом стоит 5 секунд (Timeout), пробует снова. В общем, чтобы не охуевать от каскадных сбоев, когда один еблан-сервис тянет за собой всех.