hare v0.2.2 Hare.RPC.Client behaviour

A behaviour module for implementing AMQP RPC client processes.

The Hare.RPC.Client module provides a way to create processes that hold, monitor, and restart a channel in case of failure, exports a function perform RPC to an exchange and receive the response, and some callbacks to hook into the process lifecycle.

An example Hare.RPC.Client process that performs a RPC request and returns the response, but returns "already_requested" when a request with the same payload has already been performed.

defmodule MyRPCClient do
  use Hare.RPC.Client

    def start_link(conn, config, opts \ []) do
    Hare.RPC.Client.start_link(__MODULE__, conn, config, :ok, opts)
  end

  def init(:ok) do
    {:ok, MapSet.new}
  end

  def before_request(payload, _routing_key, _opts, state) do
    case MapSet.member?(cache, payload) do
      {:ok, response} -> {:reply, "already_requested", state}
      :error          -> {:ok, MapSet.put(state, payload)}
    end
  end
end

Channel handling

When the Hare.RPC.Client starts with start_link/5 it runs the init/1 callback and responds with {:ok, pid} on success, like a GenServer.

After starting the process it attempts to open a channel on the given connection. It monitors the channel, and in case of failure it tries to reopen again and again on the same connection.

Context setup

The context setup process for a RPC client is to declare the exchange to perform requests to, declare a exclusive server-named queue to receive responses, and consume that server-named queue.

Every time a channel is open the context is set up, meaning that the exchange and a new server-named queue are declared, and the queue is consumed through the new channel based on the given configuration.

The configuration must be a Keyword.t that contains a single key: :exchange whose value is the configuration for the Hare.Context.Action.DeclareExchange. Check it for more detailed information.

Summary

Functions

See Hare.Actor.call/2

See Hare.Actor.call/3

See Hare.Actor.cast/2

See Hare.Actor.reply/2

Performs a RPC request and blocks until the response arrives

Starts a Hare.RPC.Client process linked to the current process

Callbacks

Called before a request will be performed to the exchange

Called when the process receives a call message sent by call/3. This callback has the same arguments as the GenServer equivalent and the :reply, :noreply and :stop return tuples behave the same

Called when the process receives a cast message sent by cast/3. This callback has the same arguments as the GenServer equivalent and the :noreply and :stop return tuples behave the same

Called when the RPC client process has opened AMQP channel before registering itself as a consumer

Called when the AMQP server has been disconnected from the AMQP broker

Called when the process receives a message. This callback has the same arguments as the GenServer equivalent and the :noreply and :stop return tuples behave the same

Called when the AMQP server has registered the process as a consumer of the server-named queue and it will start to receive messages

Called when the RPC client process is first started. start_link/5 will block until it returns

Called when a response has been received, before it is delivered to the caller

Called when a message has been returned. It may happen when a request is sent with option mandatory: true and broker cannot deliver the message to a queue

Called when a request has timed out

This callback is the same as the GenServer equivalent and is called when the process terminates. The first argument is the reason the process is about to exit with

Types

config()
config() :: [exchange: Hare.Context.Action.DeclareExchange.config, context: module, timeout: timeout]
from()
from() :: GenServer.from
meta()
meta() :: map
opts()
opts() :: Hare.Adapter.opts
payload()
payload() :: Hare.Adapter.payload
request()
request() :: term
response()
response() :: term
routing_key()
routing_key() :: Hare.Adapter.routing_key
state()
state() :: term

Functions

call(server, message)

See Hare.Actor.call/2.

call(server, message, timeout)

See Hare.Actor.call/3.

cast(server, message)

See Hare.Actor.cast/2.

reply(from, message)

See Hare.Actor.reply/2.

request(client, payload, routing_key \\ "", opts \\ [], timeout \\ 5000)
request(GenServer.server, request, routing_key, opts, timeout) ::
  {:ok, response} |
  {:error, reason :: term}

Performs a RPC request and blocks until the response arrives.

