翻译官网的文章已经翻译了几天了,这份官方文档写的总体算是很简洁易懂。它让我们很快的入门并了解了RabbitMQ的运作原理和使用方式。本篇最后介绍一下Exchange的另外两种类别,即direct和topic。对官网文档的翻译工作也将告一段落,接下来会探讨异步消息队列在spring项目中的集成。但现在还是先把RabbitMQ的另外两种使用方式来介绍完。

  Direct类型的Exchange

  一.基础知识点

  1.direct类别的Exchange.

  记得上一章节我们使用的是fanout类别的Exchange,它的特点是消费者不加区分地订阅所有消息,即所有的消费者都能订阅并收到广播到这个Exchange的消息。这一节我们希望稍作改动,让不同消费者能够订阅不同的消息。要实现这个功能,需要使用Direct的Exchange.

  其运作原理如图所示。

  

  首先,将Exchange设为direct类型,可用这句话来实现:channel.exchangeDeclare(EXCHANGE_NAME, "direct")。

  然后,我们发现,queue和Producer之间,除了多出Exchange,还多出了另外一条线索,也就是所谓的routeKey。其实它就是对Exchange的进一步分类,这样做了以后,当我们广播消息的时候,消息就进行了两层分类,第一层是Exchange(图中的X),第二层就是RouteKey(图中的Orange,green等)。我们使用如下语句来发布消息,channel.basicPublish(EXCHANGE_NAME, orange, null, message.getBytes());其中第二个参数就是RouteKey.

  再然后,消费者如果想要接收指定的消息,只需要在符合一定条件的queue的集合中去寻找即可。比如说,我想接收颜色为orange的消息,则去Q1中寻找,我想接收颜色为black或者green的消息,就去Q2中寻找。当然Q1和Q2需要进行绑定才能将特定的消息放进来。使用如下语句即可以将Queue与Exchange和RouteKey绑定。channel.queueBind(queueName, EXCHANGE_NAME, orange);其中第三个参数就是RouteKey。

  二.Demo

  话不多说,看下面的demo,这个demo希望对不同的日志信息做不同的处理,相信读者很快就会理解其中原理。

  首先是生产者类。

  

package com.xdx.learn;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import net.sf.json.JSONObject; import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class EmitLogDirect {
private final static String EXCHANGE_NAME="direct_logs";//交换机名称为log public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.1.195");//服务器ip
factory.setPort(5672);//端口
factory.setUsername("xdx");//登录名
factory.setPassword("xxxxx");//密码
Connection connection=factory.newConnection();//建立连接
Channel channel=connection.createChannel();//建立频道
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);//在频道里声明一个交换机,类型定位direct
System.out.println(channel+"发布20条info日志消息");
for(int i=0;i<20;i++){
String message="info日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为info
System.out.println(message);
}
System.out.println(channel+"发布10条error日志消息");
for(int i=0;i<10;i++){
String message="error日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
System.out.println(channel+"发布5条debug日志消息");
for(int i=0;i<5;i++){
String message="debug日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "debug", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
channel.close();
connection.close();
}
}

  再看消费者类。

package com.xdx.learn;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; public class ReceiveLogsDirect {
private final static String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws IOException, TimeoutException {
// 下面的配置与生产者相对应
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.1.195");// 服务器ip
factory.setPort(5672);// 端口
factory.setUsername("xdx");// 登录名
factory.setPassword("xxxxx");// 密码
Connection connection = factory.newConnection();// 连接
final Channel channel = connection.createChannel();// 频道
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
final String queueName = channel.queueDeclare().getQueue();// 生成一个独立的,非持久的,自动删除的queue
channel.queueBind(queueName, EXCHANGE_NAME, "info");// 绑定queue和exchange,还有routekey。这样队列中就有通过EXCHANGE_NAME发布的消息。
channel.queueBind(queueName, EXCHANGE_NAME, "debug");
System.out.println(" messages from channel:"+ channel+",queue:"+ queueName
+ ". To exit press CTRL+C");
// defaultConsumer实现了Consumer,我们将使用它来缓存生产者发送过来储存在队列中的消息。当我们可以接收消息的时候,从中获取。
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("channel:"+channel+",queue:"+queueName+",consumer:"+this.getConsumerTag()+" Received '" + message + "'");
// channel.basicAck(envelope.getDeliveryTag(),false);
try {
Thread.sleep(300);
} catch (Exception e) {
}
}
};
channel.basicConsume(queueName, true, consumer);//自动回复,消息发出后队列自动消除
} }

  首先我们先运行消费者类,这样它就可以守株待兔,订阅消息了。然后再运行生产者类。

  生产者类的控制台打印出如下信息:

  

  消费者类的控制台打印出如下信息。

  

  三.总结

  1.在生产者类中,我们将消息分为三种类别,info,error,debug,并分别广播进了以这三个类别作为routeKey的Exchange中。这是第一步,对消息做分类。

  2.在消费者类中,我们想要查看info和debug这两种类型的消息,所以我们将queue与这两种类别的Exchange进行了绑定,通过channel.queueBind(queueName, EXCHANGE_NAME, "info");channel.queueBind(queueName, EXCHANGE_NAME, "debug");这两条代码。这样,这个queue中就存满了这两种类型的信息,消费者就可以使用它们了。至于error类别的日志消息,因为没有消费者订阅,所以它们会被丢弃。

  应该来说demo是非常简单也非常恰当的解释了Direct类型的Exchange的运作机制。

  Topic类型的Exchange

  topic类型的Exchange其实是在Direct类型上进行了扩展,如果direct类型的Exchange只能从一个维度来对Exchange进行区分,那么topic类型的Exchange以一种更为灵活的方式对Exchange进行了多维度的区分。关键还是在RouteKey上。

  一.基础知识点

  来看topic类型的Exchange的示意图。

  

  可以看到,现在的RouteKey不在是一个单一的一个字段来描述,而是可以有多个字段来共同描述。每个字段代表一个维度。例如上述的例子,我们描述一种动物,第一个字段代表速度,第二个字段代表颜色,第三个字段代表的是物种。字段之间用点号隔开,即<speed>.<colour>.<species>。并且,它还为我们提供了两个很好用的通配符。

  *:代表一个字段

  #:代表0个或者多个字段。

  所以*.orange.*代表的是所以橙色的动物,*.*.rabbit代表的是所以兔子,而lazy.#代表的是所有反应缓慢的动物。那么:quick.orange.rabbit匹配Q1和Q2,lazy.orange.elephant也是,quick.orange.fox匹配Q1.lazy.brown.fox匹配Q2.

  值得注意的是,如果我将一个routeKey写为#,则这时候Exchange相当于fanout类型,而如果routeKey中即没有*又没有#,则相当于Direct类型了。

  二.Demo

  话不多说,还是来看一个demo吧。在这个demo中,我们将消息分为两个维度<project><severity>。

  生产者类

  

