1.23M msg/s inproc | 361k msg/s ipc | 358k msg/s tcp
9.1 µs inproc latency | 49 µs ipc | 64 µs tcp
Ruby 4.0 + YJIT on a Linux VM — see
bench/for full results
gem install omq and you're done. No libzmq, no compiler, no system packages —
just Ruby talking to every other ZeroMQ peer out there.
ØMQ gives your Ruby processes a way to talk to each other — and to anything else speaking ZeroMQ — without a broker in the middle. Same API whether they live in the same process, on the same machine, or across the network. Reconnects, queuing, and back-pressure are handled for you; you write the interesting part.
New to ZeroMQ? Start with GETTING_STARTED.md — a ~30 min walkthrough of every major pattern with working code.
- Zero dependencies on C — no extensions, no FFI, no libzmq.
gem installjust works everywhere - Fast — YJIT-optimized hot paths, batched sends, GC-tuned allocations, buffered I/O via io-stream, direct-pipe inproc bypass
omqCLI — a powerful swiss army knife for ØMQ.gem install omq-cli- Every socket pattern — req/rep, pub/sub, push/pull, dealer/router, xpub/xsub, pair, and all draft types
- Every transport — tcp, ipc (Unix domain sockets), inproc (in-process queues)
- Async-native — built on fibers, non-blocking from the ground up
- Works outside Async too — a shared IO thread handles sockets for callers that aren't inside a reactor, so simple scripts just work
- Wire-compatible — interoperates with libzmq, pyzmq, CZMQ, zmq.rs over tcp and ipc
- Bind/connect order doesn't matter — connect before bind, bind before connect, peers come and go. ZeroMQ reconnects automatically and queued messages drain when peers arrive
For architecture internals, see DESIGN.md.
No system libraries needed — just Ruby:
gem install omq
# or in Gemfile
gem 'omq'require 'omq'
require 'async'
Async do |task|
rep = OMQ::REP.bind('inproc://example')
req = OMQ::REQ.connect('inproc://example')
task.async do
msg = rep.receive
rep << msg.map(&:upcase)
end
req << 'hello'
p req.receive # => ["HELLO"]
ensure
req&.close
rep&.close
endAsync do |task|
pub = OMQ::PUB.bind('inproc://pubsub')
sub = OMQ::SUB.connect('inproc://pubsub')
sub.subscribe('') # subscribe to all
task.async { pub << 'news flash' }
p sub.receive # => ["news flash"]
ensure
pub&.close
sub&.close
endAsync do
push = OMQ::PUSH.connect('inproc://pipeline')
pull = OMQ::PULL.bind('inproc://pipeline')
push << 'work item'
p pull.receive # => ["work item"]
ensure
push&.close
pull&.close
endOMQ spawns a shared omq-io thread when used outside an Async reactor — no
boilerplate needed:
require 'omq'
push = OMQ::PUSH.bind('tcp://127.0.0.1:5557')
pull = OMQ::PULL.connect('tcp://127.0.0.1:5557')
push << 'hello'
p pull.receive # => ["hello"]
push.close
pull.closeThe IO thread runs all pumps, reconnection, and heartbeating in the background.
When you're inside an Async block, OMQ uses the existing reactor instead.
All sockets expose an Async::Queue-inspired interface:
| Async::Queue | OMQ Socket | Notes |
|---|---|---|
enqueue(item) / push(item) |
enqueue(msg) / push(msg) |
Also: send(msg), << msg |
dequeue(timeout:) / pop(timeout:) |
dequeue(timeout:) / pop(timeout:) |
Defaults to socket's read_timeout |
wait |
wait |
Blocks indefinitely (ignores read_timeout) |
each |
each |
Yields messages; returns on close or timeout |
pull = OMQ::PULL.bind('inproc://work')
# iterate messages like a queue
pull.each do |msg|
puts msg.first
endAll sockets are thread-safe. Default HWM is 1000 messages per socket.
max_message_size defaults to nil (unlimited) — set
socket.max_message_size = N to cap inbound frames at N bytes; oversized
frames cause the connection to be dropped before the body is read from the
wire. Classes live under OMQ:: (alias: ØMQ).
| Pattern | Send | Receive | When HWM full |
|---|---|---|---|
| REQ / REP | Work-stealing / route-back | Fair-queue | Block |
| PUB / SUB | Fan-out to subscribers | Subscription filter | Drop |
| PUSH / PULL | Work-stealing to workers | Fair-queue | Block |
| DEALER / ROUTER | Work-stealing / identity-route | Fair-queue | Block |
| XPUB / XSUB | Fan-out (subscription events) | Fair-queue | Drop |
| PAIR | Exclusive 1-to-1 | Exclusive 1-to-1 | Block |
Work-stealing, not round-robin. Outbound load balancing uses one shared send queue per socket drained by N racing pump fibers, so a slow peer can't stall the pipeline. Under tight bursts on small
n, distribution isn't strict RR. See DESIGN.md and Libzmq quirks for the reasoning.
Each draft pattern lives in its own gem — install only the ones you use.
| Pattern | Send | Receive | When HWM full | Gem |
|---|---|---|---|---|
| CLIENT / SERVER | Work-stealing / routing-ID | Fair-queue | Block | omq-rfc-clientserver |
| RADIO / DISH | Group fan-out | Group filter | Drop | omq-rfc-radiodish |
| SCATTER / GATHER | Work-stealing | Fair-queue | Block | omq-rfc-scattergather |
| PEER | Routing-ID | Fair-queue | Block | omq-rfc-p2p |
| CHANNEL | Exclusive 1-to-1 | Exclusive 1-to-1 | Block | omq-rfc-channel |
Install omq-cli for a command-line tool that sends, receives, pipes, and transforms ZeroMQ messages from the terminal:
gem install omq-cli
omq rep -b tcp://:5555 --echo
echo "hello" | omq req -c tcp://localhost:5555See the omq-cli README for full documentation.
- omq-ffi — libzmq FFI backend. Same OMQ socket API, but backed by libzmq instead of the pure Ruby ZMTP stack. Useful for interop testing and when you need libzmq-specific features. Requires libzmq installed.
- omq-ractor — bridge OMQ sockets into Ruby Ractors for true parallel processing across cores. I/O stays on the main Ractor, worker Ractors do pure computation.
Optional plug-ins that extend the ZMTP wire protocol. Each is a separate gem; load the ones you need.
- omq-rfc-zstd — transparent Zstandard compression on the wire, negotiated per peer via READY properties.
bundle install
bundle exec rakeSet OMQ_DEV=1 to tell Bundler to load sibling projects from source
(protocol-zmtp, nuckle, omq-rfc-*, etc.) instead of released gems.
This is required for running benchmarks and for testing changes across
the stack.
# clone OMQ and its sibling repos into the same parent directory
git clone https://github.com/paddor/omq.git
git clone https://github.com/paddor/protocol-zmtp.git
git clone https://github.com/paddor/omq-rfc-zstd.git
git clone https://github.com/paddor/omq-rfc-clientserver.git
git clone https://github.com/paddor/omq-rfc-radiodish.git
git clone https://github.com/paddor/omq-rfc-scattergather.git
git clone https://github.com/paddor/omq-rfc-channel.git
git clone https://github.com/paddor/omq-rfc-p2p.git
git clone https://github.com/paddor/omq-rfc-qos.git
git clone https://github.com/paddor/omq-ffi.git
git clone https://github.com/paddor/omq-ractor.git
git clone https://github.com/paddor/nuckle.git
cd omq
OMQ_DEV=1 bundle install
OMQ_DEV=1 bundle exec rake