Current section
Files
Jump to
Current section
Files
lib/rabbit/initializer.ex
defmodule Rabbit.Initializer do
@moduledoc """
A RabbitMQ initializer process.
This is a blocking process that can be used to declare exchanges, queues, and bindings.
It will perform any setup required by your application. It should be added to
your supervision tree before any producers or consumers.
Both `Rabbit.Consumer` and `Rabbit.ConsumerSupervisor` have the `handle_setup/2`
callback, which can be used to perform any queue, exchange or binding work as well.
But if you have more complex requirements, this module can be used.
## Example
# This is a connection
defmodule MyConnection do
use Rabbit.Connection
def start_link(opts \\ []) do
Rabbit.Connection.start_link(__MODULE__, opts, name: __MODULE__)
end
# Callbacks
@impl Rabbit.Connection
def init(:connection, opts) do
# Perform any runtime configuration
{:ok, opts}
end
end
# This is an initializer
defmodule MyInitializer do
use Rabbit.Initializer
def start_link(opts \\ []) do
Rabbit.Initializer.start_link(__MODULE__, opts, name: __MODULE__)
end
# Callbacks
@impl Rabbit.Initializer
def init(_type, opts) do
# Perform any runtime configuration
{:ok, opts}
end
end
# Start the connection
MyConnection.start_link()
# Start the initializer
MyInitializer.start_link(
connection: MyConnection,
exchanges: [
[name: "my_exchange_1"],
[name: "my_exchange_2", type: :fanout],
],
queues: [
[name: "my_queue_1"],
[name: "my_queue_2", durable: true],
],
bindings: [
[type: :queue, source: "my_exchange_1", destination: "my_queue_1"],
[type: :exchange, source: "my_exchange_2", destination: "my_exchange_1"],
]
)
"""
alias Rabbit.Initializer
@type t :: GenServer.name()
@type exchange ::
[
{:name, binary()}
| {:type, :direct | :fanout | :topic | :match | :headers}
| {:durable, boolean()}
| {:auto_delete, boolean()}
| {:internal, boolean()}
| {:passive, boolean()}
| {:no_wait, boolean()}
| {:arguments, list()}
]
@type queue ::
[
{:name, binary()}
| {:durable, boolean()}
| {:auto_delete, boolean()}
| {:exclusive, boolean()}
| {:passive, boolean()}
| {:no_wait, boolean()}
| {:arguments, list()}
]
@type binding ::
[
{:type, :queue | :exchange}
| {:source, binary()}
| {:destination, binary()}
| {:routing_key, binary()}
| {:no_wait, boolean()}
| {:arguments, list()}
]
@type option ::
{:connection, Rabbit.Connection.t()}
| {:retry_backoff, non_neg_integer()}
| {:retry_max, non_neg_integer()}
| {:queues, [queue()]}
| {:exchanges, [exchange()]}
| {:bindings, [binding()]}
@type options :: [option()]
@doc """
A callback executed when the initializer is started.
Returning `{:ok, opts}` - where `opts` is a keyword list of `t:option/0` will
cause `start_link/3` to return `{:ok, pid}` and the process to enter its loop.
Returning `:ignore` will cause `start_link/3` to return `:ignore` and the process
will exit normally without entering the loop
"""
@callback init(:initializer, options()) :: {:ok, options()} | :ignore
################################
# Public API
################################
@doc """
Starts an initializer process.
## Options
* `:connection` - A `Rabbit.Connection` process.
* `:exchanges` - A list of exchanges to declare. Please see [Exchanges](#start_link/3-exchanges).
* `:queues` - A list of queues to declare. Please see [Queues](#start_link/3-queues).
* `:bindings` - A list of bindings to declare. Please see [Bindings](#start_link/3-bindings).
* `:retry_sleep` - The amount of time in milliseconds to sleep between attempts
to fetch a connection from the connection process - defaults to `100`.
* `:retry_max` - The max amount of connection retries that will be attempted before
returning an error - defaults to `25`.
## Exchanges
Declaring exchanges is done by providing a list of keyword options. The options
include:
* `:name` - The name of the exchange.
* `:type` - The type of the exchange - one of `:direct`, `:fanout`, `:topic`,
`:match` or `:headers` - defaults to `:direct`.
* `:durable` - Whether the exchange is durable across broker restarts - defaults to `false`.
* `:auto_delete` - Deletes the exchange once all queues unbind from it - defaults to `false`.
* `:passive` - Returns an error if the exchange does not already exist - defaults to `false`.
* `:internal` - If set, the exchange may not be used directly by publishers, but only when
bound to other exchanges. Internal exchanges are used to construct wiring that is not visible
to applications - defaults to `false`.
Below is an example of exchange options:
[
[name: "my_exchange_1"],
[name: "my_exchange_2", type: :fanout, durable: true],
]
## Queues
Declaring queues is done by providing a list of keyword options. The options
include:
* `:name` - The name of the queue.
* `:durable` - Whether the queue is durable across broker restarts - defaults to `false`.
* `:auto_delete` - Deletes the queue once all consumers disconnect - defaults to `false`.
* `:passive` - Returns an error if the queue does not already exist - defaults to `false`.
* `:exclusive` - If set, only one consumer can consume from the queue - defaults to `false`.
Below is an example of queue options:
[
[name: "my_queue_1"],
[name: "my_queue_2", durable: true],
]
## Bindings
Declaring bindings is done by providing a list of keyword options. The options
include:
* `:type` - The type of the destination - one of `:exchange` or `:queue`.
* `:source` - The source of the binding.
* `:destination` - The destination of the binding.
* `:routing_key` - The routing key of the binding.
Below is an example of binding options:
[
[type: :queue, source: "my_exchange_1", destination: "my_queue_1"],
[type: :exchange, source: "my_exchange_2", destination: "my_exchange_1"]
]
## Server Options
You can also provide server options - which are simply the same ones available
for `t:GenServer.options/0`.
"""
@spec start_link(module(), list(), GenServer.options()) :: Supervisor.on_start()
def start_link(module, opts \\ [], server_opts \\ []) do
Initializer.Server.start_link(module, opts, server_opts)
end
defmacro __using__(opts) do
quote location: :keep do
@behaviour Rabbit.Initializer
if Module.get_attribute(__MODULE__, :doc) == nil do
@doc """
Returns a specification to start this consumer under a supervisor.
See `Supervisor`.
"""
end
def child_spec(args) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, [args]}
}
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
end
defoverridable(child_spec: 1)
end
end
end