channels v0.0.1 Channels.Consumer behaviour

A behaviour module for implementing an AMQP consumer.

A Channels.Consumer is a process that can be used to keep state and provides a standard interface to execute code asynchronously when a message is received from the AMQP broker.

It is based on a GenServer therefore include functionality for tracing and error reporting. It will also fit in a supervision tree.

Example

The Channels.Consumer behaviour abstracts the common broker-consumer interaction. Developers are only required to implement the callbacks and functionality they are interested in.

Let’s start with a code example and then explore the available callbacks. Imagine we want a consumer that receives messages from a queue “my_queue” binded to a fanout exchange “my_exchange”, print the messages and respond with ack:

defmodule Printer do
  use Channels.Consumer

  def handle_message(payload, _meta, header) do
    IO.puts(header <> payload)
    {:reply, :ack, header}
  end
end

# Exchange and queue config:
config = %{
  exchange: %{
    name: "my_exchange",
    type: "fanout"
  },
  queue: %{
    name: "my_queue"
  }
}

# Start the consumer
{:ok, _pid} = Channels.Consumer.start_link(Printer, "Received: ", config)

We start our Printer by calling start_link/3, passing the module with the consumer implementation, its initial state (a header: “Received: “) and a map configuring the exchange and the queue.

When we start the consumer, the following steps are taking place:

1 - A GenServer is started. 2 - The configured exchange is being declared. 3 - The configured queue is being declared. 4 - The queue is binded to the exchange. 5 - The GenServer is subscribed to the queue.

Imagine the message “Hello world!” is published to “my_exchange”. handle_message/3 will be called with:

  • payload: “Hello world!”
  • meta: Metadata sent by the broker plus the adapter and channel the consumer is using.
  • header: “Message received: “, the internal state of the consumer.

It will print:

“Message received: Hello world!”

Callbacks

There are 4 callbacks required to be implemented in a Channels.Consumer. By adding use Channels.Consumer to your module, all 6 will be defined, leaving it up to you to implement the ones you want to customize.

Name Registration

The name registration rules are the same of a GenServer.

Summary

Functions

Sends an ack to the broker

Sends a nack to the broker. Same behaviour as ack/2

Sends a reject to the broker. Same behaviour as reject/2

Starts a new consumer without links (outside of a supervison tree)

Starts a new consumer server with the given configuration

Callbacks

Invoked to change the state of the Channels.Consumer when a different version of a module is loaded (hot code swapping) and the state’s term structure should be changed

Called when a message is received from the broker

Called when the broker informs that the consumer is subscribed as a consumer and is ready to start processing messages

Called when the consumer is connected to the broker

Called when the consumer exits

Types

action :: :ack | :nack | :reject
initial :: term
meta :: map
opts :: [GenServer.options | {:adapter, Adapter.t} | {:context, module}]
payload :: binary
reason :: term
state :: term

Functions

ack(meta, opts \\ [])

Specs

ack(meta, opts :: Keyword.t) :: :ok

Sends an ack to the broker.

This function is used to explicitly send an ack to the broker after a message has been received through handle_message/3' when the ack cannot be sent as the return value of the message handling. Themetamust be the second argument received inhandle_message/3. Theopts` argument are options to be given to the underlaying AMQP adapter. Always returns :ok.

nack(meta, opts \\ [])

Specs

nack(meta, opts :: Keyword.t) :: :ok

Sends a nack to the broker. Same behaviour as ack/2.

reject(meta, opts \\ [])

Specs

reject(meta, opts :: Keyword.t) :: :ok

Sends a reject to the broker. Same behaviour as reject/2.

start(mod, initial, config, opts \\ [])

Specs

start(module, initial, config, opts) :: GenServer.on_start

Starts a new consumer without links (outside of a supervison tree).

See start_link4 for more information.

start_link(mod, initial, config, opts \\ [])

Specs

start_link(module, initial, config, opts) :: GenServer.on_start

Starts a new consumer server with the given configuration.

  • callback_mod - The module that implements de behaviour.
  • initial - The state that will be given to the init/2 callback.
  • config - The configuration of the consumer.
  • opts - GenServer options.

Callbacks

code_change(old_vsn, state, extra)

Specs

code_change(old_vsn :: term | {:down, term}, state, extra :: term) ::
  {:ok, state} |
  {:error, reason :: term}

Invoked to change the state of the Channels.Consumer when a different version of a module is loaded (hot code swapping) and the state’s term structure should be changed.

old_vsn is the previous version of the module (defined by the @vsn attribute) when upgrading. When downgrading the previous version is wrapped in a 2-tuple with first element :down. state is the current state of the GenServer and extra is any extra data required to change the state.

Returning {:ok, new_state} changes the state to new_state and the code change is successful. Returning {:error, reason} fails the code change with reason reason and the state remains as the previous state.

If code_change/3 raises the code change fails and the loop will continue with its previous state. Therefore this callback does not usually contain side effects.

handle_message(payload, meta, state)

Specs

handle_message(payload, meta, state) :: Deliver.return_values

Called when a message is received from the broker.

If returns {:noreply, state} the consumer continues normally without responding the broker (Is expected to ack, nack or reject using the provided functions (ack/1, nack/1 and reject/1) that expect the meta argument. If returns {:reply, action, state} where action is :ack, :nack or :reject responds to the broker with the given action. If some opts have to be provided to the adapter (like requeue: false) `{:noreply, {action, opts}, state} can also be returned. If returns {:stop, reason, state} the consumer runs terminate/2 and then stops with the given reason.

handle_ready(meta, state)

Specs

handle_ready(meta, state) :: Ready.return_values

Called when the broker informs that the consumer is subscribed as a consumer and is ready to start processing messages.

If returns {:noreply, state} the consumer continues normally. If returns {:stop, reason, state} the consumer runs terminate/2 and then stops with the given reason.

init(initial)

Specs

init(initial :: term) :: Init.return_values

Called when the consumer is connected to the broker.

If returns {:ok, state} the consumer starts waiting for messages. If returns {:stop, reason} the consumer is stopped with that reason without running terminate/2

terminate(meta, state)

Specs

terminate(meta, state) :: term

Called when the consumer exits.