继承DefaultConsumer,重写handleDelivery()方法
public class Producer {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost
//2. 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_consumer_exchange";
String routingKey = "consumer.save";
//4. 通过Channel发送数据
String message = "Hello Consumer Message";
for (int i = 0; i < 5; i++) {
channel.basicPublish(exchange,routingKey,null,message.getBytes());
}
}
}
public class Consumer {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();
//1. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_consumer_exchange";
String routingKey = "consumer.save";
String queueName = "test_consumer_queue";
//2. 声明一个exchange
channel.exchangeDeclare(exchange,"topic",true,false,null);
//3. 声明一个队列
channel.queueDeclare(queueName,true,false,false,null);
//4. 绑定
channel.queueBind(queueName,exchange,routingKey);
//5. 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6. 设置Channel
channel.basicConsume(queueName,true,new MyConsumer(channel));
/* //7. 获取消息
之前的方式,很ugly
while (true) {
//nextDelivery 会阻塞直到有消息过来
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("收到:" + message);
}*/
}
private static 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.out.println("——consume message——");
System.out.println("consumerTag:"+consumerTag);
System.out.println("envelope:"+envelope);
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
}
}
}
打印如下:
——consume message——
consumerTag:amq.ctag-DLKq_dy8aYspCTUBrnHTew
envelope:Envelope(deliveryTag=1, redeliver=false, exchange=test_consumer_exchange, routingKey=consumer.save)
properties:#contentHeader(content-type=null, content-encoding=null, headers=null, delivery-mode=null, priority=null, correlation-id=null, reply-to=null, expiration=null, message-id=null, timestamp=null, type=null, user-id=null, app-id=null, cluster-id=null)
body:Hello Consumer Message
——consume message——
consumerTag:amq.ctag-DLKq_dy8aYspCTUBrnHTew
envelope:Envelope(deliveryTag=2, redeliver=false, exchange=test_consumer_exchange, routingKey=consumer.save)
properties:#contentHeader(content-type=null, content-encoding=null, headers=null, delivery-mode=null, priority=null, correlation-id=null, reply-to=null, expiration=null, message-id=null, timestamp=null, type=null, user-id=null, app-id=null, cluster-id=null)
body:Hello Consumer Message
...
假设,MQ服务器上有上万条未处理的消息,随便打开一个消费者客户端,会出现下面情况:
巨量的消息瞬间全部推送过来,但是单个客户端无法同时处理这么多数据,从而导致服务器崩溃
解决方案:
void BasicQos(unit prefetchSize,unshort prefetchCount,bool global)
public class Producer {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost
//2. 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_qos_exchange";
String routingKey = "qos.save";
//4. 通过Channel发送数据
String message = "Hello Qos Message";
for (int i = 0; i < 5; i++) {
channel.basicPublish(exchange,routingKey,null,message.getBytes());
}
}
}
public class Consumer {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();
//1. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_qos_exchange";
String routingKey = "qos.#";
String queueName = "test_qos_queue";
//2. 声明一个exchange
channel.exchangeDeclare(exchange,"topic",true,false,null);
//3. 声明一个队列
channel.queueDeclare(queueName,true,false,false,null);
//4. 绑定
channel.queueBind(queueName,exchange,routingKey);
//5. 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6.限流方式 每次只推3条
channel.basicQos(0,3,false);
//7. 设置Channel autoAck一定要设置为false,才能做限流
channel.basicConsume(queueName,false,new MyConsumer(channel));
}
private static 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.out.println("——consume message——");
System.out.println("consumerTag:"+consumerTag);
System.out.println("envelope:"+envelope);
System.out.println("properties:"+properties);
System.out.println("body:"+new String(body));
// 手动签收
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
}
消费端可以进行手工ACK和NACK(不确认,表示失败)
消费端的重回队列:
对没有处理成功的消息,把消息重新传递给Broker
一般在实际应用中,会关闭重回队列
public class Producer {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost
//2. 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_ack_exchange";
String routingKey = "ack.save";
//4. 通过Channel发送数据
String message = "Hello Ack Message";
for (int i = 0; i < 5; i++) {
Map headers = new HashMap<>();
headers.put("num",i);
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()
.deliveryMode(2) //持久化
.contentEncoding("UTF-8")
.headers(headers)
.build();
channel.basicPublish(exchange,routingKey,properties,message.getBytes());
}
}
}
public class Consumer {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();
//1. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_ack_exchange";
String routingKey = "ack.#";
String queueName = "test_ack_queue";
//2. 声明一个exchange
channel.exchangeDeclare(exchange,"topic",true,false,null);
//3. 声明一个队列
channel.queueDeclare(queueName,true,false,false,null);
//4. 绑定
channel.queueBind(queueName,exchange,routingKey);
//5. autoAck = false
channel.basicConsume(queueName,false,new MyConsumer(channel));
}
private static 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.out.println("——consume message——");
System.out.println("body:"+new String(body));
System.out.println("num:" + properties.getHeaders().get("num"));
if ((Integer)properties.getHeaders().get("num") == 0 ) {
//requeue:true表示重新入队,重传
channel.basicNack(envelope.getDeliveryTag(),false,true);
} else {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
}
}
日志:
...
——consume message——
body:Hello Ack Message
num:0
——consume message——
body:Hello Ack Message
num:0
——consume message——
body:Hello Ack Message
num:0
...
num为0的消息会一直回到MQ队列的最尾端,然后一直循环打印,因为一直无法消费
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) //2:持久化投递;1:非持久化(未消费的消息重启后就没了)
.contentEncoding("UTF-8")
.expiration("5000")//5s 设置消息的TTL
.headers(headers)
.build();
String message = "Hello";
channel.basicPublish("","testQueue",properties,message.getBytes());
}
队列TTL:
Map args = new HashMap<>();
args.put("x-expires", 1800000); //30分钟
channel.queueDeclare("myqueue", false, false, false, args);
dead message:没有消费者去消费
)之后,它能被重新publish到另一个Exchange,这个Exchange就是死信队列 消息中间件消费到的消息处理失败怎么办?
专门有一个后台线程,监控消费者(如 物流)系统是否正常,能否请求的,不停的监视。
一旦发现消费者系统恢复正常,这个后台线程就从死信队列中取出 消费处理失败的消息,重新执行相应逻辑。
1.首先要设置死信队列的exchange和queue,然后进行绑定:
2.进行正常声明交换机、队列、绑定,只不过需要在队列上加上一个参数:(consumer里添加)arguments.put("x-dead-letter-exchange","dlx.exchange);
3.消息在过期、requeue、队列达到最大长度时,消息就可以直接路由到死信队列
public class Producer {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost
//2. 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_dlx_exchange";
String routingKey = "dlx.save";
//4. 通过Channel发送数据
String message = "Hello DLX Message";
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) //2:持久化投递;1:非持久化(未消费的消息重启后就没了)
.contentEncoding("UTF-8")
.expiration("5000")//5s后如果没有消费端消费,会变成死信
.build();
for (int i = 0; i < 1; i++) {
channel.basicPublish(exchange,routingKey,properties,message.getBytes());
}
}
}
public class Consumer {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();
//1. 通过connection创建一个Channel
Channel channel = connection.createChannel();
String exchange = "test_dlx_exchange";
String routingKey = "dlx.#";
String queueName = "test_dlx_queue";
String dlxExchange = "dlx.exchange";
String dlxQueue = "dlx.queue";
//2. 声明一个exchange
channel.exchangeDeclare(exchange,"topic",true,false,null);
Map arguments = new HashMap<>();
//路由失败,重发到dlx.exchange
arguments.put("x-dead-letter-exchange",dlxExchange);
/**
* 声明正常队列
* arguments要设置到声明队列上
*/
channel.queueDeclare(queueName,true,false,false,arguments);
channel.queueBind(queueName,exchange,routingKey);
//进行死信队列的声明
channel.exchangeDeclare(dlxExchange,"topic",true,false,null);
channel.queueDeclare(dlxQueue,true,false,false,null);
channel.queueBind(dlxQueue,dlxExchange,"#");
}
private static 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.out.println("——consume message——");
System.out.println("body:"+new String(body));
}
}
}
最后,启动Producer
5秒后,消息没被消费,然后就进去了死信队列(注意死信队列中初始值是1)
转自:RabbitMQ学习——高级特性