Topics

(using the .NET client)

Prerequisites

This tutorial assumes RabbitMQ isinstalled and running on localhoston standard port (5672). In case you use a different host, port or credentials, connections settings would require adjusting.

Where to get help

If you're having trouble going through this tutorial you can contact usthrough the mailing list.

In the previous tutorial we improved our logging system. Instead of using a fanout exchange only capable of dummy broadcasting, we used a directone, and gained a possibility of selectively receiving the logs.

Although using the direct exchange improved our system, it still has limitations - it can't do routing based on multiple criteria.

In our logging system we might want to subscribe to not only logs based on severity, but also based on the source which emitted the log. You might know this concept from the syslog unix tool, which routes logs based on both severity (info/warn/crit...) and facility (auth/cron/kern...).

That would give us a lot of flexibility - we may want to listen to just critical errors coming from 'cron' but also all logs from 'kern'.

To implement that in our logging system we need to learn about a more complex topicexchange.

Topic exchange

Messages sent to a topic exchange can't have an arbitrary routing_key - it must be a list of words, delimited by dots. The words can be anything, but usually they specify some features connected to the message. A few valid routing key examples: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit". There can be as many words in the routing key as you like, up to the limit of 255 bytes.

The binding key must also be in the same form. The logic behind the topic exchange is similar to a direct one - a message sent with a particular routing key will be delivered to all the queues that are bound with a matching binding key. However there are two important special cases for binding keys:

  • * (star) can substitute for exactly one word.
  • # (hash) can substitute for zero or more words.

It's easiest to explain this in an example:

In this example, we're going to send messages which all describe animals. The messages will be sent with a routing key that consists of three words (two dots). The first word in the routing key will describe speed, second a colour and third a species: "<speed>.<colour>.<species>".

We created three bindings: Q1 is bound with binding key "*.orange.*" and Q2 with "*.*.rabbit" and "lazy.#".

These bindings can be summarised as:

  • Q1 is interested in all the orange animals.
  • Q2 wants to hear everything about rabbits, and everything about lazy animals.

A message with a routing key set to "quick.orange.rabbit" will be delivered to both queues. Message "lazy.orange.elephant" also will go to both of them. On the other hand "quick.orange.fox" will only go to the first queue, and "lazy.brown.fox" only to the second. "lazy.pink.rabbit" will be delivered to the second queue only once, even though it matches two bindings. "quick.brown.fox" doesn't match any binding so it will be discarded.

What happens if we break our contract and send a message with one or four words, like "orange" or "quick.orange.male.rabbit"? Well, these messages won't match any bindings and will be lost.

On the other hand "lazy.orange.male.rabbit", even though it has four words, will match the last binding and will be delivered to the second queue.

Topic exchange

Topic exchange is powerful and can behave like other exchanges.

When a queue is bound with "#" (hash) binding key - it will receive all the messages, regardless of the routing key - like in fanout exchange.

When special characters "*" (star) and "#" (hash) aren't used in bindings, the topic exchange will behave just like a direct one.

Putting it all together

We're going to use a topic exchange in our logging system. We'll start off with a working assumption that the routing keys of logs will have two words: "<facility>.<severity>".

The code is almost the same as in the previous tutorial.

The code for EmitLogTopic.cs:

class EmitLogTopic
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("topic_logs", "topic"); var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
var message = (args.Length > 1)
? string.Join(" ", args.Skip(1).ToArray())
: "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish("topic_logs", routingKey, null, body);
Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
}
}
}
}

The code for ReceiveLogsTopic.cs:

class ReceiveLogsTopic
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare("topic_logs", "topic");
var queueName = channel.QueueDeclare(); if (args.Length < 1)
{
Console.Error.WriteLine("Usage: {0} [binding_key...]",
Environment.GetCommandLineArgs()[0]);
Environment.ExitCode = 1;
return;
} foreach (var bindingKey in args)
{
channel.QueueBind(queueName, "topic_logs", bindingKey);
} Console.WriteLine(" [*] Waiting for messages. " +
"To exit press CTRL+C"); var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queueName, true, consumer); while (true)
{
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var routingKey = ea.RoutingKey;
Console.WriteLine(" [x] Received '{0}':'{1}'",
routingKey, message);
}
}
}
}
}

