A lightweight, low-latency gRPC-based message broker for microservices. Built as a leaner alternative to Kafka.
Künefe (pronounced /ˌkuːnəˈfeɪ/) is a traditional Turkish dessert known for its layered structure. Just like the dessert, Kunefe MQ is built in layers — each one doing exactly what it should.
Kafka is a powerful system, but it comes with significant operational overhead — ZooKeeper or KRaft, multiple brokers, Schema Registry, complex configuration. For small-to-medium microservice architectures, this is often overkill.
Kunefe MQ makes a different set of trade-offs:
| Kafka | Kunefe | |
|---|---|---|
| Consumption model | Pull-based | ✅ Push-based (server streaming) |
| Ordering | ✅ Global, always guaranteed | |
| Partitioning | ✅ No - intentionally omitted | |
| Dependencies | ✅ None | |
| Deployment | ✅ Single Docker container | |
| Throughput | ✅ Millions of msg/sec | |
| Target | ✅ Large-scale systems |
Kunefe optimizes for low latency and developer experience over raw throughput.
- Push-based consumption — broker pushes messages to subscribers over a long-lived gRPC server-streaming connection. No polling, no
linger.ms. - Append-only log — messages are written sequentially to disk using
RandomAccessFile. Immutable, durable, and fast. - Segment-based log rotation — log files are split into segments and rotated based on size or age, with configurable retention policy.
- Persistent offsets — consumer group offsets are stored in RocksDB. Broker restarts do not cause message loss or redelivery.
- Java 21 Virtual Threads — each subscriber runs on a dedicated virtual thread. Thousands of concurrent consumers with zero platform thread exhaustion.
- Global ordering — no partitions means no partition-key complexity. Message order is always guaranteed within a topic.
Tested on Apple M4 Pro 24GB, Java 21 — single thread, local gRPC
| Benchmark | Result |
|---|---|
| Producer throughput (64B) | ~16,300 msg/sec |
| Producer throughput (1KB) | ~15,600 msg/sec |
| Producer throughput (10KB) | ~14,500 msg/sec |
| End-to-end latency (p50) | ~588ms* |
*E2E latency includes 100ms push loop interval.
Reducible via kunefe.log.retention.check-interval-ms configuration.
docker run -p 6565:6565 -p 8080:8080 selimsahindev/kunefe-broker:latestIncludes Prometheus and Grafana out of the box:
docker compose up- Broker:
localhost:6565 - Metrics:
localhost:8080/actuator/prometheus - Grafana:
localhost:3000(admin/admin) - Prometheus:
localhost:9090
// build.gradle.kts
implementation("dev.selimsahin.kunefe:kunefe-spring-boot-starter:0.1.0")# application.yml
kunefe:
broker:
host: localhost
port: 6565@Autowired
private KunefeTemplate kunefeTemplate;
public void placeOrder(Order order) {
kunefeTemplate.send("orders", objectMapper.writeValueAsBytes(order));
}@KunefeListener(topic = "orders", group = "order-service")
public void onOrder(byte[] payload) {
Order order = objectMapper.readValue(payload, Order.class);
// process order
}kunefe/
├── kunefe-proto/ # Protobuf contracts — BrokerService, ProducerService, ConsumerService
├── kunefe-broker/ # Broker application — log engine, gRPC services, offset store
├── kunefe-client/ # Core Java client — KunefeClient, KunefeProducer, KunefeConsumer
├── kunefe-spring-boot-starter/ # Auto-configuration — KunefeTemplate, @KunefeListener
└── kunefe-test/ # Integration tests
git clone https://github.com/selimsahindev/kunefe-mq.git
cd kunefe-mq
./gradlew buildRun the broker:
./gradlew :kunefe-broker:bootRunRun tests:
./gradlew testMIT
