Проблема: Как выполнять бизнес-транзакции, охватывающие несколько сервисов без распределенных транзакций (2PC)?
Решение: Разбиение транзакции на последовательность локальных транзакций с компенсирующими действиями
// Пример реализации Saga
public class OrderSaga
{
private readonly IPaymentService _payment;
private readonly IInventoryService _inventory;
public async Task Execute(Order order)
{
try
{
await _payment.ReserveFunds(order);
await _inventory.ReserveItems(order);
}
catch
{
// Компенсирующие действия
await _payment.CancelReservation(order);
await _inventory.CancelReservation(order);
throw;
}
}
}
Типы:
Проблема: Как эффективно обрабатывать разные нагрузки на чтение и запись?
Решение: Разделение моделей для команд (изменения) и запросов (чтения)
// Command side
public class OrderCommandHandler
{
public async Task Handle(PlaceOrderCommand cmd)
{
// Валидация и бизнес-логика
_repository.Add(cmd.Order);
}
}
// Query side
public class OrderQueryService
{
public async Task<OrderDto> GetOrder(Guid id)
{
// Оптимизированная модель для чтения
return _readModelRepository.Get(id);
}
}
Проблема: Как сохранять историю изменений состояния и поддерживать согласованность?
Решение: Хранение последовательности событий вместо текущего состояния
public class OrderAggregate
{
private readonly List<IEvent> _changes = new();
public void Apply(OrderPlaced @event) { /*...*/ }
public void PlaceOrder(OrderDetails details)
{
Apply(new OrderPlaced(details));
}
public IReadOnlyCollection<IEvent> GetChanges() => _changes.AsReadOnly();
}
Проблема: Как предотвратить каскадные сбои при отказах сервисов?
Решение: Отключение вызовов к проблемному сервису на время
// Использование Polly для Circuit Breaker
var circuitBreaker = Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync(
exceptionsBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30)
);
await circuitBreaker.ExecuteAsync(async () =>
{
await _paymentService.ProcessPayment(order);
});
Проблема: Как изолировать сбои в одной части системы от других?
Решение: Разделение ресурсов на изолированные группы
// Разделение пулов потоков для разных операций
var paymentPolicy = Policy
.BulkheadAsync(maxParallelization: 10, maxQueuingActions: 5);
var inventoryPolicy = Policy
.BulkheadAsync(maxParallelization: 5, maxQueuingActions: 2);
Проблема: Как упростить клиентские взаимодействия с множеством сервисов?
Решение: Единая точка входа для клиентов
// Пример маршрутизации в Gateway
app.MapReverseProxy(proxy =>
{
proxy.MapRoute("orders", "orders/**")
.To("http://orders-service");
proxy.MapRoute("payments", "payments/**")
.To("http://payments-service");
});
Проблема: Как находить сервисы в динамической среде?
Решение: Реестр сервисов и клиентский балансировщик нагрузки
// Регистрация сервиса в Consul
var client = new ConsulClient();
await client.Agent.ServiceRegister(new AgentServiceRegistration
{
ID = "order-service-1",
Name = "order-service",
Address = "localhost",
Port = 5000
});
Проблема: Как обрабатывать временные сбои в распределенных системах?
Решение: Стратегии повторных попыток с экспоненциальной задержкой
var retryPolicy = Policy
.Handle<TimeoutException>()
.WaitAndRetryAsync(
sleepDurations: new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5)
});
Проблема: Как гарантировать доставку событий при работе с БД?
Решение: Сохранение событий в той же транзакции, что и изменение состояния
public async Task PlaceOrder(Order order)
{
using var transaction = _db.Database.BeginTransaction();
_db.Orders.Add(order);
_db.OutboxMessages.Add(new OutboxMessage
{
Event = new OrderPlacedEvent(order)
});
await _db.SaveChangesAsync();
await transaction.CommitAsync();
}
Проблема: Как вынести общие функции (логирование, мониторинг) из сервисов?
Решение: Отдельный процесс (контейнер), сопровождающий основной сервис
// Пример конфигурации sidecar для логирования
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSidecar(config =>
{
config.AddLogCollection();
config.AddMetricsExport();
});
паттерны распределенных систем решают специфические проблемы масштабируемости, отказоустойчивости и согласованности в условиях сетевых задержек и частичных отказов. Их грамотное применение позволяет создавать устойчивые и поддерживаемые распределенные приложения.