CloudAMQP with Clojure
We recommend Langohr, a Clojure
client built on the RabbitMQ Java client. Create a
Leiningen project with this project.clj (its :main
namespace is the one below):
(defproject clojure-langohr-example "1.0.0-SNAPSHOT"
:description "Example of publishing and consuming messages with Langohr"
:main clojure-langohr-example.core
:dependencies [[org.clojure/clojure "1.12.6"]
[com.novemberain/langohr "5.6.0"]
;; amqp-client depends on slf4j-api 1.7, which slf4j-simple 2 can't bind to
[org.slf4j/slf4j-api "2.0.20"]
[org.slf4j/slf4j-simple "2.0.20"]]
:profiles {:uberjar {:aot :all}})
Publish and consume
Set CLOUDAMQP_URL to the AMQP URL from your instance’s details page in the
CloudAMQP console. Without it, the example connects to a local broker.
Save the connection, publisher and consumer as
src/clojure_langohr_example/core.clj:
(ns clojure-langohr-example.core
(:require [clojure.string :as str]
[langohr.basic :as lb]
[langohr.channel :as lch]
[langohr.confirm :as lcf]
[langohr.consumers :as lc]
[langohr.core :as rmq]
[langohr.queue :as lq])
(:import [javax.net.ssl SSLContext])
(:gen-class))
(defn connection-settings
"Settings for CLOUDAMQP_URL. For amqps:// the JVM's default SSLContext
verifies the server certificate and hostname; without it Langohr's
client would trust any certificate."
[uri]
(cond-> {:uri uri}
(str/starts-with? uri "amqps://") (assoc :ssl-context (SSLContext/getDefault)
:verify-hostname true)))
(defn connect []
(rmq/connect (connection-settings (or (System/getenv "CLOUDAMQP_URL")
"amqp://guest:guest@localhost"))))
(defn publish
"Publishes a persistent message and waits for the broker's confirm"
[ch queue message]
(lq/declare ch queue {:durable true :exclusive false :auto-delete false})
(lcf/select ch)
(lb/publish ch "" queue message {:content-type "text/plain" :persistent true})
(lcf/wait-for-confirms-or-die ch 5000)
(println (format " [x] Sent '%s'" message)))
(defn consume
"Consumes with a prefetch of 10, acking each message after handler returns"
[ch queue handler]
(lq/declare ch queue {:durable true :exclusive false :auto-delete false})
(lb/qos ch 10)
(lc/subscribe ch queue
(fn [ch {:keys [delivery-tag]} ^bytes payload]
(handler (String. payload "UTF-8"))
(lb/ack ch delivery-tag))
{:auto-ack false}))
(defn -main [& [mode]]
(let [conn (connect)]
(case mode
"publish" (do (publish (lch/open conn) "hello" "Hello CloudAMQP!")
(rmq/close conn))
"worker" (do (consume (lch/open conn) "hello" #(println (format " [x] Received '%s'" %)))
(println " [*] Waiting for messages. To exit press CTRL+C"))
(do (println "Usage: lein run publish|worker")
(rmq/close conn)
(System/exit 1)))))
Run lein run worker in one terminal and lein run publish in another.
A runnable example is in clojure/langohr in cloudamqp/examples.
Good to know
- Connect with the URL.
(rmq/connect {:uri url}), where the URL is the one on your instance’s details page in the CloudAMQP console. - Verify the server certificate. For
amqps://URLs, pass the JVM’s defaultSSLContextas:ssl-contextand set:verify-hostname true, asconnection-settingsdoes. Without them, Langohr 5.6 connects with a trust manager that accepts any certificate. - Automatic recovery is on by default: after a network failure the client reconnects and restores channels, queues and consumers.
- Use publisher confirms (
langohr.confirm/select,wait-for-confirms-or-die) and persistent messages, so a message isn’t lost if the connection or broker goes down. - Ack after processing, and set a prefetch limit with
langohr.basic/qos, so a consumer doesn’t buffer more messages than it can handle.