Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Clients

You can connect to FireMQ in two ways:

  • With a RabbitMQ client over AMQP 0-9-1. Use this to move existing applications, or when your language already has a good AMQP client.
  • With a FireMQ SDK over the FireMQ protocol. Use this for new code. The SDKs share one API across languages, confirm every publish by default, and also run in browsers over WebSocket.

Both kinds of client share the same broker: a message published over one protocol can be consumed over the other, with its properties and headers.

RabbitMQ clients

Every AMQP 0-9-1 client works. CI tests these against every change:

LanguageLibrary
Pythonpika
JavaScriptamqplib
Goamqp091-go (plus the library's own integration test suite)
JavaRabbitMQ Java client
.NETRabbitMQ.Client

Connect with the usual URL, for example amqp://guest:guest@localhost:5672/. See RabbitMQ client compatibility for the details.

FireMQ SDKs

LanguageRepositoryPackage
Rustcrates/firemq-clientfiremq-client on crates.io
GoFireMQ/firemq-gogithub.com/FireMQ/firemq-go
TypeScript: Node.js, browsersFireMQ/firemq-js@firemq/client on npm
Java, KotlinFireMQ/firemq-javaio.github.firemq:firemq-client and firemq-kotlin on Maven Central
.NETFireMQ/firemq-dotnetFireMQ.Client on NuGet
ElixirFireMQ/firemq-elixirfiremq on Hex
PHPFireMQ/firemq-phpfiremq/firemq on Packagist
Pythonbindings/pythonfiremq on PyPI
Rubybindings/rubyfiremq on RubyGems
Swift: macOS, iOSFireMQ/firemq-swiftSwift package https://github.com/FireMQ/firemq-swift
Dartbindings/dartfiremq on pub.dev, with the C library from a release
C, C++bindings/cfiremq-ffi-<target> archives on the releases page
Kotlin on Androidbindings/kotlinBuild from source; on the JVM, use firemq-kotlin

The Python, Ruby, Swift, Dart and C clients are bindings over the Rust SDK, so they ship a native library. Every SDK runs the conformance suite against a real broker in its CI.

Track progress in the roadmap issue.

Connection URLs

fire://user:password@host:5673/vhost      FireMQ protocol over TCP
fires://user:password@host:5674/vhost     over TLS
ws://host:15672/fire?vhost=dev            over WebSocket (browsers)
wss://host/fire                           over secure WebSocket, through a TLS proxy

Missing parts default to user and password guest, vhost / and the standard ports, so fire://localhost is enough for local development.

Rust

[dependencies]
firemq-client = "0.1"
tokio = { version = "1", features = ["full"] }
#![allow(unused)]
fn main() {
use firemq_client::{Client, SubscribeOptions};

async fn run() -> Result<(), firemq_client::Error> {
let client = Client::connect("fire://localhost").await?;
client.declare_queue("jobs", Default::default()).await?;
client.publish("", "jobs", "hello").await?; // returns when the broker confirms

let mut jobs = client.subscribe("jobs", SubscribeOptions::default()).await?;
while let Some(msg) = jobs.next().await {
    println!("{}", msg.body_str().unwrap_or_default());
    msg.ack()?;
}
Ok(())
}
}

The SDK's defaults favour safety: queues and exchanges are durable, messages are persistent, and publish waits for the confirm. Errors are typed: Error::Unroutable for a mandatory message nobody received, Error::Nacked when a queue rejected it, and Error::Server { code, .. } with an AMQP reply code for refused requests.

Reconnection

The Rust client reconnects on its own when the connection drops, for example while the broker restarts. After reconnecting, it does the following:

  • Declares again the exchanges, queues and bindings it declared. A queue declared with an empty name gets a new name from the broker, and the client moves its bindings and subscriptions to the new name.
  • Resumes every subscription. Your while let Some(msg) = sub.next().await loop keeps running.
  • Publishes again any message that was waiting for a confirm. Delivery is at least once, so a consumer can see a message twice.

Messages delivered before the drop and not yet acknowledged go back to the queue, and the broker delivers them again with redelivered set. The subscription skips the old copies, and acknowledging one does nothing.

While the client is reconnecting, calls wait up to 30 seconds by default and then fail with Error::Closed. Attempts back off from 100 ms to 5 s and continue until you call close. To change this, set ConnectOptions::reconnect:

#![allow(unused)]
fn main() {
use std::time::Duration;
use firemq_client::{Client, ConnectOptions, Reconnect};

async fn run() -> Result<(), firemq_client::Error> {
let mut opts = ConnectOptions::from_url("fire://localhost")?;
opts.reconnect = Some(Reconnect {
    max_attempts: Some(10),
    wait: Duration::from_secs(5),
    ..Default::default()
});
let client = Client::connect_with(opts).await?;
Ok(())
}
}

Set reconnect to None to get an error as soon as the connection drops, and handle reconnection yourself.

Write your own client

The FireMQ protocol is small: length-prefixed frames with a MessagePack header and a raw body. A client needs a MessagePack library and a few hundred lines. spec/test-vectors/frames.json has an encoded example of every frame to test your codec against.

Testing with an embedded broker (Rust)

The firemq crate runs a broker inside your tests, on free ports and in memory:

#![allow(unused)]
fn main() {
async fn run() -> std::io::Result<()> {
let server = firemq::builder().ephemeral().random_ports().start().await?;
let amqp_url = server.amqp_url().unwrap();
let fire_url = server.fire_url().unwrap();
// ... connect your code to the broker ...
server.shutdown().await;
Ok(())
}
}