Раз в сутки, часа в четыре ночи, дашборд показывал температуру в гараже минус сто двадцать семь. Устройство это к тому моменту три недели лежало разобранным в ящике: перед смертью оно успело опубликовать провал чтения с retain-флагом, и брокер с тех пор честно раздавал это значение каждому новому подписчику. Первым порывом было лезть в скетч дашборда — а лезть надо было в брокер: он не врал, он показывал то, что в нём лежит. Пока у тебя нет стетоскопа для MQTT, ты лечишь не того пациента.
Этот урок — про три стетоскопа, которые у меня теперь открыты всегда: сырой поток всех топиков, служебная ветка $SYS, где брокер публикует собственный пульс, и лог Mosquitto, в котором видны вещи, которые платы не рассказывают. Все три консольные, все три живут на малине из урока про установку брокера, и за месяц они нашли у меня призрака из гаража, цикл из двух плат и одного прожорливого наблюдателя.
Сырой поток: sub с решёткой
mosquitto_sub -h 192.168.1.50 -t '#' -v подписывается на вообще всё и печатает две колонки: топик, пробел, payload. Та самая команда первого теста из урока про установку, только теперь она у меня не разовый ритуал, а постоянный инструмент: если данные видны здесь, а в дашборде нет — виноват дашборд; если и здесь пусто — плата или сеть. Кавычки вокруг решётки обязательны: без них шелл считает # началом комментария и съедает остаток строки — я однажды приличное время не мог понять, почему команда «выполняется, но ничего не показывает».
Три флага, которые я использую еженедельно: -C 20 — закрыть соединение после двадцати сообщений, чтобы не держать терминал; -W 30 — слушать не дольше тридцати секунд; -q 1 — наблюдать подтверждения доставки для проблемного топика. Флаг -v я ставлю всегда, даже когда кажется лишним: в потоке из пяти плат значения без адресов сливаются в кашу, а с ним каждая строка подписана топиком. Когда весь дом в ленте мешает искать, прицел сужается: mosquitto_sub -t 'home/bedroom/#' -v показывает одну комнату, а -t '$SYS/#' — только пульс брокера. Решётку можно ставить и на хвосте ветки — это те же маски из первого урока, просто в консоли.
# сырой поток всего дома: топик + payloadmosquitto_sub -h 192.168.1.50 -t '#' -v # послушать ровно немного и выйти: -C 20 сообщений или -W 30 секундmosquitto_sub -h 192.168.1.50 -t '#' -v -W 30 # пульс брокера: клиенты и сообщения в секундуmosquitto_sub -h 192.168.1.50 -t '$SYS/#' -v # стереть retained-призрак из топика мёртвого устройстваmosquitto_pub -h 192.168.1.50 -t home/old/garage/temp -n -r$SYS: пульс брокера
$SYS — служебная ветка, в которой Mosquitto публикует сам себя; подписка на $SYS/# открывает пульс без единой правки конфига. Два значения я смотрю первыми. $SYS/broker/clients/connected — сколько клиентов подключено прямо сейчас: отвалилась плата — число упало на единицу; в доме завёлся клон с чужим clientId — оно начало скакать туда-сюда, ровно как в истории из урока про clientId и статусы подключения. $SYS/broker/load/messages/received/1min — среднее число входящих сообщений в секунду за последнюю минуту: при трёх платах с периодами от 10 до 30 секунд я жду около 0,2, а однажды увидел 25 — и это был диагноз цикла раньше, чем я открыл хоть один скетч.
Число с условием: $SYS-топики обновляются раз в sys_interval — по умолчанию раз в десять секунд — и лежат как retained, поэтому свежая подписка видит значения сразу, без ожидания. Для uptime и версии брокера задержка неважна, а вот для load помните: цифра усреднена за минуту, свежий шторм проявится в ней с запаздыванием. Счётчик $SYS/broker/messages/received монотонно растёт с момента старта брокера — по разнице «было-стало» после любой правки удобно понимать, много ли добавили.
Каждый открытый терминал с подпиской — тоже клиент, и clients/connected считает его наравне с платами. Я однажды не мог понять, куда уходят два соединения сверх моих пяти устройств, — это висели два забытых терминала с sub на разных экранах. С тех пор я смотрю на это число как на счётчик всего живого вокруг брокера: платы, дашборды, консоли — всё в нём.
| Симптом | Где смотрю | Что искать |
|---|---|---|
| Дашборд показывает данные разобранного устройства | sub -v, затем pub -n -r | retained-призрак: новые публикации молчат, а старое значение приходит сразу при подключении |
| Сообщений в минуту больше ожидаемого | $SYS/broker/load/messages/received/1min | цикл между двумя топиками или плата с слишком коротким периодом |
| Платы отваливаются по очереди | лог Mosquitto | пары connect/disconnect с одним именем — конфликт clientId |
| Свежепрошитая плата не появляется | $SYS/broker/clients/connected | число не выросло: смотрим код rc в скетче и listener в конфиге |
Третий инструмент — собственный лог брокера. По умолчанию Mosquitto пишет скромный набор в syslog; мне нужнее файл, которым управляют две строки в conf.d: log_dest file и log_type. Уровни я включаю по потребности: error, warning, notice, information — постоянный набор; subscribe и unsubscribe — на время наладки подписок; debug — только на десять минут, поток он поднимает такой, что слабая карта памяти начинает ощутимо трещать. Подход тот же, что в уроке про уровни логов: постоянный шум держать на минимуме, диагностический — включать точечно.
Что лог даёт такого, чего нет в потоке. New client connected с именем-клоном каждые две секунды — воюющие clientId. Socket error on client — молчаливый обрыв, чаще всего просроченный keepalive у платы с длинным delay в loop, о котором мы говорили в уроке про публикацию с ESP32. Шторм переподключений после рестарта брокера — сотни connect в минуту, когда стоило бы поставить паузу между попытками. Мой рекорд диагностики: причина «платы периодически отваливаются» нашлась в логе за девяносто секунд — раньше, чем я открыл Arduino IDE.
# /etc/mosquitto/conf.d/logging.conflog_dest file /var/log/mosquitto/mosquitto.loglog_type errorlog_type warninglog_type noticelog_type information# на время наладки подписок добавить:# log_type subscribe# log_type unsubscribe# debug — только на десять минут # после правки: sudo systemctl restart mosquitto# смотреть хвост: tail -f /var/log/mosquitto/mosquitto.logТиповые находки за месяц мониторинга — чтобы вы знали, куда смотреть. Первая — циклическая публикация: плата А слушает топик Б и пишет в А, плата Б слушает А и пишет в Б; каждая публикация подпитывает соседнюю, и load/1min растёт сам по себе, хотя ни одна плата не шлёт быстрее планового. В сыром потоке диагноз мгновенный: один и тот же топик мелькает чаще собственного периода, а сообщения двух плат зеркалят друг друга. План потока у меня живёт рядом с паспортами устройств: плата, период, сообщений в минуту — и сумма по дому сверяется с load/1min за одну минуту. Если какая-то плата шлёт быстрее плана, сначала смотрю её цикл: приёмы те же, что в уроке про кооперативный цикл, только на этот раз за delay прячется не пинг, а лишняя публикация. Второй конец этой истории — эхо групповых команд из урока про группы: там петлю создаёт одно устройство, и лечится она ветками cmd и state.
Вторая находка — кто грузит сеть. Прикидка с условием: пять плат по одному сообщению раз в десять секунд — это сорок три тысячи сообщений в сутки, около двух с половиной мегабайтов; если роутер показывает сотни мегабайтов, дело почти всегда не в платах, а в наблюдателях. Постоянная подписка на # тянет копию всего потока, а при подключении — весь retained-архив разом; я встречал и красивый вариант: отладочный sub на # остался в автозапуске и жил неделями. Поэтому мой монитор живёт с -W 30, без автозапуска, а постоянным остаётся только $SYS.
- mosquitto_sub -t '#' -v — сырой поток: данные видны тут, но не в дашборде — виноват дашборд; кавычки вокруг # обязательны
- $SYS/# — пульс брокера: clients/connected и load/messages/received/1min, обновление раз в sys_interval (по умолчанию 10 с), значения retained
- Логи через log_dest file и log_type: базовый набор постоянно, subscribe на наладку, debug на десять минут
- Retained-призрак стирается пустой публикацией mosquitto_pub -t топик -n -r: снял устройство — сотри его retained
- Цикл двух плат виден как топик, мелькающий чаще своего периода; главным потребителем сети бывает сам монитор с подпиской на #
- 1. Дашборд показывает минус сто двадцать семь от устройства, разобранного три недели назад. Первая проверка?
- 2. Что показывает $SYS/broker/load/messages/received/1min?
- 3. Зачем в mosquitto_sub -t '#' кавычки?
- 4. Три платы с периодами 10–30 секунд, в load/1min вы видите 25. Вывод?
- 5. Чем опасен постоянный монитор с подпиской на #?
Частые вопросы по теме урока
- Дашборд показывает минус сто двадцать семь от устройства, разобранного три недели назад. Первая проверка?
- Мёртвое устройство не шлёт ничего — значит, значение лежит у брокера как retained. Пустая публикация с retain-флагом стирает запись, и дашборд честно покажет пустоту. Подписаться sub -v и стереть топик публикацией -n -r: похоже на retained-призрак
- Что показывает $SYS/broker/load/messages/received/1min?
- Это усреднённый поток входящих сообщений в секунду за минуту. Для числа клиентов есть отдельный топик clients/connected, обновляется всё раз в sys_interval. Среднее число входящих сообщений в секунду за последнюю минуту
- Зачем в mosquitto_sub -t '#' кавычки?
- Решётка для шелла — начало комментария. Кавычки защищают аргумент; печать топика включает флаг -v, а QoS ставится флагом -q. Шелл без кавычек считает # началом комментария и съедает остаток команды
- Три платы с периодами 10–30 секунд, в load/1min вы видите 25. Вывод?
- Три платы дают около 0,2 сообщения в секунду. Двадцать пять — сотня кратная разница: типичные причины — петля между двумя топиками или чей-то отладочный период в миллисекундах. Поток выше ожидаемых 0,2: ищите цикл или плату с коротким периодом
- Чем опасен постоянный монитор с подпиской на #?
- Наблюдатель с решёткой получает каждую публикацию дома и весь retained-архив при подключении. Моё правило: разовая проверка с -W 30, постоянный монитор — только $SYS. Клиент получает копию всего потока и retained-архив — сам становится нагрузкой