Copy messages to different RabbitMQ node using federation plugin.
Preliminary information
I will copy messages between reindeer
and raccoon
RabbitMQ nodes.
Install RabbitMQ server on every node
Install RabbitMQ message broker on every node.
$ sudo apt update $ sudo apt install gnupg2 apt-transport-https curl $ curl https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc | sudo apt-key add - $ echo "deb https://dl.bintray.com/rabbitmq/debian stretch main" | sudo tee /etc/apt/sources.list.d/rabbitmq.list $ echo "deb https://dl.bintray.com/rabbitmq-erlang/debian buster erlang-22.x" | sudo tee -a /etc/apt/sources.list.d/rabbitmq.list $ apt update $ apt install rabbitmq-server
Use rabbitmq-diagnostics
to display RabbitMQ version.
$ sudo rabbitmq-diagnostics server_version Asking node rabbit@buster for its RabbitMQ version... 3.8.0
Enable management plugin.
$ sudo rabbitmq-plugins enable rabbitmq_management
Define admin
user with password
password.
.
$ sudo rabbitmqctl add_user admin password $ sudo rabbitmqctl set_user_tags admin administrator $ sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
Delete guest user.
$ sudo rabbitmqctl delete_user guest
Get rabbitmqadmin
utility.
$ curl -o rabbitmqadmin http://127.0.0.1:15672/cli/rabbitmqadmin
Ensure that executable bit is set.
$ chmod +x rabbitmqadmin
Setup the dedicated vhost and user
Setup dedicated vhost and user on both nodes.
Create federation_example
virtualhost and ensure that admin can manage it.
$ sudo rabbitmqctl add_vhost federation_example
$ sudo rabbitmqctl set_permissions -p federation_example admin ".*" ".*" ".*"
Declare federation_example
user with password
password, grant permissions to federation_example
virtual host.
$ sudo rabbitmqctl add_user federation_example password
$ sudo rabbitmqctl set_permissions federation_example --vhost federation_example ".*" ".*" ".*"
federation_example
is not a management user.Setup exchanges
Setup exchanges, queues and bindings on both nodes.
Declare exchange.
$ ./rabbitmqadmin --username admin --password password --vhost federation_example declare exchange name=directives type=fanout
Declare queue.
$ ./rabbitmqadmin --username admin --password password --vhost federation_example declare queue name=application-directives
Declare bindings.
$ ./rabbitmqadmin --username admin --password password --vhost federation_example declare binding source=directives destination=application-directives
Setup federation plugin on the first RabbitMQ node
Setup federation plugin on the first RabbitMQ node – reindeer
.
Enable federation plugin.
$ sudo rabbitmq-plugins enable rabbitmq_federation
Define upstream RabbitMQ server.
$ sudo rabbitmqctl set_parameter federation-upstream raccoon-upstream --vhost federation_example '{"uri":"amqp://federation_example:password@raccoon"}'
Define upstream set.
$ sudo rabbitmqctl set_parameter --vhost federation_example federation-upstream-set raccoon '[{"upstream":"raccoon-upstream"}]'
Define federation policy.
$ sudo rabbitmqctl set_policy federate-directives --vhost federation_example directives '{"federation-upstream-set":"raccoon"}' --priority 1 --apply-to exchanges
Display defined parameters.
$ sudo rabbitmqctl list_parameters -p federation_example Listing runtime parameters for vhost "federation_example" ... component name value federation-upstream raccoon-upstream {"uri":"amqp://federation_example:password@raccoon"} federation-upstream-set raccoon [{"upstream":"raccoon-upstream"}]
Check federation status.
$ sudo rabbitmqctl federation_status Listing federation links on node rabbit@reindeer... [#{error => <<>>,exchange => <<"directives">>,id => <<"2d543d9d">>, last_changed => <<"2019-10-22 21:41:08">>, local_connection => <<"<rabbit@reindeer.3.2854.0>">>,queue => <<>>, status => running,type => exchange,upstream => <<"raccoon-upstream">>, upstream_exchange => <<"directives">>,upstream_queue => <<>>, uri => <<"amqp://raccoon">>,vhost => <<"federation_example">>}]
Setup federation plugin on the second RabbitMQ node
Setup federation plugin on the second RabbitMQ node – raccoon
.
Enable federation plugin.
$ sudo rabbitmq-plugins enable rabbitmq_federation
Define upstream RabbitMQ server.
$ sudo rabbitmqctl set_parameter federation-upstream reindeer-upstream --vhost federation_example '{"uri":"amqp://federation_example:password@reindeer"}'
Define upstream set.
$ sudo rabbitmqctl set_parameter --vhost federation_example federation-upstream-set reindeer '[{"upstream":"reindeer-upstream"}]'
Define federation policy.
$ sudo rabbitmqctl set_policy federate-directives --vhost federation_example directives '{"federation-upstream-set":"reindeer"}' --priority 1 --apply-to exchanges
Display defined parameters.
$ sudo rabbitmqctl list_parameters -p federation_example Listing runtime parameters for vhost "federation_example" ... component name value federation-upstream reindeer-upstream {"uri":"amqp://federation_example:password@reindeer"} federation-upstream-set reindeer [{"upstream":"reindeer-upstream"}]
Check federation status.
$ sudo rabbitmqctl federation_status Listing federation links on node rabbit@raccoon... [#{error => <<>>,exchange => <<"directives">>,id => <<"58c6f033">>, last_changed => <<"2019-10-22 21:39:46">>, local_connection => <<"<rabbit@raccoon.3.2882.0>">>,queue => <<>>, status => running,type => exchange,upstream => <<"reindeer-upstream">>, upstream_exchange => <<"directives">>,upstream_queue => <<>>, uri => <<"amqp://reindeer">>,vhost => <<"federation_example">>}]
Publish messages and inspect queues
Publish message on the first RabbitMQ node – reindeer
.
$ ./rabbitmqadmin --username admin --password password --host reindeer --vhost federation_example publish exchange=directives routing_key= payload="sample application directive"
Inspect queues on the first RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host reindeer --vhost federation_example list queues vhost name node messages +--------------------+------------------------------------------+-----------------+----------+ | vhost | name | node | messages | +--------------------+------------------------------------------+-----------------+----------+ | federation_example | application-directives | rabbit@reindeer | 1 | | federation_example | federation: directives -> rabbit@raccoon | rabbit@reindeer | 0 | +--------------------+------------------------------------------+-----------------+----------+
Inspect queues on the second RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host raccoon --vhost federation_example list queues vhost name node messages +--------------------+-------------------------------------------+----------------+----------+ | vhost | name | node | messages | +--------------------+-------------------------------------------+----------------+----------+ | federation_example | application-directives | rabbit@raccoon | 1 | | federation_example | federation: directives -> rabbit@reindeer | rabbit@raccoon | 0 | +--------------------+-------------------------------------------+----------------+----------+
Publish messages on the second RabbitMQ node – raccoon
.
$ ./rabbitmqadmin --username admin --password password --host raccoon --vhost federation_example publish exchange=directives routing_key= payload="sample application directive"
Inspect queues on the first RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host reindeer --vhost federation_example list queues vhost name node messages +--------------------+------------------------------------------+-----------------+----------+ | vhost | name | node | messages | +--------------------+------------------------------------------+-----------------+----------+ | federation_example | application-directives | rabbit@reindeer | 2 | | federation_example | federation: directives -> rabbit@raccoon | rabbit@reindeer | 0 | +--------------------+------------------------------------------+-----------------+----------+
Inspect queues on the second RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host raccoon --vhost federation_example list queues vhost name node messages +--------------------+-------------------------------------------+----------------+----------+ | vhost | name | node | messages | +--------------------+-------------------------------------------+----------------+----------+ | federation_example | application-directives | rabbit@raccoon | 2 | | federation_example | federation: directives -> rabbit@reindeer | rabbit@raccoon | 0 | +--------------------+-------------------------------------------+----------------+----------+
Consume a message.
$ ./rabbitmqadmin --username admin --password password --host reindeer --vhost federation_example get queue=application-directives ackmode=ack_requeue_false +-------------+------------+---------------+------------------------------+---------------+------------------+------------+-------------+ | routing_key | exchange | message_count | payload | payload_bytes | payload_encoding | properties | redelivered | +-------------+------------+---------------+------------------------------+---------------+------------------+------------+-------------+ | | directives | 1 | sample application directive | 28 | string | | True | +-------------+------------+---------------+------------------------------+---------------+------------------+------------+-------------+
Inspect queues on the first RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host reindeer --vhost federation_example list queues vhost name node messages +--------------------+------------------------------------------+-----------------+----------+ | vhost | name | node | messages | +--------------------+------------------------------------------+-----------------+----------+ | federation_example | application-directives | rabbit@reindeer | 1 | | federation_example | federation: directives -> rabbit@raccoon | rabbit@reindeer | 0 | +--------------------+------------------------------------------+-----------------+----------+
Inspect queues on the second RabbitMQ node.
$ ./rabbitmqadmin --username admin --password password --host raccoon --vhost federation_example list queues vhost name node messages +--------------------+-------------------------------------------+----------------+----------+ | vhost | name | node | messages | +--------------------+-------------------------------------------+----------------+----------+ | federation_example | application-directives | rabbit@raccoon | 2 | | federation_example | federation: directives -> rabbit@reindeer | rabbit@raccoon | 0 | +--------------------+-------------------------------------------+----------------+----------+
Configuration
$ sudo apt install jq
Export upstream configuration.
$ ./rabbitmqadmin --username admin --password password --host reindeer export config-reindeer.json
$ cat config-reindeer.json | jq . { "rabbit_version": "3.8.0", "users": [ { "name": "admin", "password_hash": "e/fHqe5PFXC/m68fcBa8W612ko9Jg5Tlo6L6cjiN/jGqXbXA", "hashing_algorithm": "rabbit_password_hashing_sha256", "tags": "administrator" }, { "name": "federation_example", "password_hash": "p+bEErHIP5+hdD4Vw66GK+8EsR1VkVCg4hdNfm4Sj03k55pa", "hashing_algorithm": "rabbit_password_hashing_sha256", "tags": "" } ], "vhosts": [ { "name": "/" }, { "name": "federation_example" } ], "permissions": [ { "user": "federation_example", "vhost": "federation_example", "configure": ".*", "write": ".*", "read": ".*" }, { "user": "admin", "vhost": "/", "configure": ".*", "write": ".*", "read": ".*" }, { "user": "admin", "vhost": "federation_example", "configure": ".*", "write": ".*", "read": ".*" } ], "topic_permissions": [], "parameters": [ { "value": { "uri": "amqp://federation_example:password@raccoon" }, "vhost": "federation_example", "component": "federation-upstream", "name": "raccoon-upstream" }, { "value": [ { "upstream": "raccoon-upstream" } ], "vhost": "federation_example", "component": "federation-upstream-set", "name": "raccoon" } ], "global_parameters": [ { "name": "cluster_name", "value": "rabbit@reindeer" } ], "policies": [ { "vhost": "federation_example", "name": "federate-directives", "pattern": "directives", "apply-to": "exchanges", "definition": { "federation-upstream-set": "raccoon" }, "priority": 1 } ], "queues": [ { "name": "application-directives", "vhost": "federation_example", "durable": true, "auto_delete": false, "arguments": {} } ], "exchanges": [ { "name": "directives", "vhost": "federation_example", "type": "fanout", "durable": true, "auto_delete": false, "internal": false, "arguments": {} } ], "bindings": [ { "source": "directives", "vhost": "federation_example", "destination": "application-directives", "destination_type": "queue", "routing_key": "", "arguments": {} } ] }
Clustering
Define multiple upstream addresses when you connect to a cluster.
$ sudo rabbitmqctl set_parameter federation-upstream cluster-upstream --vhost federation_example '{"uri":["amqp://federation_example:password@fox","amqp://federation_example:password@falcon","amqp://federation_example:password@ferret"]}' Setting runtime parameter "cluster-upstream" for component "federation-upstream" to "{"uri":["amqp://federation_example:password@fox","amqp://federation_example:password@falcon","amqp://federation_example:password@ferret"]}" in vhost "federation_example" ...
Remember to also mirror ^federation
queues, so the downstream can safely reconnect to other cluster nodes.
$ sudo rabbitmqctl set_policy -p federation_example mirror-directives "^application-directives$" '{"ha-mode": "all", "ha-sync-mode":"automatic"}' Setting policy "mirror-directives" for pattern "^application-directives" to "{"ha-mode": "all", "ha-sync-mode":"automatic"}" with priority "0" for vhost "federation_example" ...
$ sudo rabbitmqctl set_policy -p federation_example mirror-federation "^federation" '{"ha-mode": "all", "ha-sync-mode":"automatic"}' Setting policy "mirror-federation" for pattern "^federation" to "{"ha-mode": "all", "ha-sync-mode":"automatic"}" with priority "0" for vhost "federation_example" ...
You will get the following error when you forget to mirror dynamically created queues used for federation purposes.
Error detail: {server_initiated_close,404, <<"NOT_FOUND - home node 'rabbit@falcon' of durable queue 'federation: directives -> rabbit@bear' in vhost 'federation_example' is down or inaccessible">>}
You can restart federation link in case of trouble.
$ sudo rabbitmqctl eval 'rabbit_federation_status:status().' [[{exchange,<<"directives">>}, {upstream_exchange,<<"directives">>}, {type,exchange}, {vhost,<<"federation_example">>}, {upstream,<<"reindeer-upstream">>}, {id,<<"58c6f033">>}, {status,running}, {local_connection,<<"<rabbit@bear.1.9902.0>">>}, {uri,<<"amqp://fox">>}, {timestamp,{{2019,10,23},{22,8,39}}}]]
$ sudo rabbitmqctl restart_federation_link 58c6f033 Restarting federation link 58c6f033 on node rabbit@bear
$ sudo rabbitmqctl eval 'rabbit_federation_status:status().' [[{exchange,<<"directives">>}, {upstream_exchange,<<"directives">>}, {type,exchange}, {vhost,<<"federation_example">>}, {upstream,<<"reindeer-upstream">>}, {id,<<"58c6f033">>}, {status,running}, {local_connection,<<"<rabbit@bear.1.10002.0>">>}, {uri,<<"amqp://falcon">>}, {timestamp,{{2019,10,23},{22,8,56}}}]]
Additional notes
Enable rabbitmq_federation_management
to define parameters and inspect plugin status using web-gui.
$ sudo rabbitmq-plugins enable rabbitmq_federation_management Enabling plugins on node rabbit@reindeer: rabbitmq_federation_management The following plugins have been configured: rabbitmq_federation rabbitmq_federation_management rabbitmq_management rabbitmq_management_agent rabbitmq_web_dispatch Applying plugin configuration to rabbit@reindeer... The following plugins have been enabled: rabbitmq_federation_management started 1 plugins.