MessagePack или JSON на устройствах: что мы сменили
Reading time: 4 minutes
Last modified:
Трекер в сотовой сети платит за каждый байт дважды: тарифом SIM и временем, пока модем не спит и сажает батарею. В мае 2024 года мы перевели сообщения GPS-трекера Micare с JSON на MessagePack. В FlowID, водном фильтре с настройкой по BLE, мы оставили JSON сознательно и добавили его ещё больше. Оба решения подошли своему каналу.
Наши задачи фиксируют, что мы поменяли и зачем, но замеров в них мало. Эфирное время и стоимость сообщения мы не записывали. Где данных нет, пост остаётся качественным и прямо об этом говорит.
Ограничение: байты на платном канале
Трекер на батарее просыпается, ловит GPS, включает модем, отправляет данные и засыпает. Чем короче передача, тем раньше сон и тем меньше трафика списывает тариф. GPS-трекер Micare работал на 2G, затем на LTE-модемах, а за основным каналом стоял резерв через USSD и SMS.
Самым дешёвым путём был USSD. Данные уходили строкой с разделителями: широта, долгота, заряд, ID устройства и время через точку с запятой. Строка короткая, но без имён полей. Парсер на сервере ждал все пять полей в строгом порядке. Реальные сообщения приходили с разным числом полей и в другом порядке. Мы переписали парсер: порядок любой, обязателен только ID устройства.
Компактность без структуры ломается, когда пропадают поля. Из этого вывод для следующего шага: оставить именованные поля и сжать кодировку.
Что мы меняли и когда
- Октябрь 2023: прошивка перешла с USSD на MQTT к нашему брокеру.
- Май 2024: в прошивке вызов сериализации сменился с JSON на MessagePack. В те же две недели получение времени перешло с NTP по TCP на NTP по UDP. Причина в задаче не указана. Следующая задача решила, что часы синхронизируются только по GPS-фиксу, без NTP и без сотовых вышек.
- Конец мая 2024: API ушло в продакшен с шагом декодирования, который сначала пробует MessagePack, а затем JSON. Устройства со старой и новой прошивкой писали в один бэкенд.
В работе над API всплыли две проблемы. Десктопный MQTT-клиент не воспроизвёл трафик устройства: опубликованное им доходило до сервера в другом виде, чем сообщения прошивки. Мы тестировали на настоящем устройстве в стейдже. Ещё мы убрали декоратор фреймворка для payload: он запускал декодирование дважды с разными входными данными.
Библиотеку выбирали из двух JavaScript-реализаций. Одна отдавала буферы, что не подошло нашему конвейеру. Мы взяли официальную @msgpack/msgpack. На npm тогда лежал бета-релиз.
Что мы измерили и что говорят документы
Байтов до и после в наших задачах нет. На странице проекта сказано, что переход должен сократить размер сообщения, точнее ничего не записано. Первоисточники добавляют:
- Сайт MessagePack: малые целые занимают один байт, короткая строка весит на байт больше текста. Больше всего выигрывает числовая телеметрия.
- Документация ArduinoJson показывает один GPS-замер в двух форматах: 62 байта в JSON (мы посчитали документированную строку) и 49 в MessagePack, примерно на 21 процент меньше. Это пример вендора, не наше устройство.
- В спецификации ключи словаря лежат в потоке как значения, так что плоский объект по-прежнему платит за имена полей. Чтобы убрать ключи, нужны массивы, а они хуже читаются.
- README
msgpack-javascriptпоказывает в бенчмарке декодер медленнееJSON.parse. Время разбора на сервере нас не ограничивало, но формат не быстрее везде.
Когда JSON лучше
FlowID показывает обратную сторону. На устройстве с 2018 года жила JSON-очередь отправки: она копила MQTT-сообщения на время обрывов связи. Задача 2021 года по багам железа подозревала, что устройство зацикливается при отправке очереди. Там же предложили хранить очередь в бинарном виде и сериализовать в JSON только при отправке: это убирает двойную сериализацию. И заменить JSON-библиотеку на обычный printf для немногих сообщений, которые собирает устройство. Задача называет это вероятными оптимизациями, и по ней не понять, что вошло в релиз.
В 2024 году для связи с телефоном мы пошли в другую сторону. Новая BLE-характеристика передаёт все данные о проливе одной JSON-структурой, единой для всех моделей устройств. MQTT-сервис получил новые версии топиков в JSON, старые остались для совместимости. Задача называет одну причину: единая структура данных для разных моделей. Мы добавим ещё две:
- Этот трафик читают люди: разработчики, поддержка, команда мобильного приложения.
- Единая структура для всех моделей важнее пары байтов на коротком радиоканале.
MessagePack тоже не требует схемы, поэтому единая структура работает в обоих форматах. Формат выбирает канал.
Чек-лист
- Запишите байты на сообщение, сообщения в сутки и единицу тарификации (байт, сообщение или эфирное время). Если не можете записать, задачи для MessagePack у вас пока нет.
- Меняйте только платный канал. JSON оставьте для настройки по BLE, логов и отладки.
- Держите именованные поля или документируйте позиции. Наша позиционная USSD-строка сломалась, когда поля пропали.
- Принимайте оба формата на сервере на время выката и умейте их различать. Нам хватило декодирования с откатом на JSON, явная метка формата надёжнее.
- Пишите в серверный лог декодированный payload в виде JSON, чтобы его мог прочитать человек.
- Тестируйте на настоящем устройстве, прежде чем доверять десктопному MQTT-клиенту.
- Фиксируйте версию библиотеки. Наш декодер был бета-сборкой.
- Замерьте эфирное время и стоимость сообщения до и после. Мы этот шаг пропустили. Сделайте его.
Обсудим ваше устройство
Мы делаем железо, прошивки и бэкенд для IoT, включая сотовую связь и BLE. Смотрите услугу разработки IoT-продуктов, кейсы GPS-трекера Micare и FlowID. Чтобы обсудить формат данных вашего устройства, напишите нам.
Часто задаваемые вопросы
MessagePack компактнее JSON?
Обычно да. На сайте MessagePack сказано: малые целые занимают один байт, короткая строка весит на байт больше своего текста. GPS-пример из документации ArduinoJson занимает 62 байта в JSON и 49 в MessagePack. Имена ключей в словарях остаются, поэтому выигрыш зависит от числа чисел и ключей в сообщении.
Нужна ли MessagePack схема?
Нет. Спецификация описывает систему типов, а не схему, так что можно слать те же свободные структуры, что и в JSON. Имена и смысл полей всё равно нужно согласовать на обеих сторонах.
Когда на устройстве оставить JSON?
Там, где данные читает человек: настройка по BLE, логи, отладка, поддержка. И там, где байты бесплатны. Меняйте платный сотовый канал, а не все каналы.
Может ли сервер принимать и JSON, и MessagePack?
Может. API нашего трекера сначала декодирует каждое MQTT-сообщение как MessagePack, при ошибке разбирает как JSON. Устройства со старой и новой прошивкой пишут в один бэкенд на время выката.