Как бы вы организовали взаимодействие между несколькими сервисами?
Ответ от нейросети
sobes.tech AI
Что хотят услышать интервьюеры:
Нужно показать, что взаимодействие сервисов строится не только на выборе REST или gRPC, но и на учёте синхронности, отказоустойчивости и наблюдаемости. Обычно ждут архитектурный ответ: где уместны HTTP-вызовы, где лучше асинхронные сообщения, как сервисы находят друг друга и как защищается внешний вход. Важно упомянуть баланс между простотой интеграции и надёжностью системы.
Определение:
Взаимодействие между несколькими сервисами обычно организуют как комбинацию синхронных и асинхронных каналов связи. Для запросов с немедленным ответом используют REST или gRPC, для событий и длительных процессов — message queue или event bus. Снаружи систему часто прикрывают API Gateway, а внутри — service discovery, таймауты, ретраи и схемы версионирования контрактов.
Пример использования:
Допустим, есть сервисы Order, Payment и Inventory.
Пользователь создаёт заказ через API Gateway, который маршрутизирует запрос в Order Service. Order Service синхронно проверяет доступность товара через Inventory Service, а затем публикует событие OrderCreated в очередь. Payment Service читает событие, проводит оплату и публикует PaymentCompleted, после чего Order Service переводит заказ в финальный статус.
// Упрощённый пример: синхронный вызов + публикация события
@RestController
@RequestMapping("/orders")
class OrderController {
private final InventoryClient inventoryClient;
private final EventPublisher eventPublisher;
OrderController(InventoryClient inventoryClient, EventPublisher eventPublisher) {
this.inventoryClient = inventoryClient;
this.eventPublisher = eventPublisher;
}
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody CreateOrderRequest request) {
boolean available = inventoryClient.checkAvailability(request.productId(), request.quantity());
if (!available) {
return ResponseEntity.badRequest().body("Not enough stock");
}
// сохраняем заказ в БД (опущено)
eventPublisher.publish("OrderCreated", request);
return ResponseEntity.accepted().body("Order accepted");
}
}
interface InventoryClient {
boolean checkAvailability(String productId, int quantity);
}
interface EventPublisher {
void publish(String topic, Object payload);
}
record CreateOrderRequest(String productId, int quantity) {}
Пояснение кода:
OrderController принимает запрос на создание заказа. Сначала он синхронно обращается к InventoryClient, чтобы быстро проверить остатки товара. Если товара нет, запрос завершается с ошибкой. Если всё в порядке, заказ принимается и отправляется событие OrderCreated через EventPublisher, чтобы другие сервисы могли обработать его асинхронно.
Шаги работы примера:
- Клиент отправляет запрос в
POST /orders. - Сервис заказа синхронно проверяет остатки в сервисе склада.
- Если проверка успешна, заказ сохраняется.
- В очередь/шину публикуется событие о создании заказа.
- Сервис оплаты или другие подписчики обрабатывают событие независимо и не блокируют создание заказа.
Ключевые моменты:
- Для быстрых запросов и простых CRUD-сценариев обычно подходит REST.
- Для низкой задержки и строгого контракта между сервисами часто выбирают gRPC.
- Для слабой связности и устойчивости к сбоям лучше использовать асинхронные сообщения.
- API Gateway удобен как единая точка входа для внешних клиентов.
- Service Discovery нужен, если адреса сервисов динамические и меняются при масштабировании.
- Обязательно нужны таймауты, ретраи, circuit breaker и идемпотентность, иначе каскадные сбои быстро «положат» систему.