MetricMQ

MetricMQ

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.

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:

  1. Broker assigns sequence ID to each published message
  2. Subscriber receives message with sequence number
  3. Subscriber sends ACK for processed sequence
  4. Broker persists ACK state to LMDB
  5. 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