Routing

之前的章节里我们构建了一个简单的日志系统。我们可以广播所有的日志消息给所有的接收端。

本节我们将给它添加一个新特性 - 我们将允许只订阅一个消息的子集。例如,我们只将关键的错误消息定位到文件中(以节省磁盘空间),同时仍然可以在控制台输出所有日志消息。

Bindings

在前面的例子中我们已经创建了绑定关系。回想代码如下:

  1. channel.queueBind(queueName, EXCHANGE_NAME, "");

一个绑定是指一个交换机和一个队列之间的关系。可以简单的理解为:队列对交换机中的消息感兴趣。

绑定需要一个额外的routingKey参数。为了避免和basic_publish中的参数混淆,我们现在称它为binding key。创建一个带有binding key的绑定:

  1. channel.queueBind(queueName, EXCHANGE_NAME, "black");

binding key的意义跟交换机的类型有关。我们之前使用的fanout交换机就直接忽略了binding key。

Direct交换机

我们之前的日志系统广播所有的消息给所有的消费者。我们希望扩展它,以便它可以根据消息严重级别来过滤。例如我们可能想要一个程序仅将关键错误写入磁盘,从而避免写入警告或信息导致磁盘空间的浪费。

我们曾经使用fanout交换机,它没有给我们很多的灵活性,仅仅就是无情的广播。

我们将使用一个direct交换机替代它。direct交换机的路由算法很简单 - 一个将要进入队列的消息的routing key必须和这个队列的binding key完全吻合。

为了说明这一点,请考虑一下设置:

可以看到direct交换机绑定了两个队列。第一个队列绑定了一个叫做orange的binding key。第二个队列有两个绑定,一个black一个green。

在这种设置下,以routing key为orange的消息将会被路由到队列Q1。而routing key为black和green的消息会被路由到Q2。所有其它消息会被丢弃。

多绑定

使用相同的binding key绑定多个队列是合法的。在我们的例子中,我们可以添加一个X和Q1之间的绑定,使用black作为binding key。这种情况下,direct交换机会像fanout一样广播消息给所有的匹配队列。一个带有routing key为black的消息将会被同时发送到Q1和Q2。

发送日志

我们将应用这种模型到我们的日志系统中。与fanout不同的是,我们将发送消息到一个direct交换机。需要提供日志级别作为routing key。这样接收程序就可以选择它希望接收的日志级别。首先先关注日志的发布:

首先创建一个交换机:

  1. channel.exchangeDeclare(EXCHANGE_NAME, "direct");

准备发送消息:

  1. channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());

为了简化,我们假设severity可以是info,warning,error等。

订阅

接收消息就像前几节中讲的类似,但有一个例外 - 我们需要为每一个感兴趣的级别创建一个新的绑定。

  1. String queueName = channel.queueDeclare().getQueue();
  2.  
  3. for(String severity : argv){
  4. channel.queueBind(queueName, EXCHANGE_NAME, severity);
  5. }

Putting it all together

EmitLogDirect.java

  1. import com.rabbitmq.client.ConnectionFactory;
  2. import com.rabbitmq.client.Connection;
  3. import com.rabbitmq.client.Channel;
  4.  
  5. public class EmitLogDirect {
  6.  
  7. private static final String EXCHANGE_NAME = "direct_logs";
  8.  
  9. public static void main(String[] argv) throws Exception {
  10.  
  11. ConnectionFactory factory = new ConnectionFactory();
  12. factory.setHost("localhost");
  13. Connection connection = factory.newConnection();
  14. Channel channel = connection.createChannel();
  15.  
  16. channel.exchangeDeclare(EXCHANGE_NAME, "direct");
  17.  
  18. String severity = getSeverity(argv);
  19. String message = getMessage(argv);
  20.  
  21. channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
  22. System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
  23.  
  24. channel.close();
  25. connection.close();
  26. }
  27.  
  28. private static String getSeverity(String[] strings){
  29. if (strings.length < 1)
  30. return "info";
  31. return strings[0];
  32. }
  33.  
  34. private static String getMessage(String[] strings){
  35. if (strings.length < 2)
  36. return "Hello World!";
  37. return joinStrings(strings, " ", 1);
  38. }
  39.  
  40. private static String joinStrings(String[] strings, String delimiter, int startIndex) {
  41. int length = strings.length;
  42. if (length == 0 ) return "";
  43. if (length < startIndex ) return "";
  44. StringBuilder words = new StringBuilder(strings[startIndex]);
  45. for (int i = startIndex + 1; i < length; i++) {
  46. words.append(delimiter).append(strings[i]);
  47. }
  48. return words.toString();
  49. }
  50. }

