MetricMQ
Projects | | Links: Github-Link
Lightweight message broker with pub/sub and queue patterns, binary protocol support, and LMDB persistence.
A Lightweight message broker with pub/sub and queue patterns, binary protocol support, and LMDB persistence.
-
Technical Details (MetricMQ README) - README.md
-
Developer Reference - https://metricmq-docs.netlify.app/
-
Getting Started Guide - https://www.sapnag.me/blog/es/2026-05-15-metricmq1/
The Gap in IoT Messaging
On one end of the spectrum, you have fire-and-forget solutions like raw UDP or standard MQTT QoS 0. they are fast, but no guarantees. On the other end, you have RabbitMQ or Kafka, extremely reliable, but they are heavy 50MB+ server applications.
MetricMQ fills this gap. It is a 328 KB single-executable broker build with C++20 , POCO lib and libSodium, that gives you exactly-once delivery, built-in Ed25519 message signing, and Prometheus metrics out of the box.
Three questions follow every IoT project:
What happens when WiFi drops? Do those readings disappear, or can they be recovered?
How do you send one reading to many places? Does every subscriber get its own copy?
How do you know a reading is real? If something on your network sends fake sensor data, can you detect it?
Most messaging tools answer one of these well. MetricMQ tries to answer all three in a broker that compiles to 328 KB and ships a native Arduino/PlatformIO library for the ESP32.
Use MetricMQ when: You need guaranteed delivery (you want to know a message actually arrived), local deployments without complex config, and cryptographically verified messages on constrained microcontrollers like the ESP32.
This post walks through four demos, each building on the last. You need one ESP32, a PC running the broker, and PlatformIO. No prior message broker experience needed.
Core Features in a Nutshell
MetricMQ provides powerful features directly out of the box without complicated plugins:
- Dual Protocol Auto-Detection: Connect using the human-readable Redis text protocol (RESP) for debugging via
redis-cli, or use the Binary Protocol (40% smaller footprint) for embedded production. The broker auto-detects based on the first byte! - Exactly-Once Delivery: Messages are persisted to an embedded LMDB database. If a subscriber disconnects and reconnects, only unacknowledged messages are replayed. No gaps, no duplicates.
- Ed25519 Message Signing: Publishers sign messages, and the broker rejects tampered packets at the wire level before they reach subscribers.
- Zero-Config Observability: A Prometheus-compatible endpoint automatically runs on port 9091.
Why Not Just Use MQTT?
MQTT is excellent and widely used. But its delivery guarantees have tradeoffs worth understanding before you pick anything:
QoS 0 (fire and forget) — fast, zero overhead, but if the network drops mid-publish, the message is gone with no record of it.
QoS 1 (at least once) — the broker acknowledges receipt, so messages get through. But a reconnect at the wrong moment can deliver the same message twice. Your code has to handle duplicates.
QoS 2 (exactly once) — four-packet handshake, guaranteed no loss, no duplicates. But the broker (Mosquitto, EMQX, HiveMQ) is a separate service you install, configure, and maintain. These range from 3 MB to 50 MB and need persistent configuration.
MetricMQ is built to deliver exactly-once semantics in a 328 KB broker with no configuration overhead. The tradeoff is maturity: it is still early and not yet as battle-tested as established platforms. If you are running large, production-scale fleets, NanoMQ remains the safer choice. But for prototyping, edge deployments, and full-stack environments where you need delivery guarantees without heavyweight broker infrastructure, MetricMQ is a strong fit.
| MetricMQ | MQTT QoS 0 | MQTT QoS 2 | RabbitMQ | |
|---|---|---|---|---|
| Broker size | 328 KB | ~3 MB | ~3 MB | ~50 MB |
| Exactly-once | ✅ | ❌ | ✅ | ✅ |
| Message signing | ✅ Built-in | ❌ | ❌ | ❌ |
| Native ESP32 client | ✅ | ✅ | ✅ | ❌ |
redis-cli works |
✅ | ❌ | ❌ | ❌ |
| Config file needed | ❌ | ✅ | ✅ | ✅ |
| Production maturity | Early/beta | High | High | High |
MetricMQ’s Architecture
Publishers (ESP32 / desktop)
│ binary protocol (16-byte header)
│ or RESP (redis-compatible text)
▼
┌─────────────────────────────────────┐
│ Session Manager │
│ Auto-detects protocol (first byte) │
│ Per-session thread, idle timeout │
├─────────────────────────────────────┤
│ Security Layer │
│ Ed25519 verify (libsodium) │
│ Topic ACL enforcement per key_id │
├─────────────────────────────────────┤
│ Message Router │
│ Wildcard topic matching │
│ Pub/sub + queue/worker patterns │
├─────────────────────────────────────┤
│ LMDB Persistence │
│ Sequence IDs + ACK tracking │
│ Compaction at 100K messages │
├─────────────────────────────────────┤
│ Prometheus Metrics (port 9091) │
│ Counters, gauges, histograms │
│ Grafana-ready, standard PromQL │
└─────────────────────────────────────┘
│
▼
Subscribers (ESP32 / desktop / redis-cli)
Key design decisions:
| Decision | Why |
|---|---|
| Custom 16-byte binary header | 40% smaller frames than RESP — matters on cellular and congested WiFi |
| Ed25519 over TLS for auth | Per-message identity without session overhead - MCUs verify without a full TLS stack, saves MCU’s memory significantly |
| LMDB over SQLite | Memory-mapped, zero-copy reads, O(1) sequence ID lookups, no query parser |
| RESP compatibility | Any redis-cli works for debugging — zero additional tooling |
| Prometheus over custom metrics | Drop-in Grafana integration, standard PromQL, production alerting rules |
MetricMQ vs. The Alternatives
Here is a realistic comparison of when to use what:
| Tool | Pros | Cons | Use Case |
|---|---|---|---|
| Raw UDP/TCP | Zero overhead, extremely fast | No delivery guarantees, requires custom framing | Video streaming, local LAN discovery |
| Mosquitto (MQTT) | Industry standard, QoS 1/2 support | Complex setup, fire-and-forget by default | Large enterprise integrations, legacy IoT |
| RabbitMQ/Kafka | Massive scale, highly reliable | Requires a server/cluster, high memory usage | Cloud data pipelines, heavy backends |
| MetricMQ | 328 KB broker, exactly-once, auto Redis protocol | No TLS yet, capped at 1000 connections | Edge computing, constrained sensor networks |
MetricMQ vs NanoMQ
The MetricMQ’s architecture do come close to the NanoMQ, They solve different problems.
Side-by-Side Comparison
| NanoMQ | MetricMQ | |
|---|---|---|
| Binary size | Under 200 KB in minimum feature set netlify | 328 KB |
| Protocol | MQTT 3.1.1 and MQTT 5.0, industry standard | Custom binary + RESP (Redis-compatible) |
| Throughput | Million-level TPS, up to 10× faster than Mosquitto on multi-core netlify | ~106K msg/s (single thread, 10 KB messages) |
| Concurrency model | Built on NNG (Nanomsg-Next-Generation), async I/O, actor model, multi-threaded | Single global mutex — contention above ~500 clients |
| TLS/SSL | Yes — full TLS/SSL support | ❌ Not yet |
| Message signing | ❌ None | ✅ Ed25519 at wire level |
| Exactly-once delivery | MQTT QoS 2 (standard) | Custom sequence ID + LMDB ACK tracking |
| Persistence | Built-in message persistence, resume uploading on reconnect | LMDB — last 100K messages |
| Rule engine | SQL-based rule engine, integrates with eKuiper for stream processing | ❌ None |
| Protocol bridging | DDS, ZMQ, SOME/IP, Iceoryx, cloud MQTT bridging | ❌ None |
| Native ESP32 client | ❌ No official Arduino library | ✅ Arduino/PlatformIO library |
redis-cli compatible |
❌ | ✅ RESP protocol auto-detected |
| Prometheus metrics | ❌ Not built-in | ✅ Port 9091 from startup |
| Config file | Yes — nanomq.conf required |
❌ Zero config needed |
| Maturity | Active project, one of the most active MQTT brokers available, backed by EMQ Technologies | Early/beta, solo project |
| Community | Thousands of GitHub stars, corporate backing | Small, early stage |
| License | MIT | MIT |
Where NanoMQ Clearly Wins
Throughput and concurrency. NanoMQ achieves million-level TPS using a built-in actor model with thread-level parallelism, while MetricMQ uses a single global mutex that limits it to around 500 concurrent clients before contention degrades performance. This is not a close comparison, NanoMQ’s architecture is fundamentally more scalable.
Production maturity. NanoMQ is one of the most active MQTT brokers available, backed by EMQ Technologies who also build EMQX (the most starred MQTT broker on GitHub). MetricMQ is a solo project.
Where MetricMQ Does Something Different
Ed25519 signing at the wire level. NanoMQ has no equivalent. MQTT’s security model is TLS for transport + username/password or client certificates for authentication. There is no concept of per-message cryptographic signing in any MQTT broker. MetricMQ’s approach is to sign the payload so the broker verifies the publisher’s identity for every single message before routing , This enhances security in an IOT applications, and is architecturally distinct from NanoMQ.
Native ESP32 Arduino library. NanoMQ has no official ESP32 client. You’d use an existing MQTT library (PubSubClient, AsyncMQTT) with NanoMQ which works fine, but you’re back to standard MQTT semantics. MetricMQ’s client was written specifically for Arduino/PlatformIO and exposes the exactly-once and signing features directly.
redis-cli interop. NanoMQ requires MQTT tooling like, MQTTX, mosquitto_sub, etc. MetricMQ lets you use redis-cli for instant debugging from any terminal, which is genuinely convenient during development.
Zero configuration. NanoMQ needs a nanomq.conf. MetricMQ starts with no config file, The fewer steps to first PONG, the better.
Prometheus built in. NanoMQ doesn’t expose a Prometheus endpoint natively. MetricMQ does, from port 9091 at startup.
Build Requirements
- Windows 10+ / Linux / macOS
- CMake 3.20+
- C++20 compiler (MSVC 2022, GCC 11+, Clang 13+)
- Conan 2.x package manager
Installation
git clone https://github.com/saptarshi-max/MetricMQ.git
cd MetricMQ
mkdir build && cd build
conan install .. --output-folder=. --build=missing
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
cmake --build . --config Release
Quick Test
Terminal 1:
cd build/Release
./metricmq-broker.exe
Terminal 2:
./sub_only.exe
Terminal 3:
./pub_only.exe
Protocol Support
RESP Protocol (Redis-Compatible)
Human-readable text protocol. Compatible with redis-cli:
redis-cli -p 6379 PUBLISH test "Hello World"
redis-cli -p 6379 SUBSCRIBE test
Binary Protocol
Optimized for embedded systems:
- 17-byte header overhead
- Zero-copy parsing
- Frame:
[VERSION|COMMAND|SEQ|TOPIC_LEN|PAYLOAD_LEN|TOPIC|PAYLOAD]
Protocol is auto-detected on connection (first byte: * = RESP, 0x01 = Binary)
Exactly-Once Delivery
Sequence-based ACK mechanism prevents duplicate message processing:
- Broker assigns sequence ID to each published message
- Subscriber receives message with sequence number
- Subscriber sends ACK for processed sequence
- Broker persists ACK state to LMDB
- On reconnect, broker skips already-ACKed messages
Performance Benchmarks
Test environment: Windows 11, Snapdragon X Plus (12-core Oryon CPU), Adreno GPU, 16GB LPDDR5x RAM, SSD
Throughput
Binary Protocol: 106,390 msg/s (10KB messages)
Data Rate: 1.01 GiB/s sustained
Publish Latency: 46.5 μs per operation
Persistence (LMDB)
Sequential Writes: 42,674 msg/s (1KB messages)
Random Reads: 1,564,210 ops/s
Storage Overhead: 60% throughput reduction vs in-memory
Binary Size
metricmq-broker.exe: 328 KB
vs ZeroMQ: 2.3 MB (7x larger)
vs RabbitMQ: 50 MB (150x larger)
Running Benchmarks
cd build/Release
.\latency_benchmark.exe
.\throughput_benchmark.exe
.\protocol_comparison_benchmark.exe
.\persistence_benchmark.exe
# JSON output
.\latency_benchmark.exe --benchmark_out=results.json --benchmark_out_format=json
ESP32/Arduino Library
Lightweight client library for ESP32 and ESP8266:
- Binary protocol support
- Exactly-once delivery
- Auto-reconnect with exponential backoff
- 2KB RAM footprint
Arduino Installation
Arduino IDE: Sketch → Include Library → Add .ZIP Library → Select esp32-metricmq
PlatformIO:
lib_deps = https://github.com/saptarshi-max/MetricMQ.git#main:esp32-metricmq
ESP32 Example
#include <WiFi.h>
#include <MetricMQ.h>
MetricMQClient mqClient;
void setup() {
WiFi.begin("SSID", "password");
mqClient.connect("192.168.1.100", 6379);
mqClient.subscribe("sensors/temp", [](String topic, String payload) {
Serial.println(payload);
});
}
void loop() {
mqClient.loop();
mqClient.publish("sensors/temp", "25.5");
delay(5000);
}
Supported: ESP32, ESP8266, Arduino with network capability
API Reference
C++ Client
RESP Protocol:
#include "metricmq/pubsub.hpp"
metricmq::Publisher pub("127.0.0.1", 6379);
pub.send("topic", "payload");
metricmq::Subscriber sub("127.0.0.1", 6379);
sub.subscribe("topic", [](const std::string& topic, const std::string& payload) {
std::cout << payload << "\n";
});
Binary Protocol:
#include "metricmq/binary_pubsub.hpp"
metricmq::BinaryPublisher pub("127.0.0.1", 6379);
pub.publish("topic", "payload");
metricmq::BinarySubscriber sub("127.0.0.1", 6379, "client-id");
sub.subscribe("topic", [](const BinaryMessage& msg) {
std::cout << msg.sequence << ": " << msg.payload << "\n";
});
Example Programs
RESP Protocol:
.\pub_only.exe
.\sub_only.exe
.\simple_pub_sub.exe
Binary Protocol:
.\binary_pub_only.exe
.\binary_sub_only.exe
Queue Mode:
.\push_only.exe
.\pull_only.exe
Testing:
.\persistence_test.exe
.\exactly_once_test.exe
Monitoring
Prometheus metrics available at http://localhost:9091/metrics:
metricmq_messages_published_total
metricmq_messages_delivered_total
metricmq_publish_latency_microseconds
metricmq_active_connections
metricmq_active_topics
Testing
cd build
ctest -C Release
License
MIT License - see LICENSE file