A timeout bound to the same rules as the GenServer timeout may be specified (5 seconds by default)

start_link(mod, conn, config, initial, opts \\ [])
start_link(module, GenServer.server, config, initial :: term, GenServer.options) :: GenServer.on_start

Starts a Hare.RPC.Client process linked to the current process.

This function is used to start a Hare.RPC.Client process in a supervision tree. The process will be started by calling init with the given initial value.

Arguments:

  • mod - the module that defines the server callbacks (like GenServer)
  • conn - the pid of a Hare.Core.Conn process
  • config - the configuration of the publisher (describing the exchange to declare)
  • initial - the value that will be given to init/1
  • opts - the GenServer options

Callbacks

before_request(request, routing_key, opts, from, state)
before_request(request, routing_key, opts :: term, from, state) ::
  {:ok, state} |
  {:ok, payload, routing_key, opts :: term, state} |
  {:reply, response, state} |
  {:reply, response, state, timeout | :hibernate} |
  {:stop, reason :: term, response, state} |
  {:stop, reason :: term, state}

Called before a request will be performed to the exchange.

It receives as argument the message payload, the routing key, the options for that publication and the internal state.

Returning {:ok, state} will cause the request to be performed with no modification, block the client until the response is received, and enter the main loop with the given state.

Returning {:ok, payload, routing_key, opts, state} will cause the given payload, routing key and options to be used instead of the original ones, block the client until the response is received, and enter the main loop with the given state.

Returning {:reply, reply, state} will respond the client inmediately without performing the request with the given response, and enter the main loop again with the given state.

Returning {:reply, reply, state, timeout} is similar to {:reply, reply, state} except handle_info(:timeout, state) will be called after timeout milliseconds if no messages are received.

Returning {:stop, reason, response, state} will not send the message, respond to the caller with response, and terminate the main loop and call terminate(reason, state) before the process exits with reason reason.

Returning {:stop, reason, state} will not send the message, terminate the main loop and call terminate(reason, state) before the process exits with reason reason.

handle_call(request, arg1, state)
handle_call(request :: term, GenServer.from, state) ::
  {:reply, reply :: term, state} |
  {:reply, reply :: term, state, timeout | :hibernate} |
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, state} |
  {:stop, reason :: term, reply :: term, state}

Called when the process receives a call message sent by call/3. This callback has the same arguments as the GenServer equivalent and the :reply, :noreply and :stop return tuples behave the same.

See GenServer.handle_call/3.

handle_cast(request, state)
handle_cast(request :: term, state) ::
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, state}

Called when the process receives a cast message sent by cast/3. This callback has the same arguments as the GenServer equivalent and the :noreply and :stop return tuples behave the same.

See GenServer.handle_cast/2.

handle_connected(state)
handle_connected(state) ::
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, state}

Called when the RPC client process has opened AMQP channel before registering itself as a consumer.

Returning {:noreply, state} will cause the process to enter the main loop with the given state.

Returning {:stop, reason, state} will terminate the main loop and call terminate(reason, state) before the process exits with reason reason.

handle_disconnected(reason, state)
handle_disconnected(reason :: term, state) ::
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, state}

Called when the AMQP server has been disconnected from the AMQP broker.

Returning {:noreply, state} will cause the process to enter the main loop with the given state. The server will not consume any new messages until connection to AMQP broker is restored.

Returning {:stop, reason, state} will terminate the main loop and call terminate(reason, state) before the process exits with reason reason.

handle_info(meta, state)
handle_info(meta, state) ::
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, state}

Called when the process receives a message. This callback has the same arguments as the GenServer equivalent and the :noreply and :stop return tuples behave the same.

See GenServer.handle_info/2.

handle_ready(meta, state)
handle_ready(meta, state) ::
  {:noreply, state} |
  {:stop, reason :: term, state}

Called when the AMQP server has registered the process as a consumer of the server-named queue and it will start to receive messages.

Returning {:noreply, state} will cause the process to enter the main loop with the given state.

