RabbitMQ(2)---高级使用
1.ack和限流
ack也就是消息确认签收,分为自动签收和手动签收。之前的交换机demo中:channel.basicConsume(queueName,true, consumer); 第二个参数就是自动签收,如果我们要手动签收则需要改成false,再去消息处理中手动签收即可
当我们消息队列中已经积压了大量消息的时候。这个时候消费者才启动,,如果是自动签收的话,就会导致大量消息涌入,可能回到服务刚启动就宕机。这个时候就可以限制消息数量,使用手动签收。处理完这一批,再处理下一批。
使用手动签收,我们还可以在拿到消息,进行不同的业务处理,比如如果消息信息有问题,那就不签收,移除当前队列,或者放到其他地方去处理之类的
RabbitMQUtils类的代码在上一节中:RabbitMQ(1)---基本概念及简单demo
ack:手动签收消息:
package com.nijunyang.rabbitmq.ack; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID; /**
* Description:
* Created by nijunyang on 2020/6/7 13:07
*/
public class AckProducer { public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
String message = "hello rabbitMQ." + new Random().nextInt(100); String exchangeName = "ack.exchange";
String routingKey = "ack.key"; Map<String,Object> heads = new HashMap<>();
heads.put("userName", "zhangsan"); AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
.deliveryMode(2)//消息持久化
.contentEncoding("UTF-8")
.correlationId(UUID.randomUUID().toString())
.headers(heads)//存放头信息
.build(); channel.basicPublish(exchangeName, routingKey, basicProperties, message.getBytes("utf-8"));
channel.basicPublish(exchangeName, routingKey, null, message.getBytes("utf-8"));
RabbitMQUtils.close(channel, connection);
}
}
package com.nijunyang.rabbitmq.ack; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.*;
import org.springframework.util.StringUtils; import java.io.IOException;
import java.util.concurrent.TimeoutException; /**
* Description:
* Created by nijunyang on 2020/6/7 13:07
*/
public class AckConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "ack.exchange";
String exchangeType = "direct";
String routingKey = "ack.key";
String queueName = "ack.queue"; channel.exchangeDeclare(exchangeName, exchangeType);
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName, exchangeName, routingKey); Consumer consumer = new DefaultConsumer(channel) { @Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { try {
Object userName = properties.getHeaders().get("userName");
if (!StringUtils.isEmpty(userName)) {
//用发送时候放的 头信息模拟业务问题
String message = new String(body, "UTF-8");
System.out.println(message);
//手动签收消息
channel.basicAck(envelope.getDeliveryTag(),false);
}
else {
throw new RuntimeException();
}
}catch (Exception e) {
//requeue参数 true 重回队列,,false不重回队列, 或者做其他处理
channel.basicNack(envelope.getDeliveryTag(),false,false);
}
}
}; //autoAck参数 true:开启自动签收,false:关闭自动签收功能
channel.basicConsume(queueName,false, consumer); }
}
限流的话只需要多一个限制:channel.basicQos(0,5,false); 每次只会处理5条消息,签收完了,在处理后面的
package com.nijunyang.rabbitmq.limit; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID; /**
* Description:
* Created by nijunyang on 2020/6/7 13:07
*/
public class LimitProducer { public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel(); String exchangeName = "limit.exchange";
String routingKey = "limit.key"; for (int i = 0; i < 20; i++) {
String message = "limit rabbitMQ." + i;
channel.basicPublish(exchangeName, routingKey, null, message.getBytes("utf-8"));
} RabbitMQUtils.close(channel, connection);
}
}
package com.nijunyang.rabbitmq.limit; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.*;
import org.springframework.util.StringUtils; import java.io.IOException;
import java.util.concurrent.TimeoutException; /**
* Description:
* Created by nijunyang on 2020/6/7 13:07
*/
public class LimitConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "limit.exchange";
String exchangeType = "direct";
String routingKey = "limit.key";
String queueName = "limit.queue"; channel.exchangeDeclare(exchangeName, exchangeType);
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName, exchangeName, routingKey); /**
* 限流设置:
* prefetchSize:每条消息大小的设置
* prefetchCount:标识每次推送多少条消息
* global:false标识channel级别的 true:标识消费的级别的
*/
channel.basicQos(0,5,false); 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(message);
//手动签收消息, 否则就会一直阻塞了
channel.basicAck(envelope.getDeliveryTag(), false); }
}; //autoAck参数 true:开启自动签收,false:关闭自动签收功能
channel.basicConsume(queueName,false, consumer); }
}
2.消息投递确认,开启这个模式之后 消息投递了之后 不能关闭连接,因为监听是绑定在channel上面的
开启消息投递确认模式,,在消息发送者上面绑定一个监听,消息投递成功或者失败回调对应方法。
package com.nijunyang.rabbitmq.confirm; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection; import java.io.IOException;
import java.util.concurrent.TimeoutException; /**
* Description:
* Created by nijunyang on 2020/6/7 19:53
*/
public class ConfirmProducer {
public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel(); //设置消息投递模式(确认模式)
channel.confirmSelect();
/**
* 消息确认监听绑定
*/
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("消息投递成功");
} @Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("消息投递失败");
}
}); String exchangeName = "confirm.exchange";
String routingKey = "confirm.key"; for (int i = 0; i < 20; i++) {
String message = "limit rabbitMQ." + i;
channel.basicPublish(exchangeName, routingKey, null, message.getBytes("utf-8"));
}
//设置了消息投递确认就不能关闭channel和连接了
// RabbitMQUtils.close(channel, connection);
}
}
3.不可达消息处理:有些消息发送之后,由于设置的原因,不能正常的路由到队列上面。
和消息投递确认差不多,只不过是在生产者的channel上面绑定一个ReturnListener(channel.addReturnListener(new RetrunListener())),然后投递消息的时候使用这个方法channel.basicPublish(exchangeName,routingKey,true,null, message.getBytes()),相比之前的投递方式多了一个布尔类型的mandatory参数。如果true那么就会调用的绑定的ReturnListener,实现的方法,如果是false那么就会直接删除这个消息。
4.死信队列。专门用来接收没有消费的消息的队列。消息发送到正常队列上面但是没有被消费,就会被转发到死信队列上面。所以说死信队列是和一个正常队列绑定的。消息变成死信的几种情况:1.消息被拒绝(basicNack basicReject)并且重回队里设置的false,2.消息设置了过期时间,时间到了也没有被消费,3.队列已经达到最大长度,后面进来的消息直接转到死信队列。死信队列也是一个正常的交换机和队列。
package com.nijunyang.rabbitmq.deadqueue; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection; import java.io.IOException; /**
* Description:
* Created by nijunyang on 2020/6/7 20:48
*/
public class DeadQueueProducer {
public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel(); String exchangeName = "normal.exchange";
String routingKey = "normal.key"; //设置消息的过期时间10s
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
.deliveryMode(2)
.expiration("10000")
.build();
for (int i = 0; i < 20; i++) {
String message = "dead rabbitMQ." + i;
channel.basicPublish(exchangeName, routingKey, basicProperties, message.getBytes("utf-8"));
}
RabbitMQUtils.close(channel, connection);
}
}
package com.nijunyang.rabbitmq.deadqueue; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.*; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; /**
* Description:
* Created by nijunyang on 2020/6/7 20:51
*/
public class DeadQueueConsumer {
public static void main(String[] args) throws Exception{
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel(); //声明正常的队列
String normalExchangeName = "normal.exchange";
String exchangeType = "direct";
String normalQueueName = "normal.queue";
String routingKey = "normal.key"; channel.exchangeDeclare(normalExchangeName, exchangeType); //申明死信队列
String deadExchangeName = "dead.exchange";
String deadExchangeType = "topic";
String deadQueueName = "dead.queue"; Map<String, Object> queueArgs = new HashMap<>();
//正常队列上绑定死信队列信息
queueArgs.put("x-dead-letter-exchange", deadExchangeName);
queueArgs.put("x-max-length", 4); //队列的最大长度
channel.queueDeclare(normalQueueName,true,false,false, queueArgs);
channel.queueBind(normalQueueName, normalExchangeName, routingKey); //声明死信队列
channel.exchangeDeclare(deadExchangeName, deadExchangeType);
channel.queueDeclare(deadQueueName,true,false,false,null);
channel.queueBind(deadQueueName, deadExchangeName,"#"); 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(message);
channel.basicNack(envelope.getDeliveryTag(),false,false); //拒签
}
};
channel.basicConsume(normalQueueName, false, consumer); }
}
可有看到所有的消息最后都转到了 死信队列中去了。这个模式还可以用于延迟队列。只需要设置正常队列消息的过期时间,然后转到死信队列,,消费者监听消费死信队列,就可以实现延时队列了。
5.单播消费模式,首先我们要明确一点消费者最终都是从队列中拿到消息消费的,我们将多个消费者都绑定到同一个队列上面去,这个时候,队列消息只会被一个消费者消费,不会重复让每个消费者都消费。
6.多播消费模式,和单播消费差不多,这个时候我们需要申明多个队列绑定同一个交换机,这样交换机的信息就会发到多个队列上面,这样通过同一个交换机将同一条消息发送到不同的队列上面去了,也就实现了让不同的消费者消费了同一条消息了。
package com.nijunyang.rabbitmq.deadqueue; import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.*; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; /**
* Description:
* Created by nijunyang on 2020/6/7 20:51
*/
public class DeadQueueConsumer {
public static void main(String[] args) throws Exception{
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel(); //声明正常的队列
String normalExchangeName = "normal.exchange";
String exchangeType = "direct";
String normalQueueName = "normal.queue";
String routingKey = "normal.key"; channel.exchangeDeclare(normalExchangeName, exchangeType); //申明死信队列
String deadExchangeName = "dead.exchange";
String deadExchangeType = "topic";
String deadQueueName = "dead.queue"; Map<String, Object> queueArgs = new HashMap<>();
//正常队列上绑定死信队列信息
queueArgs.put("x-dead-letter-exchange", deadExchangeName);
queueArgs.put("x-max-length", ); //队列的最大长度
channel.queueDeclare(normalQueueName,true,false,false, queueArgs);
channel.queueBind(normalQueueName, normalExchangeName, routingKey); //声明死信队列
channel.exchangeDeclare(deadExchangeName, deadExchangeType);
channel.queueDeclare(deadQueueName,true,false,false,null);
channel.queueBind(deadQueueName, deadExchangeName,"#"); 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(message);
channel.basicNack(envelope.getDeliveryTag(),false,false); //拒签
}
};
channel.basicConsume(normalQueueName, false, consumer); }
}
RabbitMQ(2)---高级使用的更多相关文章
- RabbitMQ AMQP (高级消息队列协议)
目录 RabbitMQ AMQP (高级消息队列协议) Message Queue 简介 概念 基本组成 场景及作用 AMQP简介 模型架构 基础组件 AMQP-RabbitMQ 简介 模型 特性 参 ...
- Rabbitmq之高级特性——百分百投递消息&消息确认模式&消息返回模式实现
rabbitmq的高级特性: 如何保障消息的百分之百成功? 要满足4个条件:生产方发送出去,消费方接受到消息,发送方接收到消费者的确认信息,完善的消费补偿机制 解决方案,1)消息落库,进行消息状态打标 ...
- EasyNetQ操作RabbitMQ(高级消息队列)
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件).写消息队列的时候用RabbitMQ比较好,但是写的时候需要自己封装下,自己的封装,就需要对RabbitM ...
- RabbitMQ的高级特性概念理解
1.RabbitMQ中的消息如何保障百分之百的投递成功? 答:百分之百的投递成功,方案可以参考下面的2.3. 2.什么是生产者端的可靠性投递? 答:第一步,生产者保障消息的成功发出.第二步,保障Rab ...
- Rabbitmq的高级特性
消息如何保证100%投递成功? 什么是生产端的可靠性投递? 1.保障消息的成功发出 2.保障MQ节点的成功接收 3.发送端收到MQ节点(Broker)确认应答 4.完善的消息补偿机制 BAT互联网大厂 ...
- Rabbitmq之高级特性——实现消费端限流&NACK重回队列
如果是高并发下,rabbitmq服务器上收到成千上万条消息,那么当打开消费端时,这些消息必定喷涌而来,导致消费端消费不过来甚至挂掉都有可能. 在非自动确认的模式下,可以采用限流模式,rabbitmq ...
- RabbitMQ(二):RabbitMQ高级特性
RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用.作为一名合格的开发者,有必要了解一下相关知识,RabbitMQ(一)已经入门RabbitMQ,本文介绍Rabb ...
- 消息中间件——RabbitMQ(七)高级特性全在这里!(上)
前言 前面我们介绍了RabbitMQ的安装.各大消息中间件的对比.AMQP核心概念.管控台的使用.快速入门RabbitMQ.本章将介绍RabbitMQ的高级特性.分两篇(上/下)进行介绍. 消息如何保 ...
- 消息中间件——RabbitMQ(八)高级特性全在这里!(下)
前言 上一篇消息中间件--RabbitMQ(七)高级特性全在这里!(上)中我们介绍了消息如何保障100%的投递成功?,幂等性概念详解,在海量订单产生的业务高峰期,如何避免消息的重复消费的问题?,Con ...
随机推荐
- 「雕爷学编程」Arduino动手做(29)——DS1302时钟模块
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...
- 最小生成树 状压+prim hdu2489
Minimal Ratio TreeTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- UVA 11651
题目链接:https://cn.vjudge.net/problem/UVA-11651 解题思路: 思路来源于网络. DP + 矩阵快速幂. 设 dp[i][j] 为满足 score 为 i 且最后 ...
- Nginx 运维(安装与使用)
Nginx 运维(安装与使用) 普通安装 Windows安装 (1)进入官方下载地址,选择合适版本(nginx/Windows-xxx). (2)解压到本地 (3)启动 下面以 C 盘根目录为例说明下 ...
- #442-Find All Duplicates in an Array-数组中重复的数字
一.题目 给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次. 找到所有出现两次的元素. 你可以不用到任何额外空间并在O(n)时间复杂度内解 ...
- java——引入第三方jar包
第一步:项目->New->Folder:创建一个文件夹: 第二步:把要引入的jar包粘贴到新建的文件夹中: 第三步:选中引入的jar包->Build Path->Add to ...
- static关键字的应用
static关键字的应用:使用静态的变量可以实现 "累加" 的效果 package com.aff.statics; public class TestCircle { pub ...
- 从Student类和Teacher类多重派生Graduate类 代码参考
#include <iostream> #include <cstring> using namespace std; class Person { private: char ...
- CAD文件打印为PDF文档
标题: CAD文件打印为PDF文档 作者: 梦幻之心星 347369787@QQ.com 标签: [CAD, 打印, 转换] 目录: 软件 日期: 2019-5-28 目录 CAD文件打印为PDF文档 ...
- 深入理解Mybatis(第一讲)——手写ORM框架(简易版Mybatis)
我们来自定义一个持久层框架,也就是Mybatis的简易版. 使用端的搭建 idea中新建maven工程IPersistence_test: 在resources目录下新建sqlMapConfig.xm ...