ReceiveLogsDirect.java

  1. import com.rabbitmq.client.*;
  2.  
  3. import java.io.IOException;
  4.  
  5. public class ReceiveLogsDirect {
  6.  
  7. private static final String EXCHANGE_NAME = "direct_logs";
  8.  
  9. public static void main(String[] argv) throws Exception {
  10. ConnectionFactory factory = new ConnectionFactory();
  11. factory.setHost("localhost");
  12. Connection connection = factory.newConnection();
  13. Channel channel = connection.createChannel();
  14.  
  15. channel.exchangeDeclare(EXCHANGE_NAME, "direct");
  16. String queueName = channel.queueDeclare().getQueue();
  17.  
  18. if (argv.length < 1){
  19. System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
  20. System.exit(1);
  21. }
  22.  
  23. for(String severity : argv){
  24. channel.queueBind(queueName, EXCHANGE_NAME, severity);
  25. }
  26. System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
  27.  
  28. Consumer consumer = new DefaultConsumer(channel) {
  29. @Override
  30. public void handleDelivery(String consumerTag, Envelope envelope,
  31. AMQP.BasicProperties properties, byte[] body) throws IOException {
  32. String message = new String(body, "UTF-8");
  33. System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
  34. }
  35. };
  36. channel.basicConsume(queueName, true, consumer);
  37. }
  38. }

【RabbitMQ】 Routing的更多相关文章

  1. 【RabbitMQ】 WorkQueues

    消息分发 在[RabbitMQ] HelloWorld中我们写了发送/接收消息的程序.这次我们将创建一个Work Queue用来在多个消费者之间分配耗时任务. Work Queues(又称为:Task ...

  2. 【rabbitmq】rabbitmq集群环境搭建

    安装rabbitmq-server 总共有3台虚拟机,都安装有rabbitmq服务,安装过程可参考: [rabbitmq]Centos7 下安装rabbitmq 创建用户和vhost 说明: 此步骤不 ...

  3. 【RabbitMQ】 Java简单的实现RabbitMQ

    准备工作 1.安装RabbitMQ,参考[RabbitMQ] RabbitMQ安装 2.新建Java项目,引入RabbitMQ的Maven依赖 <dependency> <group ...

  4. 【RabbitMQ】 RabbitMQ配置开机启动

    环境 系统:Linux(CentOS 7.2) Erlang环境:21.1(安装参考[Erlang]源码安装) RabbitMQ:3.7.9(安装参考[RabbitMQ] RabbitMQ安装) 配置 ...

  5. 【RabbitMQ】显示耗时处理进度

    [RabbitMQ]显示耗时处理进度 通过网页提交一个耗时的请求,然后启动处理线程,请求返回.处理线程每完成一部分就给前台推送完成的数量,前端显示进度. 依赖jar <?xml version= ...

  6. 【RabbitMQ】使用学习

    [RabbitMQ]使用学习 转载: ============================================================= =================== ...

  7. 【UVA1057】Routing

    [UVA1057]Routing 题面 洛谷 题解 有一个比较好想的dp就是\(f_{i,j}\)表示第一个点在\(i\),第二个点在\(j\)的最小点数,但是直接搞不好转移. 考虑建出反图,那么\( ...

  8. 【RabbitMQ】 RabbitMQ安装

    MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的是程序之间 ...

  9. 【RabbitMQ】一文带你搞定RabbitMQ延迟队列

    本文口味:鱼香肉丝   预计阅读:10分钟 一.说明 在上一篇中,介绍了RabbitMQ中的死信队列是什么,何时使用以及如何使用RabbitMQ的死信队列.相信通过上一篇的学习,对于死信队列已经有了更 ...

随机推荐

  1. 内存数据库MemSQL ——基于内存,MVCC+哈希表、跳表

    本周数据库业界探讨最火热的话题就是MemSQL,究竟是不是"旧瓶装新酒"引发了诸多的辩论,同时也引发了究竟是产品技术重要还是DBA重要的疑问.网络中有一些关于MemSQL的介绍,基 ...

  2. 软中断与硬中断 & 中断抢占 中断嵌套

    参考了这篇文章:http://blog.csdn.net/zhangskd/article/details/21992933 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通 ...

  3. NDK安装 eclipse 不出现NDK目录问题

    android adt自带eclipse无法设置ndk路径 具体情况是 我在mac上搭建android环境 到android sdk官网下载r23版本的adt时自带的eclipse没有设置ndk路径的 ...

  4. git pull 部署问题一揽子问题解决

    之前遇到问题 在服务器拉取一直不成功, php 的shell函数 调用 git pull 一直不成功 ,但是单独 用root 权限 在机器上面 执行 git pull 是可以的 说明语法没问题. 而 ...

  5. qt 状态栏

    有段时间没有写过博客了.假期去上海旅游,所以一直没有能够上网.现在又来到这里,开始新的篇章吧!   今天的内容主要还是继续完善前面的那个程序.我们要为我们的程序加上一个状态栏.   状态栏位于主窗口的 ...

  6. [转] Linux strace 简介

    http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html 简介 strace常用来跟踪进程执行时的系统调用和所接收的信号. 在L ...

  7. MD5 SHA-1 示例

    测试代码 public class SignatureUtils {     public static void main(String[] args) throws Exception {     ...

  8. date和long的相互转换

    import java.text.SimpleDateFormat; import java.util.Date; public class T { public static void main(S ...

  9. HTML5 ArrayBufferView之DataView

    DataView视图 如果一段数据包括多种类型(比如服务器传来的HTTP数据),这时除了建立ArrayBuffer对象的复合视图以外,还可以通过DataView视图进行操作. DataView视图提供 ...

  10. 用WebStorm调试本地html(含嵌入的javascript).

    题外话: 以前很少能调试,甚至以为不能调试(好笨),后来我看了一本叫做<<Learning Three.js>>的一本书后,里面推荐有几种javascript的编辑工具,都蛮好 ...