RabbitMQ Reverse Topic Exchange
Switch branches/tags
v1.0.2 v1.0.1 v1.0.0 rabbitmq_v3_7_0_milestone18 rabbitmq_v3_7_0_milestone17 rabbitmq_v3_7_0_milestone16 rabbitmq_v3_7_0_milestone15 rabbitmq_v3_7_0_milestone14 rabbitmq_v3_7_0_milestone13 rabbitmq_v3_7_0_milestone12 rabbitmq_v3_7_0_milestone11 rabbitmq_v3_7_0_milestone10 rabbitmq_v3_7_0_milestone9 rabbitmq_v3_7_0_milestone8 rabbitmq_v3_7_0_milestone7 rabbitmq_v3_7_0_milestone6 rabbitmq_v3_7_0_milestone5 rabbitmq_v3_7_0_milestone4 rabbitmq_v3_7_0_milestone3 rabbitmq_v3_7_0_milestone2 rabbitmq_v3_7_0_milestone1 rabbitmq_v3_6_14 rabbitmq_v3_6_13 rabbitmq_v3_6_13_rc2 rabbitmq_v3_6_13_rc1 rabbitmq_v3_6_13_milestone1 rabbitmq_v3_6_12 rabbitmq_v3_6_12_rc3 rabbitmq_v3_6_12_rc2 rabbitmq_v3_6_12_rc1 rabbitmq_v3_6_11 rabbitmq_v3_6_11_rc3 rabbitmq_v3_6_11_rc2 rabbitmq_v3_6_11_rc1 rabbitmq_v3_6_11_milestone5 rabbitmq_v3_6_11_milestone4 rabbitmq_v3_6_11_milestone3 rabbitmq_v3_6_11_milestone2 rabbitmq_v3_6_11_milestone1 rabbitmq_v3_6_10 rabbitmq_v3_6_10_rc2 rabbitmq_v3_6_10_rc1 rabbitmq_v3_6_10_milestone4 rabbitmq_v3_6_10_milestone3 rabbitmq_v3_6_10_milestone2 rabbitmq_v3_6_10_milestone1 rabbitmq_v3_6_9 rabbitmq_v3_6_8 rabbitmq_v3_6_7 rabbitmq_v3_6_7_rc3 rabbitmq_v3_6_7_rc2 rabbitmq_v3_6_7_rc1 rabbitmq_v3_6_7_milestone6 rabbitmq_v3_6_7_milestone5 rabbitmq_v3_6_7_milestone4 rabbitmq_v3_6_7_milestone3 rabbitmq_v3_6_7_milestone2 rabbitmq_v3_6_7_milestone1 rabbitmq_v3_6_6 rabbitmq_v3_6_6_rc2 rabbitmq_v3_6_6_rc1 rabbitmq_v3_6_6_milestone5 rabbitmq_v3_6_6_milestone4 rabbitmq_v3_6_6_milestone3 rabbitmq_v3_6_6_milestone2 rabbitmq_v3_6_6_milestone1 rabbitmq_v3_6_5 rabbitmq_v3_6_5_milestone2 rabbitmq_v3_6_5_milestone1 rabbitmq_v3_6_4 rabbitmq_v3_6_4_rc1 rabbitmq_v3_6_4_milestone2 rabbitmq_v3_6_4_milestone1 rabbitmq_v3_6_3 rabbitmq_v3_6_3_rc3 rabbitmq_v3_6_3_rc2 rabbitmq_v3_6_3_rc1 rabbitmq_v3_6_3_milestone2 rabbitmq_v3_6_3_milestone1 rabbitmq_v3_6_2 rabbitmq_v3_6_2_rc4 rabbitmq_v3_6_2_rc3 rabbitmq_v3_6_2_rc2 rabbitmq_v3_6_2_rc1 rabbitmq_v3_6_2_milestone5 rabbitmq_v3_6_2_milestone4 rabbitmq_v3_6_2_milestone3 rabbitmq_v3_6_2_milestone2 rabbitmq_v3_6_2_milestone1 rabbitmq_v3_6_1 rabbitmq_v3_6_1_rc2 rabbitmq_v3_6_1_rc1 rabbitmq_v3_6_0
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
test
.gitignore
.travis.yml
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE
LICENSE-MPL-RabbitMQ
Makefile
README.md
erlang.mk
rabbitmq-components.mk

