/**
-
@ClassName: Producer
-
@Description: 生产者
-
@author Coder编程
-
@date2019年7月30日 下午23:15:51
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = “test_consumer_exchange”;
String routingKey = “consumer.save”;
String msg = “Hello RabbitMQ Consumer Message”;
for(int i =0; i<5; i ++){
channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
}
}
}
1.2.2 消费者
/**
-
@ClassName: Consumer
-
@Description: 消费者
-
@author Coder编程
-
@date2019年7月30日 下午23:13:51
*/
public class Consumer {
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = “test_consumer_exchange”;
String routingKey = “consumer.#”;
String queueName = “test_consumer_queue”;
channel.exchangeDeclare(exchangeName, “topic”, true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//实现自己的MyConsumer()
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
1.2.3 自定义类:MyConsumer
/**
-
@ClassName: MyConsumer
-
@Description: TODO
-
@author Coder编程
-
@date 2019年7月30日 下午23:11:55
*/
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
//根据需求,重写自己需要的方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println(“-----------consume message----------”);
//消费标签
System.err.println("consumerTag: " + consumerTag);
//这个对象包含许多关键信息
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
}
}
1.3 打印结果

2.1 什么是消费端的限流?
-
假设一个场景,首先,我们Rabbitmq服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现下面情况:
-
巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据!这个时候很容易导致服务器崩溃,出现故障。
为什么不在生产端进行限流呢?
因为在高并发的情况下,客户量就是非常大,所以很难在生产端做限制。因此我们可以用MQ在消费端做限流。
- RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息。
在限流的情况下,千万不要设置自动签收,要设置为手动签收。
- void BasicQos(uint prfetchSize,ushort prefetchCount,bool global);
参数解释:
prefetchSize:0
prefetchCount:会告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack。
global: true\false 是否将上面设置应用于channel,简单点说,就是上面限制是channel级别还是consumer级别。
prefetchSize和global这两项,rabbitmq没有实现,暂且不研究prefetch_count在no_ask = false的情况下生效,即在自动应答的情况下这两个值是不生效的。
第一个参数:消息的限制大小,消息多少兆。一般不做限制,设置为0
第二个参数:一次最多处理多少条,实际工作中设置为1就好
第三个参数:限流策略在什么上应用。在RabbitMQ一般有两个应用级别:1.通道 2.Consumer级别。一般设置为false,true 表示channel级别,false表示在consumer级别
2.2 代码演示
2.2.1 生产者
/**
-
@ClassName: Producer
-
@Description: 生产者
-
@author Coder编程
-
@date2019年7月30日 下午23:15:51
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = “test_qos_exchange”;
String routingKey = “qos.save”;
String msg = “Hello RabbitMQ QOS Message”;
for(int i =0; i<5; i ++){
channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
}
}
}
2.2.2 消费者
/**
-
@ClassName: Consumer
-
@Description: 消费者
-
@author Coder编程
-
@date2019年7月30日 下午23:13:51
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = “test_qos_exchange”;
String queueName = “test_qos_queue”;
String routingKey = “qos.#”;
channel.exchangeDeclare(exchangeName, “topic”, true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//1 限流方式 第一件事就是 autoAck设置为 false
//设置为1,表示一条一条数据处理
channel.basicQos(0, 1, false);
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
2.2.3 自定义类:MyConsumer
/**
-
@ClassName: MyConsumer
-
@Description: TODO
-
@author Coder编程
-
@date 2019年7月30日 下午23:11:55
*/
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println(“-----------consume message----------”);
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
//需要做签收,false表示不支持批量签收
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
2.2.4 测试结果
我们先注释掉:channel.basicAck(envelope.getDeliveryTag(), false);然后启动Consumer。
查看Exchange

查看Queues

然后再启动Producer。查看打印结果:

