这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天
消息队列
简单,工作,发布确认,发布订阅,路由,主题
生产者
- 连接工厂
- 信道
- 队列
- 发送(basicPublish)
// 创建连接工厂
// 连接工厂 工厂模式
ConnectionFactory connectionFactory = new ConnectionFactory();
//
connectionFactory.setHost(host);
connectionFactory.setUsername(UserName);
connectionFactory.setPassword(passWorld);
// 获取连接对象
Connection connection = connectionFactory.newConnection();
// 获取信道
Channel channel = connection.createChannel();
// 生成队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 发送消息
Scanner sc =new Scanner(System.in);
while (sc.hasNext()){
// 循环发送
String message = sc.next();
channel.basicPublish("", QUEUE_NAME,null,message.getBytes());
System.out.println("发送完成:"+message);
}
消费者
- 工厂
- 信道
- 发送消息(basicConsume)
Channel channel = RabbitMqUtils.getChannel("127.0.0.1", "root", "root");
// 消息接收
DeliverCallback deliverCallback = (consumerTag, message) -> {
System.out.println("接收到的消息:" + new String(message.getBody()));
};
// 取消接收消息
CancelCallback cancelCallback = (cosumerTag) -> {
System.out.println("取消逻辑:" + cosumerTag);
};
// 接收消息
System.out.println("c2启动线程。。。。。。。。。");
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
消息应答
自动应答
手动应答
- channel.basicAck
- channel.basicNAck
- channel.basicRejecr
与 Channel.basicNack 相比少一个参数 不处理该消息了直接拒绝,可以将其丢弃了
在消费方的回调函数中,手动确认消息
消息持久化
如果队列突然宕机,或者消费方无法响应,就会导致消息丢失的现象
队列持久化
在生产者创建队列时,dur选项改为true
// 生成队列
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
消息持久化
在生产者发送消息的时候
MessageProperties.PERSISTENT_TEXT_PLAIN
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());
不公平分发
能者多劳,在消费者里设置
channel.basicQos(1);
欲取值
信道里最多能存几条
channel.basicQos(几条);
消息确认
消息发布在队列里,持久化需要保存在磁盘里。如果保存成功了,给生产者发个消息
- 消息持久化
- 队列持久化
- 消息确认
// 开启发布确认模式
channel.confirmSelect();
模式
- 单个确认发布
一个一个确认了在发,太慢了
- 批量确认发布
发完了或者每多少条确认一次,不知道丢失的是那个
- 异步确认发布
你只管发,我开一个回调函数线程去记录哪些成功哪些失败
// 开启发布确认
channel.confirmSelect();
// 监听器,开启单调的线程进行监听
channel.addConfirmListener
// 发送消息
channel.basicPublish("",QUEUE_NAME,null,message.getBytes() );
使用线程安全的ConcurrentSkipListMap在发送时进行存储,
回调函数中,如果处理成功,则删除。
最终返回未处理成功的map集合
Exchange
工作原理
交换机根据RoutingKey绑定队列,发给指定的消费者
类型
直接(direct)
- 路由交换机
交换机发送给指定RoutingKey的对象
主题(topic)
在绑定RuoutingKey的时候,加上前后通配符*..,只要符合就能接收到
当一个队列绑定未#,则接收所有消息。没有符号出现则与direct一样
标题(headers)
扇出(fanout)
- 发布交换机
将消息发送给所有绑定自己的队列,无论RoutingKey是否相同
channel.exchangeDeclare("Name", "fanout");
String queue = channel.queueDeclare().getQueue();
// 队列名 交换机名 RoutingKey
channel.exchangeBind(queue, "Name", "");
死信队列
消息发生异常
- TTl到期
- 队列达到最大长度
- 消息被拒绝
// 创建普通队列,如果想要在失败后加入死信队列,需要加入一个map集合存放的arg参数,
Map<String, Object> map = new HashMap<>();
map.put("x-dead-letter-exchange", "dead");
// 绑定队列到死信RoutinKey
map.put("x-dead-letter-routing-key", "list");
channel.queueDeclare("queue", false, false, false, map);
AMQP.Basicproperties priperties =
new AMQP.Basicproperties.builder().expiration("时间).buid
channel.baicPublish(,,,,,)
延迟队列
到规定的时间获取数据
整合SpringBoot
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 配置文件
// 创建交换机
@Bean("xExchange") // 直接交换机
public DirectExchange xExchange() {
return new DirectExchange(NORMAL_EXCHANGE);
}
// 创建队列,创建时添加条件,包括绑定死信交换机和RoutingKey,但是正常的绑定在Binding方法中
@Bean("queueA")
public Queue queueA() {
// 用map存放条件
Map<String, Object> map = new HashMap<>(3);
// 绑定死信交换机,设置RoutingKey
map.put("x-dead-letter-exchange", DEAD_EXCHANGE);
map.put("x-dead-letter-routing-key", "YD");
// 设置存活时间
map.put("x-message-ttl", 10000);
return QueueBuilder.durable(NORMAL_QUEUE1).withArguments(map).build();
}
//绑定
@Bean
public Binding queueABindX(@Qualifier("queueA") Queue q1,
@Qualifier("xExchange") DirectExchange d1){
return BindingBuilder.bind(q1).to(d1).with("XA");
}
- 发送消息
@Resource
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMsg/{msg}")
public void sendMsg(@PathVariable String msg){
// 开始时间
log.info("当前时间:{},发送一条信息给两个TTl队列:{}", new Date().toString(),msg);
// 发送消息 交换机 routingKey 发送的消息
rabbitTemplate.convertAndSend(NORMAL_EXCHANGE,"XA","消息来自TTL为10秒的消息:"+msg);
rabbitTemplate.convertAndSend(NORMAL_EXCHANGE,"XB","消息来自TTL为40秒的消息:"+msg);
}
- 接收消息
@RabbitListener(queues = "QD")
public void receiveD(Message message, Channel channel)throws Exception {
String msg = new String(message.getBody());
log.info("当前时间:{},收到死信队列的消息:{}",new Date().toString(),msg);
}
注意:
用死信队列作为延迟队列,他会以第一个进入队列的过期时间为准,
如果第一个任务的过期时间很长。则会导致后米娜过期时间失效。
使用插件解决
使用步骤
- 配置交换机、队列、绑定routingKey
public class DelayQueueConfig {
// 死信交换机
public static final String DELAY_EXCHANGE_NAME = "delayed.exchange";
// 队列
public static final String DELAY_QUEUE_NAME = "delayed.queue";
// routingKey
public static final String DELAY_ROUTING_KEY = "delayed.routingKey";
/**
* 声明交换机
* 交换机名 、 类型 、 是否需要持久化 、 是否需要自动删除 、 其他的参数
* @return 死信交换机
*/
@Bean
public CustomExchange delayedExchange(){
Map<String,Object> arg = new HashMap<>();
// 以直接发送形式的死信延迟队列
arg.put("x-delayed-message","direct");
return new CustomExchange(DELAY_EXCHANGE_NAME,"x-delayed-message",
false,false,arg);
}
// 创建队列
@Bean
public Queue delayedQueue(){
return new Queue(DELAY_QUEUE_NAME);
}
// 队列绑定
public Binding delayedQueueBindingDelayedExchange(@Qualifier Queue delayedQueue,
@Qualifier CustomExchange delayedExchange){
return BindingBuilder.bind(delayedQueue).to(delayedExchange).with(DELAY_ROUTING_KEY).noargs();
}
}
- 发送消息
@GetMapping("/delayed/{msg}/{delayTime}")
public void sendDelayedMsg(@PathVariable String msg,
@PathVariable Integer delayTime){
log.info("当前时间:{},发送一条{}浩渺的信息给延迟队列{}", new Date().toString(),delayTime,msg);
//发送消息
rabbitTemplate.convertAndSend(DelayQueueConfig.DELAY_EXCHANGE_NAME,
DelayQueueConfig.DELAY_ROUTING_KEY, msg,message->{
message.getMessageProperties().setDelay(delayTime);
return message;
});
}
- 接收消息
@Component
@Slf4j
public class DelayedQueueConsumer {
// 监听消息
@RabbitListener(queues = DelayQueueConfig.DELAY_QUEUE_NAME)
public void receiveDelayQueue(Message message){
String msg = new String(message.getBody());
log.info("当前时间:{},收到消息{}",new Date().toString(),msg);
}
}
消息确认和回退
消息确认:生产者和交换机之间的
消息回退:交换机和信道之间的
- 配置文件中打开消息确认
# 消息确认
publisher-confirm-type: correlated
# 消息回退
publisher-returns: true
- 自定义配置类,注入进spring框架
实现RabbitTemplate的ConfirmCallback和ReturnCallback方法,并通过set方法注入回RabbitTemplate中