четверг, 9 июля 2026 г.

ILivenessStrategy

ILivenessStrategy

Английское наименование: ILivenessStrategy.

Русская трактовка: Это интерфейс (контракт), который определяет стратегию или алгоритм проверки «живучести» (liveness) компонента. В контексте распределенных систем, микросервисов и архитектур с долгими фоновыми задачами этот паттерн позволяет отделить логику принятия решения («Жив ли я?») от механизма оповещения об ошибке (например, сброса нагрузки или остановки процесса).

Слово «Strategy» в названии прямо указывает на применение соответствующего поведенческого паттерна проектирования. Это означает, что у вас может быть несколько разных реализаций этого интерфейса для разных сценариев.

Детальный разбор компонентов

Интерфейс ILivenessStrategy обычно содержит один основной метод:

  • bool IsAlive(); или Task<bool> IsAliveAsync();

Этот метод не лечит проблему, он только отвечает на вопрос «Да/Нет».

Зачем это нужно (проблематика)

Представьте ваш FractalCellTemplate. Как внешний мониторинг должен понять, что ячейка не зависла?

  1. Если просто проверять наличие потока (thread), то поток может работать (не умер), но при этом находиться в бесконечном цикле ожидания заблокированного ресурса (deadlock). Система формально жива, но фактически бесполезна.
  2. Если поместить проверку внутрь ячейки, она сама должна решать, что считать жизнью.

Именно здесь появляется ILivenessStrategy. Вы внедряете его в ячейку, и теперь проверка жизни становится настраиваемой.

Примеры конкретных стратегий (реализаций интерфейса)

Вместо того чтобы захардкодить одну проверку, вы создаете разные классы:

  1. HeartbeatLivenessStrategy (Стратегия сердцебиения)

    • Логика: Внутри ячейки есть поле _lastUpdate. Каждый раз, когда поведение успешно обрабатывает событие, оно обновляет это время. Стратегия проверяет: (DateTime.UtcNow - _lastUpdate) < Timeout.
    • Применение: Идеально подходит для вашей архитектуры. Если поведения перестали вызывать свои хендлеры — значит, конвейер встал, и стратегия вернет false.
  2. QueueLengthLivenessStrategy (Стратегия длины очереди)

    • Логика: Проверяет внутренний буфер сообщений. Если количество ожидающих обработки событий превышает критический порог (например, 10 000), система считается неживой, так как она не справляется с нагрузкой и находится в состоянии перегрузки (overload).
    • Применение: Защита от каскадного падения. Лучше сбросить текущую задачу, чем накопить гигантский бэклог, который никогда не будет обработан.
  3. DependencyPingLivenessStrategy (Стратегия пинга зависимостей)

    • Логика: Ячейка живет до тех пор, пока доступна база данных или шина сообщений. Стратегия делает легковесный запрос (SELECT 1) к БД. Если ответ не пришел за 500 мс — компонент мертв.
    • Применение: Проверка целостности среды выполнения.
  4. ResourceUsageLivenessStrategy (Стратегия потребления ресурсов)

    • Логика: Проверяет использование CPU или памяти самим процессом. Если потребление RAM > 90% в течение минуты, считаем, что произошла утечка памяти, и процесс пора перезапустить.

Применение в вашем коде

Ваша фабрика могла бы выглядеть так:

csharp
// Регистрация конкретной стратегии
services.AddSingleton<ILivenessStrategy, HeartbeatLivenessStrategy>();

// Внутри FractalCellCore
public class FractalCellTemplate 
{
    private readonly ILivenessStrategy _livenessStrategy;
    
    public FractalCellTemplate(ILivenessStrategy livenessStrategy) 
    {
        _livenessStrategy = livenessStrategy;
    }

    // Внешний HealthCheck вызывает этот метод
    public async Task ReportHealthAsync() 
    {
        if (!await _livenessStrategy.IsAliveAsync())
        {
            // Логика реакции: остановить BehaviorRouter, отменить токены, отправить алерт
            await InitiateGracefulShutdown("Liveness check failed");
        }
    }
}

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

Как работает HeartbeatLivenessStrategy?

Какие еще бывают стратегии проверки живучести, кроме Heartbeat?

Зачем нужен ILivenessStrategy, если можно просто проверять поток?

Разберем эти вопросы по порядку, так как они затрагивают самую суть мониторинга распределенных систем.

1. Как работает HeartbeatLivenessStrategy?

Это одна из самых надежных и простых стратегий для систем с активной обработкой данных (как ваша). Она основана на принципе «последнего известногого хорошего состояния».

