Какие паттерны проектирования распределенных систем вы знаете?csharp-65

1. Saga Pattern

Проблема: Как выполнять бизнес-транзакции, охватывающие несколько сервисов без распределенных транзакций (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;
        }
    }
}

Типы:

  • Хореография (сервисы взаимодействуют через события)
  • Оркестрация (центральный координатор управляет процессом)

2. CQRS

Проблема: Как эффективно обрабатывать разные нагрузки на чтение и запись?

Решение: Разделение моделей для команд (изменения) и запросов (чтения)

// 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);
    }
}

3. Event Sourcing

Проблема: Как сохранять историю изменений состояния и поддерживать согласованность?

Решение: Хранение последовательности событий вместо текущего состояния

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();
}

4. Circuit Breaker

Проблема: Как предотвратить каскадные сбои при отказах сервисов?

Решение: Отключение вызовов к проблемному сервису на время

// Использование Polly для Circuit Breaker
var circuitBreaker = Policy
    .Handle<HttpRequestException>()
    .CircuitBreakerAsync(
        exceptionsBeforeBreaking: 3,
        durationOfBreak: TimeSpan.FromSeconds(30)
    );

await circuitBreaker.ExecuteAsync(async () =>
{
    await _paymentService.ProcessPayment(order);
});

5. Bulkhead

Проблема: Как изолировать сбои в одной части системы от других?

Решение: Разделение ресурсов на изолированные группы

// Разделение пулов потоков для разных операций
var paymentPolicy = Policy
    .BulkheadAsync(maxParallelization: 10, maxQueuingActions: 5);

var inventoryPolicy = Policy
    .BulkheadAsync(maxParallelization: 5, maxQueuingActions: 2);

6. API Gateway

Проблема: Как упростить клиентские взаимодействия с множеством сервисов?

Решение: Единая точка входа для клиентов

// Пример маршрутизации в Gateway
app.MapReverseProxy(proxy =>
{
    proxy.MapRoute("orders", "orders/**")
        .To("http://orders-service");

    proxy.MapRoute("payments", "payments/**")
        .To("http://payments-service");
});

7. Service Discovery

Проблема: Как находить сервисы в динамической среде?

Решение: Реестр сервисов и клиентский балансировщик нагрузки

// Регистрация сервиса в Consul
var client = new ConsulClient();
await client.Agent.ServiceRegister(new AgentServiceRegistration
{
    ID = "order-service-1",
    Name = "order-service",
    Address = "localhost",
    Port = 5000
});

8. Retry Pattern

Проблема: Как обрабатывать временные сбои в распределенных системах?

Решение: Стратегии повторных попыток с экспоненциальной задержкой

var retryPolicy = Policy
    .Handle<TimeoutException>()
    .WaitAndRetryAsync(
        sleepDurations: new[]
        {
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(3),
            TimeSpan.FromSeconds(5)
        });

9. Outbox Pattern

Проблема: Как гарантировать доставку событий при работе с БД?

Решение: Сохранение событий в той же транзакции, что и изменение состояния

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();
}

10. Sidecar Pattern

Проблема: Как вынести общие функции (логирование, мониторинг) из сервисов?

Решение: Отдельный процесс (контейнер), сопровождающий основной сервис

// Пример конфигурации sidecar для логирования
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSidecar(config =>
{
    config.AddLogCollection();
    config.AddMetricsExport();
});

Резюмируем:

паттерны распределенных систем решают специфические проблемы масштабируемости, отказоустойчивости и согласованности в условиях сетевых задержек и частичных отказов. Их грамотное применение позволяет создавать устойчивые и поддерживаемые распределенные приложения.