Все три Go-сервиса (ingest, device-control, rule-engine) отдают /metrics в формате Prometheus: счётчики MQTT/RabbitMQ/ClickHouse операций, гистограммы длительности батч-флашей и диспатча правил, переходы устройств online/offline. Добавлены сервисы prometheus и grafana в docker-compose с провижининг конфигом (datasource + два готовых дашборда: "Ingest & Telemetry" и "Automation & Devices").
59 lines
4.1 KiB
Markdown
59 lines
4.1 KiB
Markdown
# rule-engine-service (Go)
|
||
|
||
Статус: реализован (MVP).
|
||
|
||
Зона ответственности:
|
||
- Потребляет события «новое показание» из RabbitMQ (очередь
|
||
`telemetry.new_reading`, публикуемая ingest-service) — асинхронно, с ручным
|
||
ack/nack: надёжность доставки здесь важнее задержки.
|
||
- Загружает активные `automation_rules` из PostgreSQL в кэш в памяти
|
||
(обновляется по тикеру), чтобы не ходить в БД на каждое событие.
|
||
- Оценивает условия правил обобщённо: `sensor_type X operator value` против
|
||
показания — никаких зашитых названий датчиков/устройств, всё берётся из
|
||
строки `automation_rules`.
|
||
- При срабатывании: вызывает device-control-service по gRPC (нужен быстрый
|
||
результат успех/неудача) и публикует событие `automation.rule_triggered` в
|
||
RabbitMQ для будущего notification-service (не критично к задержке).
|
||
|
||
Не входит в зону ответственности: прямое взаимодействие с MQTT или Redis —
|
||
изменения состояния устройства всегда идут через gRPC API device-control-service.
|
||
|
||
## Архитектурные решения
|
||
|
||
- **automation_rules хранит внутренние ID Postgres, а не внешние device_id.**
|
||
`condition_source_device_id`/`target_device_id` — это FK на `devices.id`
|
||
(bigserial), а MQTT/Redis/gRPC везде оперируют `devices.external_id`
|
||
(строка). Поэтому кэш правил грузится JOIN'ом `automation_rules` с
|
||
`devices` (дважды — для source и target), резолвя оба ID в external_id
|
||
один раз при загрузке, а не на каждое событие.
|
||
- **Retry — только на транспортных ошибках.** Если gRPC-вызов
|
||
device-control-service не удался физически (сеть, сервис недоступен) —
|
||
событие из RabbitMQ nack'ается с `requeue=true`, всё правило
|
||
переигрывается позже. Если устройство само отклонило команду
|
||
(`CommandResult.success=false`, например неизвестный device_id) — это
|
||
финальный исход, ack, повторов не будет. Осознанное упрощение: если из
|
||
нескольких правил на одно показание одно не удалось из-за сети, при
|
||
повторной доставке переиграются ВСЕ правила читающие это показание, включая
|
||
уже успешно сработавшие — дедупликация не реализована, для MVP это
|
||
приемлемо (команды идемпотентны: turn_on дважды — не проблема).
|
||
- **Кэш правил — только для чтения активных правил**, никакой записи назад в
|
||
Postgres. CRUD правил — это зона ответственности Laravel (этап 2).
|
||
|
||
## Метрики
|
||
|
||
`GET /metrics` (порт `RULE_ENGINE_METRICS_PORT`, по умолчанию 9102) —
|
||
счётчики обработанных показаний, сработавших правил (по action_type и
|
||
исходу), latency вызова device-control-service, обновлений кэша правил.
|
||
|
||
## Запуск
|
||
|
||
```bash
|
||
cd services/rule-engine-service
|
||
go test ./...
|
||
go build ./cmd/rule-engine-service
|
||
```
|
||
|
||
Конфигурация — через переменные окружения (секция rule-engine-service в
|
||
корневом `.env.example`). Как и device-control-service, использует
|
||
`proto/device_control` через `replace` на `../../proto` в `go.mod`.
|