Skip to main content

Plugin Development Basics

This guide covers the basics of RabbitMQ plugin development. It is expected that before reading this guide, the reader has a basic understanding of the RabbitMQ plugin mechanism.

Readers are also expected to have a basic understanding of Erlang and the OTP design principles.

Learn You Some Erlang is a great way to get started with Erlang and OTP.

Why Develop a Plugin?

Writing a RabbitMQ plugin provides a number of appealing possibilities:

  • Enable your application to access internal RabbitMQ functionality that is not exposed via one of the supported protocols.
  • Running in the same Erlang VM as the broker may increase performance for certain workloads.
  • Plugins can implement features that otherwise would have to be implemented by every application (service) in the system, creating duplication and increasing maintenance load

Why To Not Develop a Plugin

As with any plugin mechanism, consideration should be given when developing functionality as to whether embedding it as a plugin is the most appropriate path to take. Some reasons that you might not want to develop your functionality as a plugin:

  • Depending on internal RabbitMQ APIs can result in your application requiring changes when new RabbitMQ come out, including patch releases. If you can do what you need to do without using RabbitMQ internals, then your application will be far more forward-compatible
  • A poorly written plugin can result in the entire node becoming unavailable or misbehaving

Getting Started

To develop a RabbitMQ plugin, first make sure the following requirements are met:

  • Ensure that you have a working installation of Git
  • Ensure that the dependencies detailed in the Server Build guides are installed and functional

Erlang.mk is used to build RabbitMQ and its plugins. The easiest way to start on a new plugin is probably to copy an existing plugin such as rabbitmq-metronome, used as an example below.

Activating Plugins During Development

To test the plugin during development, use the following make target to start a RabbitMQ node with the local plugin built from source and enabled:

make run-broker

Plugin Quality Tips

A badly-written plugins can pose a risk to the stability of the broker. To ensure that your plugin can safely operate without affecting RabbitMQ core, a couple of safety best practices are highly recommended.

  1. Always use a top-level supervisor for your application.
  2. Never start the plugin application directly, instead opting to create a (possibly quite trivial) supervisor that will prevent the Erlang VM from shutting down due to a crashed top-level application.

Broker and Dependency Version Constraints

It's possible to specify broker and dependency version requirements for a plugin using the broker_version_requirements key in plugin's application environment. The requirements are specified as a list of minimum version in each release series. Consider the following example:

{application, my_plugin,[
%% ...
{broker_version_requirements, ["3.11.15", "3.10.22"]}
]}

The above requires RabbitMQ 3.10.x starting with 3.10.22 and 3.11.x starting with 3.11.15. Note that when new major and minor (feature) RabbitMQ versions come out, it is necessary for plugin maintainers to update the list.

Plugins can have dependencies. It is possible to specify supported version series for dependencies, too. This is quite similar to the above but uses a dictionary-like data structure (proplist).

For example:

{application, my_plugin, [
%% ...
{dependency_version_requirements, [{rabbitmq_management, ["3.11.0", "3.10.22"]}]}
]}

means the plugin depends on rabbitmq_management 3.10.x starting with 3.10.22 and all versions in the 3.11.x series.

Example Plugin: Metronome

Seeing as no development guide would be complete without a Hello World example, the following tries to provide the basics of how your would build your very own RabbitMQ plugin.

The following example details how you might build a simple plugin that acts like a metronome.

Every second, it fires a message that has a routing key in the form yyyy.MM.dd.dow.hh.mm.ss to a topic exchange called "metronome" by default. Applications can attach queues to this exchange with various routing keys in order to be invoked at regular intervals. For example, to receive a message every second, a binding of "......" could be applied. To receive a message every minute, a binding of ".....*.00" could be applied instead.

The rabbitmq-metronome repository on GitHub contains a copy of the code for this plugin.

The following table should explain the purpose of the various files in the repository.

FilenamePurpose
Makefile

This top-level Makefile defines the name of your plugin and its dependencies. The name must match the Erlang application name. Dependencies are declared using erlang.mk's variables. Just after that, the Makefile includes rabbitmq-components.mk and erlang.mk, as well as rabbitmq-plugins.mk using erlang.mk plugins facility. See below for a description of those files.

erlang.mk

A local copy of erlang.mk. This is not a vanilla copy because RabbitMQ relies on a few modifications which have not been merged upstream at the time of this writing. That's why ERLANG_MK_REPO and ERLANG_MK_COMMIT are overridden for now.

rabbitmq-components.mk

A local copy of rabbitmq-components.mk. The original file is in rabbitmq-common which your plugin will depend on automatically. It contains other erlang.mk extensions and helpers which must be defined before erlang.mk inclusion. This file must be kept up-to-date w.r.t. rabbitmq-common: when it is out-of-date, you will get the following error:

error: rabbitmq-components.mk must be updated!

In this case, just run the following command to update your copy:

make rabbitmq-components-mk
rabbitmq_metronome.schema

A Cuttlefish configuration schema. Used to translate configuration file to the internal format used by RabbitMQ and its runtime.

Metronome schema contains mappings for the metronome.exchange setting, setting the exchange used by the plugin.

Configuration will be regenerated when the plugin is enabled. Plugin-specific values in the config will cause error if plugin has not been enabled.

More information about writing schema files can be found in the Cuttlefish docs.

src/rabbit_metronome.erl

Implementation of the Erlang "application" behaviour. Provides a means for the Erlang VM to start and stop the plugin.

src/rabbit_metronome_sup.erl

Implementation of the Erlang "supervisor" behaviour. Monitors the worker process and restarts it if it crashes.

src/rabbit_metronome_worker.erl

The core of the plugin. The worker will connect internally to the broker, then create a task that will be triggered every second.

test/metronome_SUITE.erl

Automated tests for the plugin.

Development Process

Run make to build the plugin:

make

To start a node with the plugin built and enabled on:

make run-broker

To ensure that the new plugin is up and running, run the following command:

rabbitmq-diagnostics status

If your plugin has loaded successfully, you should see it in the enabled plugin list:

# => Plugins
# =>
# => Enabled plugin file: /var/folders/gp/53t98z011678vk9rkcb_s6ph0000gn/T/rabbitmq-test-instances/rabbit@warp10/enabled_plugins
# => Enabled plugins:
# =>
# => * rabbitmq_metronome
# => * amqp_client

To run Common Test test suites, use

make tests

Finally, you can produce an .ez file, suitable for distribution with:

DIST_AS_EZS=yes make dist

The file appears in the plugins directory under repository root.