Run the following examples:

To receive all the logs:

$ ReceiveLogsTopic.exe "#"

To receive all logs from the facility "kern":

$ ReceiveLogsTopic.exe "kern.*"

Or if you want to hear only about "critical" logs:

$ ReceiveLogsTopic.exe "*.critical"

You can create multiple bindings:

$ ReceiveLogsTopic.exe "kern.*" "*.critical"

And to emit a log with a routing key "kern.critical" type:

$ EmitLogTopic.exe "kern.critical" "A critical kernel error"

Have fun playing with these programs. Note that the code doesn't make any assumption about the routing or binding keys, you may want to play with more than two routing key parameters.

Some teasers:

  • Will "*" binding catch a message sent with an empty routing key?
  • Will "#.*" catch a message with a string ".." as a key? Will it catch a message with a single word key?
  • How different is "a.*.#" from "a.#"?

(Full source code for EmitLogTopic.cs and ReceiveLogsTopic.cs)

Next, find out how to do a round trip message as a remote procedure call in tutorial 6

RabbitMq_05_Topics的更多相关文章

随机推荐

  1. 百练2505:A multiplication game

    传送门:http://bailian.openjudge.cn/practice/2505/ [题解] 我们找找规律: 1~9显然是Stan wins. 10~18是Ollie wins. 19~16 ...

  2. usaco 月赛 2005 january watchcow

    2013-09-18 08:13 //By BLADEVIL var n, m :longint; pre, other :..] of longint; last :..] of longint; ...

  3. 【git】git提交忽略不必要的文件或文件夹

    对于经常使用Git的朋友来说,.gitignore配置一定不会陌生.废话不说多了,接下来就来说说这个.gitignore的使用. 首先要强调一点,这个文件的完整文件名就是".gitignor ...

  4. 转: Java安全停止线程方法

    转: http://blog.csdn.net/flyingpig4/article/details/7675557 1.早期Java提供java.lang.Thread类型包含了一些列的方法star ...

  5. ubuntu16.04下fcitx无法在QT Creator输入中文解决办法

    我的博客新地址:www.liuquanhao.com ------------------------------------------------------ Qt creator无法用fcitx ...

  6. [译]Java8的函数式接口

    Java8引入了 java.util.function 包,他包含了函数式接口,具体的描述在以下api说明文档中: 函数式接口为lambda表达式和方法引用提供目标类型.每个函数式接口有一个单独的抽象 ...

  7. Eclipse svn代码提交冲突

    Eclipse svn代码提交冲突(转) 1.Synchronize视图下查看代码冲突 1.Incoming Mode 全部update,更新到本地2.Outgoing Mode 全部commit,提 ...

  8. C#实现DES加密解密,AES加密解密

    DES算法描述简介: DES是Data Encryption Standard(数据加密标准)的缩写.它是由IBM公司研制的一种加密算法,美国国家标准局于1977年公布把它作为非机要部门使用的数据加密 ...

  9. UVA Rails(stl-stack)

    具体思路是从B方向的序列 倒推 栈C 的入栈出栈顺序,  以B方向的5,4,3,2,1为例子:从一个数字5开始,要想从栈C拿到5号车厢,5号车厢得入栈C,且5号车厢处于栈顶位置,为了保证5号车  厢在 ...

  10. 大数据DMP画像系统(转载 简介-龙果学院)

    一.目标 1.掌握画像标签开发技巧 2.掌握数据挖掘技巧 3.了解业内画像和DMP系统的架构和开发 4.大数据结合业务场景落地 系统开发要求 涉及的技术要点:spark .elasticsearch. ...