RabbitMQ基础教程之使用进阶篇

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇

I. 背景

前一篇基本使用篇的博文中,介绍了rabbitmq的三种使用姿势,可以知道如何向RabbitMQ发送消息以及如何消费,但遗留下几个疑问,本篇则主要希望弄清楚这几点

  • Exchange声明的问题(是否必须声明,如果不声明会怎样)
  • Exchange声明的几个参数(durable, autoDelete)有啥区别
  • 当没有队列和Exchange绑定时,直接往队列中塞数据,好像不会有数据增加(即先塞数据,然后创建queue,建立绑定,从控制台上看这个queue里面也不会有数据)
  • 消息消费的两种姿势(一个主动去拿数据,一个是rabbit推数据)对比

II. 基本进阶篇

1. Exchange默认场景

将前面的消息发送代码捞出来,干掉Exchange的声明,如下

public class DefaultProducer {
public static void publishMsg(String queue, String message) throws IOException, TimeoutException {
ConnectionFactory factory = RabbitUtil.getConnectionFactory(); //创建连接
Connection connection = factory.newConnection(); //创建消息通道
Channel channel = connection.createChannel();
channel.queueDeclare(queue, true, false, true, null); // 发布消息
channel.basicPublish("", queue, null, message.getBytes()); channel.close();
connection.close();
} public static void main(String[] args) throws IOException, TimeoutException {
for (int i = 0; i < 20; i++) {
publishMsg("hello", "msg" + i);
}
}
}

在发布消息时,传入的Exchange名为“”,再到控制台查看,发现数据被投递到了(AMQP default)这个交换器,对应的截图如下

 
image

看一下上面的绑定描述内容,重点如下

  • 默认交换器选择Direct策略
  • 将rountingKey绑定到同名的queue上
  • 不支持显示的绑定和解绑

上面的代码为了演示数据的流向,在发布消息的同时也定义了一个同名的Queue,因此可以在控制台上看到同名的 "hello" queue,且内部有20条数据

当我们去掉queue的声明时,会发现另一个问题,投入的数据好像并没有存下来(因为没有queue来接收这些数据,而之后再声明queue时,之前的数据也不会分配过来)

2. 绑定之后才有数据

首先是将控制台中的hello这个queue删掉,然后再次执行下面的代码(相对于前面的就是注释了queue的声明)

public class DefaultProducer {
public static void publishMsg(String queue, String message) throws IOException, TimeoutException {
ConnectionFactory factory = RabbitUtil.getConnectionFactory(); //创建连接
Connection connection = factory.newConnection(); //创建消息通道
Channel channel = connection.createChannel();
// channel.queueDeclare(queue, true, false, true, null); // 发布消息
channel.basicPublish("", queue, null, message.getBytes()); channel.close();
connection.close();
} public static void main(String[] args) throws IOException, TimeoutException {
for (int i = 0; i < 20; i++) {
publishMsg("hello", "msg" + i);
}
}
}

然后从控制台上看,可以看到有数据写入Exchange,但是没有queue来接收这些数据

 
IMAGE

然后开启消费进程,然后再次执行上面的塞入数据,新后面重新塞入的数据可以被消费;但是之前塞入的数据则没有,消费消息的代码如下:

public class MyDefaultConsumer {
public void consumerMsg(String queue) throws IOException, TimeoutException {
ConnectionFactory factory = RabbitUtil.getConnectionFactory(); //创建连接
Connection connection = factory.newConnection(); //创建消息通道
Channel channel = connection.createChannel();
channel.queueDeclare(queue, true, false, true, null);
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");
try {
System.out.println(" [ " + queue + " ] Received '" + message);
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}; // 取消自动ack
channel.basicConsume(queue, false, consumer);
} public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
MyDefaultConsumer consumer = new MyDefaultConsumer();
consumer.consumerMsg("hello"); Thread.sleep(1000 * 60 * 10);
}
}

小结:

  • 通过上面的演示得知一点
  • 当没有Queue绑定到Exchange时,往Exchange中写入的消息也不会重新分发到之后绑定的queue上

3. Durable, autoDeleted参数

在定义Queue时,可以指定这两个参数,这两个参数的区别是什么呢?

a. durable

持久化,保证RabbitMQ在退出或者crash等异常情况下数据没有丢失,需要将queue,exchange和Message都持久化。

若是将queue的持久化标识durable设置为true,则代表是一个持久的队列,那么在服务重启之后,也会存在,因为服务会把持久化的queue存放在硬盘上,当服务重启的时候,会重新什么之前被持久化的queue。队列是可以被持久化,但是里面的消息是否为持久化那还要看消息的持久化设置。也就是说,重启之前那个queue里面还没有发出去的消息的话,重启之后那队列里面是不是还存在原来的消息,这个就要取决于发生着在发送消息时对消息的设置

b. autoDeleted

自动删除,如果该队列没有任何订阅的消费者的话,该队列会被自动删除。这种队列适用于临时队列

这个比较容易演示了,当一个Queue被设置为自动删除时,当消费者断掉之后,queue会被删除,这个主要针对的是一些不是特别重要的数据,不希望出现消息积累的情况

// 倒数第二个参数,true表示开启自动删除
// 正数第二个参数,true表示持久化
channel.queueDeclare(queue, true, false, true, null);

c. 小结

  • 当一个Queue已经声明好了之后,不能更新durable或者autoDelted值;当需要修改时,需要先删除再重新声明
  • 消费的Queue声明应该和投递的Queue声明的 durable,autoDelted属性一致,否则会报错
  • 对于重要的数据,一般设置 durable=true, autoDeleted=false
  • 对于设置 autoDeleted=true 的队列,当没有消费者之后,队列会自动被删除