我们会发现消费端,只收到了一条消息。这是为什么呢?
第一点因为我们在consumer中
channel.basicConsume(queueName, false, new MyConsumer(channel));
第二个参数设置为false为手动签收。
第二点在qos中设置只接受一条消息。如果这一条消息不给Broker Ack应答的话,那么Broker会认为你并没有消费完这一条消息,那么就不会继续发送消息。
channel.basicQos(0, 1, false);
可以看下管控台,unack=1,Ready=4,total=5.

接下来我们放开注释channel.basicAck(envelope.getDeliveryTag(), false); 进行消息签收。重启服务。
3.1 打印结果

可以看到正常打印五条结果
4.1 消费端的手工ACK和NACK
消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿!
如果由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消费端消费成功!
4.2 消费端的重回队列
消费端重回队列是为了对没有处理成功的消息,把消息重新传递给Broker!
一般我们在实际应用中,都会关闭重回队列,也就是设置为False.
4.3 代码演示
4.3.1 生产者
/**
-
@ClassName: Producer
-
@Description: 生产者
-
@author Coder编程
-
@date2019年7月30日 下午23:15:51
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = “test_ack_exchange”;
String routingKey = “ack.save”;
for(int i =0; i<5; i ++){
Map<String, Object> headers = new HashMap<String, Object>();
headers.put(“num”, i);
//添加属性,后续会使用到
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) //投递模式,持久化
.contentEncoding(“UTF-8”)
.headers(headers)
.build();
String msg = "Hello RabbitMQ ACK Message " + i;
channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
}
}
}
4.3.2 消费者
/**
-
@ClassName: Consumer
-
@Description: 消费者
-
@author Coder编程
-
@date2019年7月30日 下午23:13:51
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//1创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = “test_ack_exchange”;
String queueName = “test_ack_queue”;
String routingKey = “ack.#”;
channel.exchangeDeclare(exchangeName, “topic”, true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
// 手工签收 必须要关闭 autoAck = false
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
4.3.3 自定义类:MyConsumer
/**
-
@ClassName: MyConsumer
-
@Description: TODO
-
@author Coder编程
-
@date 2019年7月30日 下午23:11:55
*/
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println(“-----------consume message----------”);
System.err.println("body: " + new String(body));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if((Integer)properties.getHeaders().get(“num”) == 0) {
//Nack三个参数 第二个参数:是否是批量,第三个参数:是否重回队列(需要注意可能会发生重复消费,造成死循环)
channel.basicNack(envelope.getDeliveryTag(), false, true);
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}
5.1 打印结果:

注意:
可以看到重回队列会出现重复消费导致死循环的问题,这时候最好设置重试次数,比如超过三次后,消息还是消费失败,就将消息丢弃。
6.1 TTL
-
TTL是Time To Live的缩写,也就是生存时间
-
RabbitMQ支持消息的过期时间,在消息发送时可以进行指定
-
RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过了队列的超时时间配置,那么消息会自动的清除
6.2 代码演示
6.2.1 直接通过管控台进行演示

通过管控台创建一个队列
x-max-length 队列的最大大小
x-message-ttl 设置10秒钟,如果消息还没有被消费的话,就会被清除。

添加exchange


Queue与Exchange进行绑定
点击 test_ttl_exchange 进行绑定
查看是否绑定成功


通过管控台发送消息

消息未处理自动清除

生产端设置过期时间
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding(“UTF-8”)
.expiration(“10000”)
.headers(headers)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。


既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。
《Java高级面试》

《Java高级架构知识》

《算法知识》

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
ders)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-Av2dz8s9-1713744427840)]
[外链图片转存中…(img-4di1CPrL-1713744427841)]
[外链图片转存中…(img-wwKOfZTh-1713744427841)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。
《Java高级面试》
[外链图片转存中…(img-OtwCAhl7-1713744427842)]
《Java高级架构知识》
[外链图片转存中…(img-IYNDtii1-1713744427842)]
《算法知识》
[外链图片转存中…(img-h6CaahJx-1713744427842)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
本文详细介绍了如何在RabbitMQ中使用生产者和消费者进行通信,并展示了如何通过QoS进行限流、手动签收以及利用TTL进行消息过期管理。
723

被折叠的 条评论
为什么被折叠?



