Введение: Почему ваш C# сервис — это не идеальный механизм
Представьте: вы написали элегантный микросервис на C#. Код прошел code review, unit-тесты покрывают 95%, интеграционные тесты зеленые. Вы выкатываете его в прод, и… через 3 часа система падает. Причина? Неожиданный скачок трафика, который перегрузил пул потоков, или внезапное отключение базы данных, которое вы не предусмотрели.
Добро пожаловать в реальный мир, где Chaos Engineering (хаос-инжиниринг) становится не роскошью, а необходимостью. Это не про «сломать всё ради веселья». Это дисциплина, которая помогает выявлять слабые места в распределенных системах до того, как их обнаружат ваши пользователи.
В этой статье мы разберем, как применять Chaos Engineering для C# приложений: от теории до реальных примеров кода с использованием библиотек Polly и Simmy. Вы узнаете, как целенаправленно вносить хаос в .NET сервисы, чтобы повысить их устойчивость и предсказуемость.
Что такое Chaos Engineering и почему это важно для .NET?
Chaos Engineering — это практика проведения экспериментов над распределенной системой, чтобы проверить её способность выдерживать неожиданные сбои. Netflix, Amazon, Microsoft — все гиганты используют это. Для C# разработчиков это особенно актуально, так как .NET Core/5+ все чаще используется для построения высоконагруженных, облачных сервисов на Microsoft Azure.
Основные принципы хаос-инжиниринга:
- Определение «steady state» (стабильного состояния) — как ваша система ведет себя в норме (латентность, throughput, error rate).
- Формулировка гипотезы — «Если упадет база данных OrderDB, сервис заказов продолжит отвечать с кешированными данными в течение 5 минут».
- Внесение хаоса — вы реально отключаете OrderDB на 30 секунд.
- Проверка гипотезы — смотрим метрики. Если гипотеза не подтвердилась — у вас есть баг в архитектуре.
Ключевое слово здесь — «эксперимент». Вы не просто надеетесь на лучшее, вы доказываете, что ваша система выживет.
Инструментарий для Chaos Engineering в C#
В мире .NET существует два мощных инструмента, которые работают в связке:
- Polly — библиотека для реализации паттернов отказоустойчивости (Retry, Circuit Breaker, Timeout, Bulkhead Isolation).
- Simmy (Microsoft.Extensions.Resilience) — библиотека для хаос-инжиниринга, расширяющая Polly. Позволяет «инжектировать» сбои в ваши политики.
Simmy — это «злой брат» Polly. Если Polly учит ваше приложение выживать, то Simmy проверяет, насколько хорошо оно это умеет, целенаправленно ломая вызовы.
Практика: Настройка хаос-эксперимента на C#
Давайте перейдем к коду. Допустим, у нас есть сервис бронирования, который обращается к внешнему API оплаты. Мы хотим проверить, как поведет себя BookingService, если API оплаты начнет отвечать с задержкой или ошибками.
Шаг 1: Установка пакетов
dotnet add package Microsoft.Extensions.Http.Polly
dotnet add package Microsoft.Extensions.Resilience
Шаг 2: Настройка HttpClient с политиками
Сначала мы настраиваем стандартную политику Circuit Breaker и Retry с помощью Polly. Затем мы «оборачиваем» её в политику хаоса из Simmy.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Resilience;
using Polly;
using Polly.Extensions.Http;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPaymentClient(this IServiceCollection services)
{
services.AddHttpClient<IPaymentService, PaymentService>(client =>
{
client.BaseAddress = new Uri("https://api.payment-gateway.com");
client.Timeout = TimeSpan.FromSeconds(5);
})
.AddPolicyHandler(GetChaosPolicy()); // Добавляем хаос
return services;
}
private static IAsyncPolicy<HttpResponseMessage> GetChaosPolicy()
{
// 1. Базовая политика отказоустойчивости (Polly)
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.InternalServerError)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var circuitBreakerPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
var basePolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
// 2. Политика хаоса (Simmy)
var chaosPolicy = new ChaosPolicyBuilder<HttpResponseMessage>()
.For<HttpRequestException>(injectionRate: 0.1) // 10% запросов будут падать с исключением
.ForResult(response => new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError), injectionRate: 0.05) // 5% запросов вернут 500
.ForLatency(delay: TimeSpan.FromMilliseconds(500), injectionRate: 0.2) // 20% запросов будут замедлены на 500 мс
.Build();
// Оборачиваем: сначала хаос, потом защита
return Policy.WrapAsync(chaosPolicy, basePolicy);
}
}
Что здесь происходит?
ChaosPolicyBuilderпозволяет задать injectionRate — процент запросов, которые будут подвержены хаосу.- Мы настроили три типа сбоев: исключение, HTTP 500 и задержка.
- Политика хаоса обернута поверх политики отказоустойчивости. Это значит, что сначала Simmy «ломает» запрос, а потом Polly пытается его «спасти» (ретрей или размыкание цепи).
Шаг 3: Мониторинг и сбор метрик
Хаос без метрик — это просто разрушение. Используйте Application Insights или Prometheus + Grafana. Simmy автоматически записывает телеметрию, если вы подключили Microsoft.Extensions.Telemetry.
// В Program.cs добавьте:
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddMeter("Polly")
.AddMeter("Simmy"));
Вы увидите счетчики: chaos-policy.injections, chaos-policy.latency и т.д. Это позволит вам видеть, как часто происходят «сбои» и как на них реагирует система.
Сценарии хаос-экспериментов для C# приложений
Вот 3 реалистичных сценария, которые можно протестировать с помощью описанного подхода: