# health-check-service (Go) Статус: реализован. Изначально жил как горутина-тикер внутри device-control-service (см. его README/git-историю) — выделен в отдельный сервис по мере роста проекта, как и планировалось с самого начала. Зона ответственности: - Периодически (по тикеру, `HEALTH_CHECK_INTERVAL`) обходит все известные устройства из Redis-множества `devices:known` и проверяет `last_seen` каждого. - Когда устройство превышает таймаут офлайна (`HEALTH_CHECK_TIMEOUT`), переводит его `status` в `offline` в том же Redis (Device Shadow keyspace, который device-control-service читает/пишет) и публикует `device.status_changed` в RabbitMQ. Не входит в зону ответственности: - **Детекция перехода в online.** Это происходит как побочный эффект MQTT-подписки device-control-service на `devices/+/telemetry` и `devices/+/ack` (там уже есть открытое соединение и подписка ради reported_state) — заводить для этого отдельный сервис с собственной MQTT-подпиской избыточно. health-check-service работает только "в одну сторону": online → offline. - Хранение/интерпретация телеметрии — это ingest-service. - Знание о существовании устройств из PostgreSQL — этот сервис, как и device-control-service, не подключается к Postgres: список устройств для сканирования берётся из того же Redis-множества `devices:known` (пополняется device-control-service). ## Метрики `GET /metrics` на `HEALTH_CHECK_METRICS_PORT`. Счётчик `device_health_transitions_total{direction="offline"}` — намеренно та же метрика (по имени), что device-control-service публикует для `direction="online"`: Prometheus агрегирует одноимённые метрики с разных таргетов прозрачно, так что дашборд Grafana не пришлось переделывать под разделение на два сервиса. ## Запуск ```bash cd services/health-check-service go test ./... go build ./cmd/health-check-service ``` Конфигурация — через переменные окружения (секция health-check-service в корневом `.env.example`). Не зависит от `proto/` — работает только с Redis и RabbitMQ, поэтому в отличие от device-control-service/rule-engine-service сборка Docker-образа идёт из собственного контекста (не корня репозитория).