4. ACK

执行一个任务可能需要花费几秒钟,你可能会担心如果一个消费者在执行任务过程中挂掉了。一旦RabbitMQ将消息分发给了消费者,就会从内存中删除。在这种情况下,如果正在执行任务的消费者宕机,会丢失正在处理的消息和分发给这个消费者但尚未处理的消息。
但是,我们不想丢失任何任务,如果有一个消费者挂掉了,那么我们应该将分发给它的任务交付给另一个消费者去处理。

为了确保消息不会丢失,RabbitMQ支持消息应答。消费者发送一个消息应答,告诉RabbitMQ这个消息已经接收并且处理完毕了。RabbitMQ就可以删除它了。

因此手动ACK的常见手段

// 接收消息之后,主动ack/nak
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");
try {
System.out.println(" [ " + queue + " ] Received '" + message);
channel.basicAck(envelope.getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(envelope.getDeliveryTag(), false, true);
}
}
}; // 取消自动ack
channel.basicConsume(queue, false, consumer);

手动ack时,有个multiple,其含义表示:

可以理解为每个Channel维护一个unconfirm的消息序号集合,每publish一条数据,集合中元素加1,每回调一次handleAck方法,unconfirm集合删掉相应的一条(multiple=false)或多条(multiple=true)记录

III. 其他

1. 参考

Java Client API Guide

2. 一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

3. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

4. 扫描关注

 
QrCode

RabbitMQ基础教程之使用进阶篇的更多相关文章

  1. RabbitMQ基础教程之基本使用篇

    RabbitMQ基础教程之基本使用篇 最近因为工作原因使用到RabbitMQ,之前也接触过其他的mq消息中间件,从实际使用感觉来看,却不太一样,正好趁着周末,可以好好看一下RabbitMQ的相关知识点 ...

  2. RabbitMq基础教程之基本概念

    RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之 ...

  3. Python 基础教程 —— 网络爬虫入门篇

    前言 Python 是一种解释型.面向对象.动态数据类型的高级程序设计语言,它由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年.自面世以后,Pytho ...

  4. Python基础之面向对象(进阶篇)

    面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例(即:对象),实 ...

  5. RabbitMQ基础教程

    目录 RabbitMQ相关概念介绍 生产者和消费者 队列 交换器.路由键.绑定 交换器类型 RabbitMQ运转流程 AMQP协议介绍 AMQP生产者流转过程 AMQP消费者流转过程 安装Rabbit ...

  6. MongoDB基础教程系列--第三篇 MongoDB基本操作(二)

    1.集合操作 1.1.创建集合 MongoDB 用 db.createCollection(name, options) 方法创建集合. 格式 db.createCollection(name, op ...

  7. MongoDB基础教程系列--第四篇 MongoDB 查询文档

    查询文档 查询文档可以用 find() 方法查询全部文档,可以用 findOne() 查询第一个文档,当然还可以根据 条件操作符 和 $type操作符 查询满足条件的文档. find() 和 find ...

  8. MongoDB基础教程系列--第五篇 MongoDB 映射与限制记录

    上一篇提到的 find() 的方法,细心的伙伴会发现查询的结果都是显示了集合中全部的字段,实际应用中,显然是不够用的.那么有没有办法指定特定的字段显示出文档呢?答案是肯定的,MongoDB 中用映射实 ...

  9. MongoDB基础教程系列--第六篇 MongoDB 索引

    使用索引可以大大提高文档的查询效率.如果没有索引,会遍历集合中所有文档,才能找到匹配查询语句的文档.这样遍历集合中整个文档的方式是非常耗时的,特别是处理大数据时,耗时几十秒甚至几分钟都是有可能的. 创 ...

随机推荐

  1. 19、配置嵌入式servlet容器(下)

    使用外置的Servlet   嵌入式Servlet容器:应用打成可执行的j ar 优点:简单.便携: 缺点:默认不支持JSP.优化定制比较复杂         使用定制器[ServerProperti ...

  2. mybatis动态sql语句学习(一)

    动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格 ...

  3. 一段markdown编辑器代码研究

    一段markdown编辑器代码研究 说明 代码在 https://github.com/dukeofharen/markdown-editor 之所以选择这个来分析是一方面是因为它的代码结构比较简单, ...

  4. MVC 实现下拉框

    MVC动态实现下拉框的方式有很多种,但是方便快捷的却是很少,现在记录一种常用的下拉框实现方式: 1.先看看视图代码是怎么写的 <div class="form-group col-xs ...

  5. 截屏快捷键(windows)自带截屏,不需要安装任何插件

    方案一:电脑自带截屏,不需要QQ Shift+win+s即可截图(推荐使用) 方案二:搜狗输入法下载自带:Ctrl+Shift+x

  6. ios Block详细用法

    ios Block详细用法 ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.Blocks可以作为函数参数或者函数的 ...

  7. 阿贾克斯(AJAX)

    AJAX   :Asynchronous JavaScript and XML 异步的javascript 和xml 优点 : 在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分数据 不需要 ...

  8. Css中路径data:image/png;base64的用法详解 (转载)

    大家可能注意到了,网页上有些图片的src或css背景图片的url后面跟了一大串字符,比如: background-image:url(data:image/png;base64, iVBORw0KGg ...

  9. linux常用监测命令

    1 uptime uptime 命令可以用来查看服务器已经运行了多久,当前登录的用户有多少. 2 top top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于W ...

  10. 手机端app开发初识

    1.所需软件说明 百度云下载链接: https://pan.baidu.com/s/1-TEQZP9QbJSlGSYedyAUFw密码: 2z8l 或者官方链接: Hbuilder:http://ww ...