package com.xdx.learn;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import net.sf.json.JSONObject; import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class EmitLogTopic {
private final static String EXCHANGE_NAME="topic_logs";//交换机名称为log public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.1.195");//服务器ip
factory.setPort(5672);//端口
factory.setUsername("xdx");//登录名
factory.setPassword("xxxx");//密码
Connection connection=factory.newConnection();//建立连接
Channel channel=connection.createChannel();//建立频道
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);//在频道里声明一个交换机,类型定位topic
System.out.println(channel+"发布3条project1.info日志消息");
for(int i=0;i<3;i++){
String message="project1.info日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project1.info", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为info
System.out.println(message);
}
System.out.println(channel+"发布3条project1.error日志消息");
for(int i=0;i<3;i++){
String message="project1.error日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project1.error", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
System.out.println(channel+"发布3条project1.debug日志消息");
for(int i=0;i<3;i++){
String message="project1.debug日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project1.debug", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
System.out.println(channel+"发布3条project2.info日志消息");
for(int i=0;i<3;i++){
String message="project2.info日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project2.info", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为info
System.out.println(message);
}
System.out.println(channel+"发布3条project2.error日志消息");
for(int i=0;i<3;i++){
String message="project2.error日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project2.error", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
System.out.println(channel+"发布3条project2.debug日志消息");
for(int i=0;i<3;i++){
String message="project2.debug日志消息"+i;
channel.basicPublish(EXCHANGE_NAME, "project2.debug", null, message.getBytes());//发布消息,发布到EXCHANGE_NAME,并且routeKey标为error
System.out.println(message);
}
channel.close();
connection.close();
}
}

  消费者类

  

package com.xdx.learn;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; public class ReceiveLogsTopic {
private final static String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws IOException, TimeoutException {
// 下面的配置与生产者相对应
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.1.195");// 服务器ip
factory.setPort(5672);// 端口
factory.setUsername("xdx");// 登录名
factory.setPassword("xxxx");// 密码
Connection connection = factory.newConnection();// 连接
final Channel channel = connection.createChannel();// 频道
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
final String queueName = channel.queueDeclare().getQueue();// 生成一个独立的,非持久的,自动删除的queue
channel.queueBind(queueName, EXCHANGE_NAME, "*.info");// 绑定queue和exchange,还有routekey。这样队列中就有通过EXCHANGE_NAME发布的消息。
channel.queueBind(queueName, EXCHANGE_NAME, "project1.*");
System.out.println(" messages from channel:"+ channel+",queue:"+ queueName
+ ". To exit press CTRL+C");
// defaultConsumer实现了Consumer,我们将使用它来缓存生产者发送过来储存在队列中的消息。当我们可以接收消息的时候,从中获取。
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("channel:"+channel+",queue:"+queueName+",consumer:"+this.getConsumerTag()+" Received '" + message + "'");
// channel.basicAck(envelope.getDeliveryTag(),false);
try {
Thread.sleep(300);
} catch (Exception e) {
}
}
};
channel.basicConsume(queueName, true, consumer);//自动回复,消息发出后队列自动消除
} }

  运行消费者类,再运行生产者类,分别打印出。

  生产者类:

  

  生产者类:

  

  大家可以根据代码和结果来验证我们的假设,这里就不做总结了。

