RabbitMQ高级属性
- **中国加油,武汉加油!**
- 篇幅较长,配合目录观看
- 案例准备
- 1. 参数的含义
- 2. confirm机制
- 3. return机制-写生产者和消费者
- 4. 消费端的限流问题
- 5. TTL队列(Time To Live)
- 6. 死信队列
- 7. 消费者端手动签收和消息的重回队列
- 8. 消息的延迟投递来解决传递的可靠性
- 9. 日志消息表实现可靠消息的传输
中国加油,武汉加油!
篇幅较长,配合目录观看
案例准备
- 一个Centos7的服务器
1. 参数的含义
channel.queueDeclare(QUEUE_NAME,true,false,true,null);
第二个参数:如果是false 重启之后 队列都没有了 数据也会丢失
第三个参数:连接一旦关闭 那么就会删除这个队列
第四个参数:就是最后一个消费者 退出去之后 那么这个队列是否自动删除
第五个参数:讲ttl队列的时候要专门讲
channel.basicPublish("",QUEUE_NAME,null,"我是小波波1111".getBytes());
第一个参数 :交换机
第二个参数:路由key
第三个参数:设置的队列的属性
第四个参数:值
2. confirm机制
场景: 放到队列中的消息成功还是失败都会进行反馈
public class Producer {
//申明队列的名字
private static final String QUEUE_NAME="nz1904-confirm-01";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//第一步:开启confirm消息确认机制
channel.confirmSelect();
//我们就要对消息的可达性实施监听
//下面就是对消息的签收情况进行确认
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long l, boolean b) throws IOException {
System.out.println("发送成功的监听.....");
}
@Override
public void handleNack(long l, boolean b) throws IOException {
System.out.println("发送失败的监听.....");
}
});
channel.queueDeclare(QUEUE_NAME,false,false,true,null);
channel.basicPublish("",QUEUE_NAME,null,"我是小波波1111".getBytes());
}
}
3. return机制-写生产者和消费者
场景:我们在发送消息的是时候、我们指定的交换机不存在或者指定的路由key不存在 这种时候我们就需要监听这种不可达的消息
channel.basicPublish(arg1, arg2, arg3, mandatory, arg5);
mandatory 如果设置为ture:就表示的是要监听不可达的消息 进行处理
如果设置为false 那么队列端会直接删除这个消息
package com.wpj.returnlistener;
import com.wpj.utils.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ReturnListener;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
private static final String EXCHANGE_NAME="test_return_exchange";
//能路由的key
private static final String ROUTING_KEY="return.save";
//不能路由的key
private static final String ROUTING_ERROR_KEY="abc.save";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//添加监听
channel.addReturnListener(new ReturnListener() {
/**
*
* @param i:队列响应给浏览器的状态码
* @param s:表示的是状态码对应的文本信息
* @param s1:交换机的名字
* @param s2:表示的是路由的key
* @param basicProperties:表示的是消息的属性
* @param bytes:消息体的内容
* @throws IOException
*/
@Override
public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
System.out.println("监听到不可达的消息");
}
});
channel.basicPublish(EXCHANGE_NAME,ROUTING_ERROR_KEY,true,null,"这里是测试Return机制".getBytes());
}
}
package com.wpj.returnlistener;
import com.wpj.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer {
private static final String EXCHANGE_NAME="test_return_exchange1";
//是能路由的key
private static final String ROUTING_KEY="return.#";
//制定绑定的队列
private static final String QUEUE_NAME="test_return_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//申明队列
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//申明交换机
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//绑定
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,ROUTING_KEY);
//申明消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("收到这个消息了....");
}
};
//进行消费的绑定
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
4. 消费端的限流问题
场景:消费者死了 队列里面一瞬间就就积累了上万条数据、这个时候当我们打开客户端的时候、瞬间就有巨量的信息给推送过来、但是我们的客户端是没有办法同时处理这么多数据的 结果就是消费者死了…
这种场景下我们就需要对消费端进行限流
package com.wpj.limit;
import com.wpj.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
//申明队列的名字
private static final String QUEUE_NAME="nz1904-limit-01";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for (int i = 0; i <100 ; i++) {
channel.basicPublish("",QUEUE_NAME, null,("测试限流"+i).getBytes());
}
channel.close();
connection.close();
}
}
package com.wpj.limit;
import com.wpj.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer {
//申明队列的名字
private static final String QUEUE_NAME="nz1904-limit-01";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建通道
final Channel channel = connection.createChannel();
//申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//消费者的申明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1接受到的消息是:"+new String(body));
//进行手动应答
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//绑定这个消费者
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
}
}
package com.wpj.limit;
import com.wpj.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer1 {
//申明队列的名字
private static final String QUEUE_NAME="nz1904-limit-01";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建通道
final Channel channel = connection.createChannel();
//申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//设置限流机制
/**
* 第一个参数:消息本身的大小 如果设置为0 那么表示对消息本身的大小不限制
* 第二个参数:告诉rabbitmq不要一次性给消费者推送大于N个消息 你要推送的前提是
* 现在这N个消息 已经手动被确认 已经完成
* 第三个参数:true/false :是否将上面的设置应用于整个通道 true :表示整个通道的消费者
* 都是这个策略 如果是false表示的是 只有当前的consumer 是这个策略
*/
channel.basicQos(0,5,false);
//结论:实际上如果不设置的话 分配任务的事 一开始就分配好了
//必须手动签收
//消费者的申明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2接受到的消息是:"+new String(body));
try {
Thread.sleep(200);
}catch (Exception err){
}
//进行手动应答
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//绑定这个消费者
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
}
}
5. TTL队列(Time To Live)
场景:我要下单 下单之后 在一定的时间内、我的订单如果没有被处理 那么自动失效
简单的说就是咋们的队列中的消息是有时间限制的、如果超时那么这个消息将会被队列给删除
//我们只需要给下面的队列设置好属性 那么这个队列 就自动变成 ttl队列了
Map<String,Object> map=new HashMap<>();
map.put("x-message-ttl",5000);
channel.queueDeclare(QUEUE_NAME,false,false,false,map);
6. 死信队列
前提:
- 发送到队列中的消息被拒绝了
- 消息的ttl时间过期
- 队列达到了最大长度 再往里面放信息
package com.wpj.dlx;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.wpj.utils.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生产者 发送消息是发送到了 那个非死信的交换机上面的
*/
public class Producer {
//定义的是队列(正常的交换机) 这里发送消息是在交换机上面
private static final String EXCHANGE_NAME="ttl-dlx-exchange";
//定义一个路由的key
private static final String ROUTING_KEY="dlx.#";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
for (int i = 0; i <5 ; i++) {
channel.basicPublish(EXCHANGE_NAME,ROUTING_KEY,false,null,("死信队列测试"+i).getBytes());
}
}
}
package com.wpj.dlx;
import com.rabbitmq.client.*;
import com.wpj.utils.ConnectionUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* 这个就是模拟消费者
*/
public class Consumer {
//定义的是交换机
private static final String EXCHANGE_NAME="ttl-dlx-exchange";
//正常情况下的队列
private static final String QUEUE_NAME="ttl-dlx-queue";
//定义死信队列的交换机的名字
private static final String DLX_EXCHANGE_NAME="dlx-exchange";
//死信队列的定义
private static final String DLX_QUEUE_NAME="dlx-queue";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
final Channel channel = connection.createChannel();
//创建交换机和队列进行绑定
channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
//队列的申明
//我们要申明成死信队列
Map<String,Object> map=new HashMap<String,Object>();
map.put("x-message-ttl",5000);
//添加一个死信的属性 //后面这个名字就是死信队列交换机的名字
map.put("x-dead-letter-exchange",DLX_EXCHANGE_NAME);
channel.queueDeclare(QUEUE_NAME,true,false,false,map);
//进行队列和交换机进行绑定
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"dlx.#");
//上面是正常的队列的申明
//下面就是死信队列的申明
channel.exchangeDeclare(DLX_EXCHANGE_NAME,"topic");
//申明队列
channel.queueDeclare(DLX_QUEUE_NAME,true,false,false,null);
//绑定这个死信队列
channel.queueBind(DLX_QUEUE_NAME,DLX_EXCHANGE_NAME,"#");
//直接性的来调用这个
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("获取到数据了:"+new String(body));
}
};
//绑定消费者
channel.basicConsume(DLX_QUEUE_NAME,true,defaultConsumer);
//现在有这个问题呀?
}
}
7. 消费者端手动签收和消息的重回队列
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接受到的消息是:"+new String(body));
/**第一个参数:当前消息的标记
* 第二个参数:是否批量进行应答
* 下面是签收
*/
//channel.basicAck(envelope.getDeliveryTag(),false);
//下面也可以拒绝签收
/**
* 第三个参数:表示决绝签收之后这个消息是否要重回队列?
*/
channel.basicNack(envelope.getDeliveryTag(),false,true);
}
8. 消息的延迟投递来解决传递的可靠性
9. 日志消息表实现可靠消息的传输