Routing

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

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

Bindings

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

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

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

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

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。这样接收程序就可以选择它希望接收的日志级别。首先先关注日志的发布:

首先创建一个交换机:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");

准备发送消息:

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

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

订阅

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

String queueName = channel.queueDeclare().getQueue();

for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}

Putting it all together

EmitLogDirect.java

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel; public class EmitLogDirect { private static final String EXCHANGE_NAME = "direct_logs"; public static void main(String[] argv) throws Exception { ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "direct"); String severity = getSeverity(argv);
String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + severity + "':'" + message + "'"); channel.close();
connection.close();
} private static String getSeverity(String[] strings){
if (strings.length < 1)
return "info";
return strings[0];
} private static String getMessage(String[] strings){
if (strings.length < 2)
return "Hello World!";
return joinStrings(strings, " ", 1);
} private static String joinStrings(String[] strings, String delimiter, int startIndex) {
int length = strings.length;
if (length == 0 ) return "";
if (length < startIndex ) return "";
StringBuilder words = new StringBuilder(strings[startIndex]);
for (int i = startIndex + 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}

ReceiveLogsDirect.java

import com.rabbitmq.client.*;

import java.io.IOException;

public class ReceiveLogsDirect {

  private static final String EXCHANGE_NAME = "direct_logs";

  public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue(); if (argv.length < 1){
System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
System.exit(1);
} for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}

【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. 什么是staging server

    原文链接:http://blog.csdn.net/blade2001/article/details/7194895 软件应用开发的经典模型有这样几个环境:开发环境(development).集成环 ...

  2. UVa10886 Standard Deviation

    留坑(p.345) 这是什么意思 暴力? 然而那些有两个人跑的那么快是为什么?(有个人竟然是陈锋...)

  3. AFNetworking2.0 NSHipster翻译

    AFNetworking 是当前 iOS 和 Mac OS X 开发中最广泛使用的开源项目之一.它帮助了成千上万叫好又叫座的应用,也为其它出色的开源库提供了基础.这个项目是社区里最活跃.最有影响力的项 ...

  4. (转)Google Fonts 的介绍与使用

    转载自“前端笔记”  http://www.cnblogs.com/milly/archive/2013/05/10/google-fonts.html Google Fonts 是什么?(以下翻译为 ...

  5. Latex插入图片 分类: LaTex 2014-11-18 20:07 261人阅读 评论(0) 收藏

    在Latex中插入图片的方式很多,我这里只介绍自己常用的一种方式,欢迎大家指导. 我习惯于使用graphicx宏包来插入图片,有时候会配合上subfigure宏包来同时插入多幅图片组合. 首先,需要在 ...

  6. ※C++随笔※=>☆C++基础☆=>※№→C++中 #include<>与#include""

    #include<> 使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找: #include"" 使用双引号则表示首先在 ...

  7. Android获得线性渐变某点的颜色

    安卓官方确实提供了好多非常强大的工具给我们了,例如我们最近经常在shape中加入gradient(渐变),像我的项目中用的是线性渐变, <?xml version="1.0" ...

  8. ios--绘图介绍

    iOS–绘图介绍 绘制图像的三种方式 一. 子类化UIView,在drawRect:方法画图 执行方法时,系统会自行创建画布(CGContext),并且讲画布推到堆栈的栈顶位置 执行完毕后,系统会执行 ...

  9. iOS UIKit:viewController之动画(5)

    当弹出一个view controller时,UIKit提供了一些标准转换动画,并且也支持用户自定义的动画效果. 1 UIView动画 UIView是自带动画实现功能,其中有两种方式实现:        ...

  10. Objective-C:swift、objective-c、C++、C混合编程

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...