五.RabbitMQ之路由(Routing)和主题(topics)的更多相关文章

  1. RabbitMQ学习总结 第五篇:路由Routing

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  2. RabbitMQ之路由(Routing)【译】

    在上一节中,我们创建了一个简单的日志系统,可以广播消息到很多接收者. 这一节,我们将在上一节的基础上加一个功能--订阅部分消息.例如,我们只将严重错误信息写入到日志文件保存在磁盘上,同时我们能将所有的 ...

  3. RabbitMQ入门教程(七):主题交换机Topics

    原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

  4. RabbitMQ官方中文入门教程(PHP版) 第四部分:路由(Routing)

    路由(Routing) 在前面的教程中,我们实现了一个简单的日志系统.可以把日志消息广播给多个接收者. 本篇教程中我们打算新增一个功能——使得它能够只订阅消息的一个字集.例如,我们只需要把严重的错误日 ...

  5. RabbitMQ 入门教程(PHP版) 第四部分:路由(Routing)

    路由(Routing) 在前面的第三部分教程中,我们实现了一个简单的日志系统.可以把日志消息广播给多个接收者. 本篇教程中我们打算新增一个功能——使得它能够只订阅消息的一个字集.例如,我们只需要把严重 ...

  6. RabbitMQ系列教程之五:主题(Topic)(转载)

    RabbitMQ系列教程之五:主题(Topic) (本实例都是使用的Net的客户端,使用C#编写),说明,中文方括号[]表示名词. 在上一个教程中,我们改进了我们的日志记录系统. 没有使用只能够进行虚 ...

  7. asp.net MVC 5 路由 Routing

    ASP.NET MVC ,一个适用于WEB应用程序的经典模型 model-view-controller 模式.相对于web forms一个单一的整块,asp.net mvc是由连接在一起的各种代码层 ...

  8. AngularJS - 路由 routing 基础示例

    AngularJS 路由 routing 能够从页面的一个视图跳转到另外一个视图,对单页面应用来讲是至关重要的.当应用变得越来越复杂时,我们需要一个合理的方式来管理用户在使用过程中看到的界面.Angu ...

  9. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

随机推荐

  1. CSS和文档

    1. 块级元素: p,div,ul,ol,h1,h2 . . . h6等.块级元素独占一行,旁边不能有其他元素. 2. 行内元素:span,a,strong,em等. display属性可以使块级元素 ...

  2. 我是如何理解Android的Handler模型_3

    AsyncTask则相当于现代化的电话系统,接线员的功能被完全封装了. 对于上例,新建更新TextView的类并继承AsyncTack类,如下: class UpdataTV extends Asyn ...

  3. 前端面试题系列(1):doctype作用 标准模式与兼容模式

    1.doctype作用 <!DOCTYPE>声明位于HTML文档的第一行.处于<HTML>标签之前.告知浏览器的解析器用什么文档标准解析这个文档.DOCYTYPE不存在或格式不 ...

  4. OpenCV探索之路(二十七):皮肤检测技术

    好久没写博客了,因为最近都忙着赶项目和打比赛==| 好吧,今天我打算写一篇关于使用opencv做皮肤检测的技术总结.那首先列一些现在主流的皮肤检测的方法都有哪些: RGB color space Yc ...

  5. 程序、计算机程序、java初论

    一.程序? 程序一词来自生活,通常指完成某些事情的一种既定方式和过程,可以将程序看成对一系列动作的执行过程的描述. 例如:个人去银行取钱 1.带上存折/银行卡去银行 2.取号排队 3.将存折或储蓄卡递 ...

  6. 在SQL Server Express版本中没有代理功能如何自动备份数据库

    因为是免费的且单个数据库可以支持到10GB,对于一般企业完全足够了,也就将就使用了,备份将分为两步: 1.创建备份脚本 2.创建系统的计划任务进行每天的备份 详细做法如下: 1.创建备份脚本 打开SS ...

  7. VMware下设置Centos7联网与固定IP连接Xshell

    爱折腾的小伙伴应该经常会用Vmware安装一些虚拟机用于学习,但是比如装了Linux,经常操作的时候非常切换窗口的时候非常麻烦,所以很多人都会选择用Xshell来连接本地的Linux虚拟机,但是用Xs ...

  8. 熵(Entropy),交叉熵(Cross-Entropy),KL-松散度(KL Divergence)

    1.介绍: 当我们开发一个分类模型的时候,我们的目标是把输入映射到预测的概率上,当我们训练模型的时候就不停地调整参数使得我们预测出来的概率和真是的概率更加接近. 这篇文章我们关注在我们的模型假设这些类 ...

  9. Java快速扫盲指南

    文章转自:https://segmentfault.com/a/1190000004817465#articleHeader22 JDK,JRE和 JVM 的区别 JVM:java 虚拟机,负责将编译 ...

  10. C++课程设计2

    PS:大一下学期C++课程设计 1.成绩管理系统 #include<stdio.h> #include<string> #include<iostream> #in ...