README.md

RabbitMQ Reverse Topic Exchange Type

This plugin adds a reverse topic exchange type to RabbitMQ. The exchange type is x-rtopic.

The idea is to be able to specify routing patterns when publishing messages. With the default topic exchange patterns are only accepted when binding queues to exchanges.

With this plugin you can decide which queues receive the message at publishing time. With the default topic exchange the decision is made during queue binding.

With this exchange your routing keys will be words separated by dots, and the binding keys will be words separated by dots as well, with the difference that on the routing keys you can provide special characters like the # or the *. The hash # will match zero or more words. The star * will match one word.

Usage

If we have the following setup, (we assume the exchange is of type rtopic):

  • Queue A bound to exchange rtopic with routing key "server1.app1.mod1.info".
  • Queue B bound to exchange rtopic with routing key "server1.app1.mod1.error".
  • Queue C bound to exchange rtopic with routing key "server1.app2.mod1.info".
  • Queue D bound to exchange rtopic with routing key "server2.app2.mod1.warning".
  • Queue E bound to exchange rtopic with routing key "server1.app1.mod2.info".
  • Queue F bound to exchange rtopic with routing key "server2.app1.mod1.info".

Then we execute the following message publish actions.

%% Parameter order is: message, exchange name and routing key.

basic_publish(Msg, "rtopic", "server1.app1.mod1.info").
%% message is only received by queue A.

basic_publish(Msg, "rtopic", "*.app1.mod1.info").
%% message is received by queue A and F.

basic_publish(Msg, "rtopic", "#.info").
%% message is received by queue A, C, E and F.

basic_publish(Msg, "rtopic", "#.mod1.info").
%% message is received by queue A, C, and F.

basic_publish(Msg, "rtopic", "#").
%% message is received by every queue bound to the exchange.

basic_publish(Msg, "rtopic", "server1.app1.mod1.*").
%% message is received by queues A and B.

basic_publish(Msg, "rtopic", "server1.app1.#").
%% message is received by queues A, B and E.

The exchange type used when declaring an exchange is x-rtopic.

Installation and Binary Builds

This plugin is now available from the RabbitMQ community plugins page. Please consult the docs on how to install RabbitMQ plugins.

Then enable the plugin:

rabbitmq-plugins enable rabbitmq_rtopic_exchange

Building from Source

See Plugin Development guide.

TL;DR: running

make dist

will build the plugin and put build artifacts under the ./plugins directory.

Examples and Tests

To run the tests use make tests.

The test suite can also be used for code examples.

Performance

Internally the plugin uses a trie like data structure, so the following has to be taken into account when binding either queues or exchanges to it.

The following applies if you have thousands of queues. After some benchmarks I could see that performance degraded for +1000 bindings. So if you have say, 100 bindings to this exchange, then performance should be acceptable in most cases. In any case, running your own benchmarks wont hurt. The file rabbit_rtopic_perf.erl has some precarious tools to run benchmarks that I ought to document at some point.

A trie performs better when doing prefix searches than suffix searches. For example we have the following bindings:

a0.b0.c0.d0
a0.b0.c1.d0
a0.b1.c0.d0
a0.b1.c1.d0
a0.b0.c2.d1
a0.b0.c2.d0
a0.b0.c2.d1
a0.b0.c3.d0
a1.b0.c0.d0
a1.b1.c0.d0

If we publish a message with the following routing key: "a0.#", it's the same as asking "find me all the routing keys that start with "a0". After the algorithm descended on level in the trie, then it needs to visit every node in the trie. So the longer the prefix, the faster the routing will behave. That is, queries of the kind "find all string with prefix", will go faster, the longer the prefix is.

On the other hand if we publish a message with the routing key "#.d0", it's the same as asking "find me all the bindings with suffix "d0". That would be terribly slow to do with a trie, but there's a trick. If you need to use this exchange for this kind of routing, then you can build your bindings in reverse, therefore you could do a "all prefixes" query instead of a "all suffixes" query.

If you have the needs for routing "a0.#.c0.d0.#.f0.#" then again, with a small amount of binding keys it should be a problem, but keep in mind that the longer the gaps represented by the # character, the slower the algorithm will run. AFAIK there's no easy solution for this problem.

License

See LICENSE.

Copyright

(c) Pivotal Software Inc., 2007-2016.

Originally developed by Alvaro Videla.