Механика работы:

  1. Состояние: Внутри ячейки (FractalCellCore) или в общей области памяти хранится поле _lastHeartbeatTimestamp типа DateTime.
  2. Обновление (Write): Каждый раз, когда происходит значимое событие — например, поведение успешно завершило обработку сообщения или фабрика создала новую ячейку, — это поле обновляется текущим временем UTC: _lastHeartbeatTimestamp = DateTime.UtcNow;
  3. Проверка (Read): Внешний монитор (или сам FractalCellTemplate по таймеру) вызывает метод стратегии IsAlive(). Стратегия выполняет простое вычисление:
    csharp
    var elapsed = DateTime.UtcNow - _lastHeartbeatTimestamp;
    return elapsed < _timeoutThreshold;
  4. Результат: Если время с момента последнего обновления превысило порог (_timeoutThreshold, например, 30 секунд), стратегия возвращает false.

Почему это эффективно? Если конвейер поведений зависнет внутри хендлера (например, из-за deadlock при обращении к БД), новые события перестанут обрабатываться, поле _lastHeartbeatTimestamp не обновится, и через 30 секунд система сама поймет, что она мертва, инициировав корректную остановку (Graceful Shutdown).


2. Какие еще бывают стратегии проверки живучести?

Помимо Heartbeat, существует целый класс алгоритмов, выбор которых зависит от того, какую именно проблему вы пытаетесь обнаружить.

НазваниеЛогика проверкиЧто обнаруживает
Dependency PingПытается выполнить легковесный запрос к критической зависимости (БД, брокеру сообщений, кешу).Проблемы со связью или падение внешней инфраструктуры.
Resource SaturationПроверяет метрики ОС: % CPU > 95% ИЛИ доступная RAM < 100 МБ.Утечки ресурсов, бесконечные циклы, состояние перегрузки (overload).
Queue Stall DetectionСравнивает длину входящей очереди сейчас и минуту назад. Если длина не уменьшилась (и очередь не пуста) — компонент мертв.Зависание потребителя событий при наличии работы.
Progress MarkerХранит ID последнего успешно обработанного элемента (например, номер строки файла или ID транзакции). Если за 5 минут ID не изменился — сбой.Тихое зависание на обработке одного конкретного сложного элемента.
Contract ValidationБерет случайное сообщение из тестового набора и прогоняет его через логику. Результат должен соответствовать ожидаемому эталону.Порча кода бизнес-логики (регрессионное тестирование на лету).
External Signal (Watchdog)Ожидает сигнала («пинка») от внешнего доверенного процесса (ОС или планировщика). Если сигнал не пришел вовремя — сброс.Полная заморозка всего процесса операционной системой.

3. Зачем нужен ILivenessStrategy, если можно просто проверять поток?

Простая проверка потока (thread) — это самый примитивный уровень жизнеспособности, который часто дает ложноположительные результаты. Вот почему он недостаточен:

Проблема 1: Ложная жизнь (False Positive / Zombie Thread) Поток может быть активен (процессор считает его запущенным), но находиться в состоянии взаимной блокировки (deadlock) или бесконечно ждать освобождения мьютекса, которого никогда не будет.

  • Пример: Поток ждет ответа от базы данных, которая упала. Поток жив, CPU почти не используется, но приложение ничего не делает. Простая проверка скажет: «Все хорошо». А ILivenessStrategy (типа Heartbeat или Dependency Ping) честно скажет: «Мы умерли».

Проблема 2: Проблема черного ящика Проверка потока отвечает только на вопрос: «Работает ли операционная система?». Она не отвечает на вопрос: «Выполняет ли мое приложение свою бизнес-функцию?».

  • Пример: В вашем приложении есть два независимых конвейера: один обрабатывает платежи, другой генерирует отчеты. Платежный поток упал из-за исключения, а отчетный продолжает работать. Для системы мониторинга потоков процесс выглядит здоровым (один поток крутится). Но фактически сервис платежей мертв. Разные ILivenessStrategy позволяют мониторить каждый конвейер отдельно.

Проблема 3: Планирование .NET Runtime В современных асинхронных приложениях (.NET async/await) код выполняется не на одном постоянном потоке, а прыгает между потоками из пула (Thread Pool). Один этап обработки сообщения мог начаться на потоке №1, а закончиться на потоке №5. Привязывать проверку жизни к конкретному физическому потоку в такой архитектуре бессмысленно.

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

Комментариев нет:

Отправить комментарий