← Назад к вопросам
Как обеспечивал доставку данных при нестабильной работе микросервиса
3.0 Senior🔥 241 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Обеспечение доставки данных при нестабильности микросервисов
Это критичная задача в распределённых системах. Рассмотрю практические подходы, которые я применял в production.
1. Retry Logic с Exponential Backoff
Первая линия защиты — повторные попытки с экспоненциальной задержкой:
spring:
cloud:
circuitbreaker:
resilience4j:
instances:
userService:
registerHealthIndicator: true
slidingWindowSize: 20
slowCallRateThreshold: 50.0
slowCallDurationThreshold: 2s
failureRateThreshold: 50.0
waitDurationInOpenState: 30s
permittedNumberOfCallsInHalfOpenState: 3
resilience4j:
retry:
configs:
default:
maxAttempts: 3
waitDuration: 1000
intervalFunction: exponential
exponentialRandomizationFactor: 0.5
instances:
userService:
baseConfig: default
@Service
public class UserServiceClient {
@Retry(name = "userService", fallback = "fallbackGetUser")
@CircuitBreaker(name = "userService", fallback = "fallbackGetUser")
public User getUser(String userId) {
return restTemplate.getForObject(
"http://user-service/api/users/" + userId,
User.class
);
}
public User fallbackGetUser(String userId, Exception ex) {
log.error("Failed to get user, returning cached data", ex);
return userCache.get(userId);
}
}
2. Circuit Breaker паттерн
@Service
public class OrderService {
@CircuitBreaker(name = "paymentService", fallback = "processOffline")
public PaymentResult processPayment(Order order) {
return paymentClient.charge(order);
}
public PaymentResult processOffline(Order order, Exception ex) {
// Когда сервис payment недоступен, сохраняем в очередь на обработку позже
asyncQueue.enqueue(new PendingPayment(order));
return PaymentResult.PENDING;
}
}
3. Message Queue (RabbitMQ / Kafka)
Для асинхронной доставки данных — очень важно:
@Service
public class OrderPublisher {
@Autowired
private RabbitTemplate rabbitTemplate;
public void publishOrder(Order order) {
try {
rabbitTemplate.convertAndSend(
"orders.exchange",
"orders.created",
order,
message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
message.getMessageProperties().setExpiration("3600000"); // 1 час
return message;
}
);
} catch (Exception e) {
// Fallback: сохранить в БД на обработку
deadLetterQueue.save(order);
}
}
}
@Component
public class OrderConsumer {
@RabbitListener(queues = "orders.queue", ackMode = MANUAL)
public void processOrder(Order order, Channel channel, @Header(name = "amqp_deliveryTag") long deliveryTag) {
try {
orderService.process(order);
channel.basicAck(deliveryTag, false); // Confirm успешной обработки
} catch (Exception e) {
log.error("Error processing order", e);
// Не подтверждаем - сообщение вернётся в очередь
try {
channel.basicNack(deliveryTag, false, true);
} catch (IOException ex) {
log.error("Error nacking message", ex);
}
}
}
}
4. Distributed Tracing (OpenTelemetry)
Для мониторинга цепи вызовов:
@Configuration
public class TracingConfig {
@Bean
public OpenTelemetry openTelemetry() {
return AutoConfiguredOpenTelemetrySdkBuilder.initialize().getOpenTelemetrySdk();
}
}
@Service
public class OrderService {
@Autowired
private Tracer tracer;
public void createOrder(Order order) {
try (Scope scope = tracer.spanBuilder("createOrder")
.setAttribute("order.id", order.getId())
.setAttribute("user.id", order.getUserId())
.startScope()) {
// business logic
}
}
}
5. Outbox Pattern (для гарантированной доставки)
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 1. Сохранить заказ
orderRepository.save(order);
// 2. Сохранить событие в той же транзакции
OutboxEvent event = new OutboxEvent(
"OrderCreated",
objectMapper.writeValueAsString(order),
false
);
outboxRepository.save(event);
}
}
// Отдельный процесс публикует события из outbox
@Component
public class OutboxPublisher {
@Scheduled(fixedDelay = 1000)
public void publishPendingEvents() {
List<OutboxEvent> pending = outboxRepository.findByPublishedFalse(PageRequest.of(0, 100));
pending.forEach(event -> {
try {
publisher.publish(event.getEventType(), event.getPayload());
event.setPublished(true);
outboxRepository.save(event);
} catch (Exception e) {
log.error("Failed to publish event", e);
// Повторим позже
}
});
}
}
6. Dead Letter Queue
@Configuration
public class RabbitConfig {
@Bean
public Queue ordersQueue() {
return QueueBuilder.durable("orders.queue")
.deadLetterExchange("orders.dlx")
.deadLetterRoutingKey("orders.dead-letter")
.ttl(3600000) // 1 час TTL
.build();
}
@Bean
public Queue deadLetterQueue() {
return new Queue("orders.dead-letter.queue");
}
}
// Обработчик мёртвых писем
@Component
public class DeadLetterHandler {
@RabbitListener(queues = "orders.dead-letter.queue")
public void handleDeadLetter(Order order) {
log.warn("Processing dead letter: {}", order);
// Отправить alert, сохранить для ручной обработки, etc.
alertService.notifyFailedOrder(order);
}
}
7. Persistent Storage как fallback
@Service
public class DataDeliveryService {
public void ensureDelivery(DataPacket packet) {
// Попытка 1: отправить напрямую
try {
remoteService.send(packet);
return;
} catch (ServiceUnavailableException e) {
log.warn("Remote service unavailable, saving to database");
}
// Попытка 2: сохранить для отложенной доставки
PendingDelivery delivery = new PendingDelivery(
packet,
LocalDateTime.now(),
0
);
pendingDeliveryRepository.save(delivery);
}
@Scheduled(fixedDelay = 30000) // 30 сек
public void retryFailedDeliveries() {
pendingDeliveryRepository.findByAttemptsLessThan(5)
.forEach(delivery -> {
try {
remoteService.send(delivery.getPacket());
pendingDeliveryRepository.delete(delivery);
} catch (Exception e) {
delivery.incrementAttempts();
if (delivery.getAttempts() >= 5) {
alertService.notifyPermanentFailure(delivery);
}
pendingDeliveryRepository.save(delivery);
}
});
}
}
Summary
Ключевые компоненты стратегии:
- Retry + Circuit Breaker — быстрое восстановление
- Message Queue — асинхронная обработка
- Outbox Pattern — гарантированная доставка
- Dead Letter Queue — обработка исключений
- Persistent Storage — резервный механизм
- Monitoring & Alerting — видимость в систему
Этот многоуровневый подход обеспечивает надёжность, даже когда отдельные компоненты выходят из строя.