RabbitMQ笔记
本篇主要记录内容:docker安装rabbitmq、rabbitmq 交换机directExchange、fanoutExchange、topicExchange三种学习,还有其他的(header)用的不多就不记录了。消息确认、延迟、死信队列等。
1:docker安装rabbitmq
//获取镜像 1:docker pull rabbitmq //创建并启动容器 2:docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5672:5672 rabbitmq

到此就rabbitmq环境就准备完成了,不得不说docker安装真是简单。。。
2:与springboot整合配置测试环境
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
添加配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: admin
virtual-host: /
3:directExchange(直连交换机,根据路由key进行分发)
代码中配置队列、也可以采用注解方式配置、也可在rabbit控制台操作(最好在代码中配置)
1:声明交换机
2:声明队列
3:创建绑定Key
4:bind队列与交换机
DirectConfig
package com.person.chenpt.config.rabbit;
import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 操作rabbitmq消息队列步骤
* 代码中配置、也可以采用注解方式配置、也可在rabbit控制台操作(最好在代码中配置)
* 1:声明交换机
* 2:声明队列
* 3:创建绑定Key
* 4:bind队列与交换机
*
*
* direct 直连交换机 根据路由key进行分发 多个队列binding同一个交换机
* 配置多台监听绑定到同一个直连交互的同一个队列
* 会以轮询的方式对消息进行消费,而且不存在重复消费。
*
*
* @Author: chenpt
* @Description:
* @Date: Created in 2022-07-28 11:08
* @Modified By:
*/
@Configuration
public class DirectConfig {
@Bean
public DirectExchange directExchange(){
return new DirectExchange(RabbitConst.directExchange,true,false,null);
}
@Bean
public Queue directQueue(){
/**
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
*/
return new Queue(RabbitConst.directQueue,true);
}
@Bean
public Binding bindingDirectQueue(){
return BindingBuilder.bind(directQueue()).to(directExchange()).with(RabbitConst.directRoutingKey);
}
@Bean
public Queue directQueue2(){
return new Queue(RabbitConst.directQueue2,true);
}
@Bean
public Binding bindingDirectQueue2(){
return BindingBuilder.bind(directQueue2()).to(directExchange()).with(RabbitConst.directRoutingKey);
}
}
controller
@RestController
@RequestMapping("/rabbit")
@Api(tags = "消息队列")
public class RabbitMqController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send")
@ApiOperation("direct队列发送")
public Result sendMessage(){
rabbitTemplate.convertAndSend(RabbitConst.directExchange,RabbitConst.directRo utingKey,"hello test");
return Result.success();
}
}
consumer
@RabbitListener(queues = RabbitConst.directQueue2)
public void process2(String testMsg){
System.out.println("DirectReceiver2消费者收到消息 : " + testMsg);
}
4:FanoutExchange(扇形交换机)
不需要路由键、多个队列binding到此交换机均能收到消息
package com.person.chenpt.config.rabbit;
import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
* 扇型交换机
* 不需要路由键 多个队列binding到交换机都能收到消息
* 与多个队列采用同一个路由键binding到direct交换机效果相同
*
*
* @Author: chenpt
* @Description:
* @Date: Created in 2022-07-28 14:54
* @Modified By:
*/
@Configuration
public class FanoutConfig {
@Bean
FanoutExchange fanoutExchange(){
return new FanoutExchange(RabbitConst.fanoutExchange,true,false);
}
@Bean
Queue fanoutQueue1(){
return new Queue(RabbitConst.fanoutQueue1,true);
}
@Bean
Queue fanoutQueue2(){
return new Queue(RabbitConst.fanoutQueue2,true);
}
@Bean
Binding fanoutBinding1(){
return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
}
@Bean
Binding fanoutBinding2(){
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
}
}
controller
@GetMapping("/sendFanout")
@ApiOperation("fanout队列发送")
public Result sendFanoutMessage(){
for(int i=1;i<=10;i++){
rabbitTemplate.convertAndSend(RabbitConst.fanoutExchange,null,"hello fanout test");
}
return Result.success("ok");
}
consume
@RabbitListener(queues = RabbitConst.fanoutQueue1)
public void fanoutQueue1(String testMsg){
System.out.println("fanoutReceiver1消费者收到消息 : " + testMsg);
}
@RabbitListener(queues = RabbitConst.fanoutQueue2)
public void fanoutQueue2(String testMsg){
System.out.println("fanoutReceiver2消费者收到消息 : " + testMsg);
}
5:TopicExchange(主题交换机)
对路由键进行模式匹配后投递
符号 # 表示一个或多个词,符号 * 表示一个词。
例如“abc.#”能够匹配到“abc.def.ghi”,但是“abc.*” 只会匹配到“abc.def”
package com.person.chenpt.config.rabbit;
import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 主题模式
* 主题交换机,对路由键进行模式匹配后进行投递,
* 符号 # 表示一个或多个词,
* 符号 * 表示一个词。
* 因此“abc.#”能够匹配到“abc.def.ghi”,
* 但是“abc.*” 只会匹配到“abc.def”
* @Author: chenpt
* @Description:
* @Date: Created in 2022-07-28 15:28
* @Modified By:
*/
@Configuration
public class TopicConfig {
@Bean
TopicExchange topicExchange(){
return new TopicExchange(RabbitConst.topicExchange,true,false);
}
@Bean
Queue topicQueue1(){
return new Queue(RabbitConst.topicQueue1,true);
}
@Bean
Queue topicQueue2(){
return new Queue(RabbitConst.topicQueue2,true);
}
@Bean
Binding bindingTopicQueue1(){
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with(RabbitConst.topicRoutingKey1);
}
@Bean
Binding bindingTopicQueue2(){
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(RabbitConst.topicRoutingKey2);
}
}
controller
@GetMapping("/sendTopic")
@ApiOperation("topic队列发送")
public Result sendTopicMessage(){
rabbitTemplate.convertAndSend(RabbitConst.topicExchange,"chenpt.order.test","hello order test");
rabbitTemplate.convertAndSend(RabbitConst.topicExchange,"chenpt.sms","hello sms test");
return Result.success("ok");
}
consume
@RabbitListener(queues = RabbitConst.topicQueue1)
public void topicQueue1(String testMsg){
System.out.println("topicReceiver1消费者收到消息 : " + testMsg);
}
@RabbitListener(queues = RabbitConst.topicQueue2)
public void topicQueue2(String testMsg){
System.out.println("topicReceiver2消费者收到消息 : " + testMsg);
}
6:DeadExchange 死信交换机
当消息在队列中变成死信时,被重新发送到一个特殊的交换机中,同时绑定的队列就成为死信队列。
以下几种情况会导致消息变为死信:
消息被拒绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false;
消息过期;
队列达到最大长度。
DeadConfig
此配置下声明了一个deadQueue,另外创建了一个测试队列和死信队列进行绑定
package com.person.chenpt.config.rabbit;
import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 死信队列--Dead-Letter-Exchange,死信交换器
* 消息在一个队列中变成死信(Dead Letter)之后,被重新发送到一个特殊的交换器(DLX)中,同时,绑定DLX的队列就称为“死信队列”。
* 以下几种情况导致消息变为死信:
* 消息被拒绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false;
* 消息过期;
* 队列达到最大长度。
* @Author: chenpt
* @Description:
* @Date: Created in 2022-07-29 11:31
* @Modified By:
*/
@Configuration
public class DeadConfig {
@Bean
FanoutExchange deadExchange(){
return new FanoutExchange(RabbitConst.deadExchange,true,false);
}
@Bean
Queue deadQueue(){
return new Queue(RabbitConst.deadQueue,true);
}
@Bean
Binding deadBinding(){
return BindingBuilder.bind(deadQueue()).to(deadExchange());
}
@Bean
DirectExchange directExchangeBindDead(){
return new DirectExchange("testDirectEx",true,false);
}
@Bean
Queue directQueueBindDead(){
Map<String,Object> deadmap = new HashMap<>();
// 绑定该队列到私信交换机
deadmap.put("x-dead-letter-exchange",RabbitConst.deadExchange);
// deadmap.put("x-dead-letter-routing-key",null);//由于deadExchange是fanout类型的所以不需要routing-key
return new Queue("testDirectQue",true,false,false,deadmap);
}
@Bean
Binding directBindDead(){
return BindingBuilder.bind(directQueueBindDead()).to(directExchangeBindDead()).with("test");
}
}
controller
/**
* 发送带有过期时间的消息 (10s)
* 超时未消费的消息则进入死信队列
* @return
*/
@GetMapping("/sendDlx")
@ApiOperation("dlx测试")
public Result sendDlx(){
rabbitTemplate.convertAndSend("testDirectEx","test","hello deadEx test",message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
message.getMessageProperties().setExpiration("10000");
return message;
});
return Result.success("ok");
}
consume和消息确认一起讲
开启手动确认--配置文件需要修改
spring:
rabbitmq: host: localhost port: 5672 username: admin password: admin virtual-host: / listener: #设置监听容器(Listener container)类型,如不设置,将会默认为SimpleRabbitListenerContainerFactory,且下面的direct手动确认配置不生效 type: direct direct: #开启手动确认 acknowledge-mode: manual #是否重试 retry: enabled: true
consume
@RabbitListener(queues = "testDirectQue")
public void testDirectQue(String testMsg, Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// int err = 1/0;//模拟异常
logger.info("testDirectQue消费者收到消息 : {},消费的主题消息来自 : {}",testMsg,message.getMessageProperties().getConsumerQueue());
/**
* 第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
*/
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
logger.info("=====================消费者异常========================");
/**
* message.getMessageProperties().getRedelivered()
* false 表示第一次进队列
* true 表示重入队列
*/
if (message.getMessageProperties().getRedelivered()) {
logger.info("================消息已重复处理失败,拒绝再次接收======================" + testMsg);
/**
* 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列、
* true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
*/
channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
} else {
logger.info("====================消息即将再次返回队列处理=========================" + testMsg);
/**
* requeue为是否重新回到队列,true重新入队
*/
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
}
上述消息确认是在消费者中,其实生产者也有消息确认,当发送到交换机、队列时都有相应的回调函数代码如下
package com.person.chenpt.config.rabbit;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 生产者
* 消息确认
* 可以在回调函数根据需求做对应的扩展或者业务数据处理
* @Author: chenpt
* @Description:
* @Date: Created in 2022-07-28 16:00
* @Modified By:
*/
@Configuration
public class RabbitProductConfirmConfig {
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("ConfirmCallback: "+"相关数据:"+correlationData);
System.out.println("ConfirmCallback: "+"确认情况:"+ack);
System.out.println("ConfirmCallback: "+"原因:"+cause);
}
});
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("ReturnCallback: "+"消息:"+returned.getMessage());
System.out.println("ReturnCallback: "+"回应码:"+returned.getReplyCode());
System.out.println("ReturnCallback: "+"回应信息:"+returned.getReplyText());
System.out.println("ReturnCallback: "+"交换机:"+returned.getExchange());
System.out.println("ReturnCallback: "+"路由键:"+returned.getRoutingKey());
}
});
return rabbitTemplate;
}
}
先记录这些基本使用应该没有问了,后续有知识点再更新记录
作者:
不二尘
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


浙公网安备 33010602011771号