Returning {:stop, reason, state} will not send the message, terminate the main loop and call terminate(reason, state) before the process exits with reason reason.

init(initial)
init(initial :: term) ::
  {:ok, state} |
  :ignore |
  {:stop, reason :: term}

Called when the RPC client process is first started. start_link/5 will block until it returns.

It receives as argument the fourth argument given to start_link/5.

Returning {:ok, state} will cause start_link/5 to return {:ok, pid} and attempt to open a channel on the given connection, declare the exchange, declare a server-named queue, and consume it. After that it will enter the main loop with state as its internal state.

Returning :ignore will cause start_link/5 to return :ignore and the process will exit normally without entering the loop, opening a channel or calling terminate/2.

Returning {:stop, reason} will cause start_link/5 to return {:error, reason} and the process will exit with reason reason without entering the loop, opening a channel, or calling terminate/2.

on_response(response, from, state)
on_response(response, from, state) ::
  {:reply, response, state} |
  {:reply, response, state, timeout | :hibernate} |
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, response, state} |
  {:stop, reason :: term, state}

Called when a response has been received, before it is delivered to the caller.

It receives as argument the message payload, the routing key, the options for that publication, the response, and the internal state.

Returning {:reply, reply, state} will cause the given reply to be delivered to the caller instead of the original response, and enter the main loop with the given state.

Returning {:reply, reply, state, timeout} is similar to {:reply, reply, state} except handle_info(:timeout, state) will be called after timeout milliseconds if no messages are received.

Returning {:noreply, state} will enter the main loop with the given state without responding to the caller (that will eventually timeout or keep blocked forever if the timeout was set to :infinity).

Returning {:noreply, state, timeout} is similar to {:noreply, state} except handle_info(:timeout, state) will be called after timeout milliseconds if no messages are received.

Returning {:stop, reason, reply, state} will deliver the given reply to the caller instead of the original response and call terminate(reason, state) before the process exits with reason reason.

Returning {:stop, reason, state} not reply to the caller and call terminate(reason, state) before the process exits with reason reason.

on_return(payload, state)
on_return(payload, state) ::
  {:reply, response, state} |
  {:reply, response, state, timeout | :hibernate} |
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, response, state} |
  {:stop, reason :: term, state}

Called when a message has been returned. It may happen when a request is sent with option mandatory: true and broker cannot deliver the message to a queue.

It receives as argument the caller reference and the internal state.

Returning {:reply, reply, state} will cause the given reply to be delivered to the caller instead of the original response, and enter the main loop with the given state.

Returning {:noreply, state} will enter the main loop with the given state without responding to the caller (that will eventually timeout or keep blocked forever if the timeout was set to :infinity).

Returning {:stop, reason, reply, state} will deliver the given reply to the caller instead of the original response and call terminate(reason, state) before the process exits with reason reason.

Returning {:stop, reason, state} not reply to the caller and call terminate(reason, state) before the process exits with reason reason.

on_timeout(from, state)
on_timeout(from, state) ::
  {:reply, response, state} |
  {:reply, response, state, timeout | :hibernate} |
  {:noreply, state} |
  {:noreply, state, timeout | :hibernate} |
  {:stop, reason :: term, response, state} |
  {:stop, reason :: term, state}

Called when a request has timed out.

It receives as argument the message payload, the routing key, the options for that publication, and the internal state.

Returning {:reply, reply, state} will cause the given reply to be delivered to the caller, and enter the main loop with the given state.

Returning {:noreply, state} will enter the main loop with the given state without responding to the caller (that will eventually timeout or keep blocked forever if the timeout was set to :infinity).

Returning {:stop, reason, reply, state} will deliver the given reply to the caller, and call terminate(reason, state) before the process exits with reason reason.

Returning {:stop, reason, state} will not reply to the caller and call terminate(reason, state) before the process exits with reason reason.

terminate(reason, state)
terminate(reason :: term, state) :: any

This callback is the same as the GenServer equivalent and is called when the process terminates. The first argument is the reason the process is about to exit with.