RabbitMq_05_Topics
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的更多相关文章
随机推荐
- bzoj1855: [Scoi2010]股票交易 单调队列优化dp ||HDU 3401
这道题就是典型的单调队列优化dp了 很明显状态转移的方式有三种 1.前一天不买不卖: dp[i][j]=max(dp[i-1][j],dp[i][j]) 2.前i-W-1天买进一些股: dp[i][j ...
- android View实现变暗效果
android项目中做一个默认图片变暗,有焦点时变亮的效果.相信大家都能各种办法,各种手段很容易的实现这个效果.这里记录下作者实现这个效果的过程及遇到的问题,仅供参考.见下图(注:因为是eclipse ...
- 滑杆(JSlider)和进度指示条(JProgressBar) 的使用
package first; import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; impo ...
- Launcher3自定义壁纸旋转后拉伸无法恢复
MTK8382/8121平台. 描述:将自定义图片设置成壁纸后,横屏显示时,旋转为竖屏,图片由于分辨率过小,会拉伸:再旋转为横屏,拉伸不恢复. 这两天正在解这个问题,研究了很久,走了不少弯路,最后发现 ...
- 【bzoj2212&3702】二叉树
线段树合并入门题. 分别计算左子树的逆序对,右子树的逆序对,合并的时候计算贡献. #include<bits/stdc++.h> #define N 8000005 using names ...
- 【洛谷】xht模拟赛 题解
前言 大家期待已久并没有的题解终于来啦~ 这次的T1和HAOI2016撞题了...深表歉意...表示自己真的不知情... 天下的水题总是水得相似,神题各有各的神法.--<安娜·卡列妮娜> ...
- servlet(1) - 手写第一个servlet程序 - 小易Java笔记
声明:如tomcat的安装目录为D:\Java\tomcat6,下面要根据tomcat的安装目录而定 1. 建立程序的文件结构 ==>找到tomcat的安装目录,在webapps目录下新建一个名 ...
- JS:body元素对象的clientWidth、offsetWidth、scrollWidth、clientLeft、offsetLeft、scrollLeft
document.body.clientWidth 获取body元素对象的内容可视区域的宽度,即clientWidth=width+padding,不包括滚动条. document.body.clie ...
- rabbitmq安装-Erlang
安装Erlang Install Erlang from the Erlang Solutions repository or Follow the instructions under " ...
- Linux下Tomcat安装配置
买了台阿里云服务器,因为配置比较低,所以用Linux系统,这里记录一下我在Linux系统中Tomcat的安装配置. 前提JDK已经安装好. 安装 首先在/usr/local/下建立一个tomcat的文 ...