Springboot-cli 开发脚手架系列
文章目录
简介
- Springboot整合RabbitMQ,消息者及生成者完整案例
包含多种队列使用案例
- 普通队列
- 延迟队列
- 通配符队列
- topic队列
- 临时队列
- 死信队列
RabbitMQ参数说明
x-message-ttl
发送到队列的消息在丢弃之前可以存活多长时间(毫秒)。x-expires
队列在被⾃动删除(毫秒)之前可以使⽤多长时间。x-max-length
队列在开始从头部删除之前可以包含多少就绪消息。x-max-length-bytes
队列在开始从头部删除之前可以包含的就绪消息的总体⼤⼩。x-dead-letter-exchange
设置队列溢出⾏为。这决定了在达到队列的最⼤长度时消息会发⽣什么。有效值为drop-head或
reject-publish。交换的可选名称,如果消息被拒绝或过期,将重新发布这些名称。x-dead-letter-routing-key
可选的替换路由密钥,⽤于在消息以字母为单位时使⽤。如果未设置,将使⽤消息的原始路由密钥。x-max-priority
队列⽀持的最⼤优先级数;如果未设置,队列将不⽀持消息优先级。x-queue-mode
将队列设置为延迟模式,在磁盘上保留尽可能多的消息以减少内存使⽤;如果未设置,队列将保留内存缓存以尽
快传递消息。x-queue-master-locator
将队列设置为主位置模式,确定在节点集群上声明时队列主机所在的规则
1. 环境
- 已安装RabbitMQ且启用延时插件(简介有安装教程)
docker安装RabbitMQ及延时插件安装详细教程 pom.xml
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. 常量配置-定义队列
- 队列定义
public class RabbitDefine {
/**
* 普通交换机
*/
public final static String DIRECT_EXCHANGE = "direct.exchange";
/**
* 普通队列
*/
public final static String DIRECT_QUEUE = "direct.queue";
/**
* 延迟队列交换机
*/
public final static String DELAY_EXCHANGE = "delay.exchange";
/**
* 延迟队列
*/
public final static String DELAY_QUEUE = "delay.queue";
/**
* 通配符交换机
*/
public final static String FANOUT_EXCHANGE = "fanout.exchange";
/**
* 通配符队列
*/
public final static String FANOUT_QUEUE = "fanout.queue";
/**
* topic 交换器
*/
public final static String TOPIC_EXCHANGE = "topic.exchange";
/**
* topic队列1
*/
public final static String TOPIC_QUEUE_ONE = "topic.queue.one";
/**
* topic队列2
*/
public final static String TOPIC_QUEUE_TWO = "topic.queue.two";
/**
* 临时队列交换器
*/
public final static String TTL_EXCHANGE = "ttl.exchange";
/**
* 临时队列
*/
public final static String TTL_QUEUE = "ttl.queue";
/**
* 死信交换机
*/
public final static String DEAD_EXCHANGE = "dead.exchange";
/**
* 死信队列
*/
public final static String DEAD_QUEUE = "dead.queue";
}
3. 提供者
环境配置
开启手动提交ack消息模式
spring:
application:
name: rabbitmq-producer
rabbitmq:
host: 192.168.41.128
port: 5672
username: guest
password: guest
virtualHost: /
# 手动提交ack消息
listener:
simple:
acknowledge-mode: manual
direct:
acknowledge-mode: manual
3.1 direct消息队列配置
- Direct 类型的交换器由路由规则很简单,它会把消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中。Direct Exchange 是 RabbitMQ 默认的交换器模式,也是最简单的模式。它根据 RoutingKey 完全匹配去寻找队列。
@Configuration
public class RabbitDirectConfig {
/**
* 创建direct Exchange交换机也叫完全匹配交换机
*/
@Bean
public DirectExchange directExchange() {
return ExchangeBuilder
.directExchange(RabbitDefine.DIRECT_EXCHANGE)
// 开启持久化
.durable(true)
.build();
}
/**
* 普通队列
*/
@Bean
public Queue directQueue() {
return QueueBuilder
.durable(RabbitDefine.DIRECT_QUEUE)
.build();
}
/**
* 交换机绑定队列
*/
@Bean
public Binding directBinding() {
return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
}
}
3.2 通配符队列
- 发送到交换机的消息会被转发到与该交换机绑定的所有队列上。消息广播。Fanout交换机转发消息是最快的。
@Configuration
public class RabbitFanoutConfig {
/**
* 创建Fanout Exchange交换机也叫通配符交换机
*/
@Bean
public FanoutExchange fanoutExchange() {
return ExchangeBuilder
.fanoutExchange(RabbitDefine.FANOUT_EXCHANGE)
// 开启持久化
.durable(true)
.build();
}
/**
* 创建队列
*/
@Bean
public Queue fanoutQueue() {
return QueueBuilder
.durable(RabbitDefine.FANOUT_QUEUE)
.build();
}
/**
* 确定绑定关系,队列和交换机绑定
*/
@Bean
public Binding fanoutBinding() {
return BindingBuilder.bind(fanoutQueue()).to(fanoutExchange());
}
}
3.3 Topic队列
- topic与direct类型的交换器类似,也是将消息路由到RoutingKey与BindingKey匹配的队列中,但它不是完全匹配,而是模糊匹配。
- 规则:#用于匹配一个单词,用于匹配多个单词(可以是0个);例如:order..current。
@Configuration
public class RabbitTopicConfig {
/**
* 创建Topic Exchange交换机也叫模糊匹配交换机
*/
@Bean
public TopicExchange topicExchange() {
return ExchangeBuilder
.topicExchange(RabbitDefine.TOPIC_EXCHANGE)
// 开启持久化
.durable(true)
.build();
}
/**
* 创建队列 1
*/
@Bean
public Queue topicOneQueue() {
return QueueBuilder
.durable(RabbitDefine.TOPIC_QUEUE_ONE)
.build();
}
/**
* 创建队列 2
*/
@Bean
public Queue topicTwoQueue() {
return QueueBuilder
.durable(RabbitDefine.TOPIC_QUEUE_TWO)
.build();
}
/**
* 确定绑定关系
* #用于匹配一个单词 *用于匹配多个单词
*/
@Bean
public Binding topicOneBinding(@Qualifier("topicOneQueue") Queue topicQueue, @Qualifier("topicExchange") TopicExchange topicExchange) {
return BindingBuilder.bind(topicQueue).to(topicExchange).with("#.queue.one");
}
@Bean
public Binding topicTwoBinding(@Qualifier("topicTwoQueue") Queue topicQueue, @Qualifier("topicExchange") TopicExchange topicExchange) {
return BindingBuilder.bind(topicQueue).to(topicExchange).with("#.queue.*");
}
}
3.4 延迟队列
- 消息延期到达
@Configuration
public class RabbitDelayConfig {
/**
* 创建延时交换机
*/
@Bean
public DirectExchange delayExchange() {
return ExchangeBuilder
.directExchange(RabbitDefine.DELAY_EXCHANGE)
// 开启延时
.delayed()
// 开启持久化
.durable(true)
.build();
}
/**
* 队列
*/
@Bean
public Queue delayQueue() {
return QueueBuilder
.durable(RabbitDefine.DELAY_QUEUE)
.build();
}
/**
* 交换机绑定队列
*/
@Bean
public Binding delayBinding() {
return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay");
}
}
3.5 死信队列
- 当消息过期时,消息进入死信队列
@Configuration
public class RabbitDeadConfig {
/**
* 死信交换机
*
* @return FanoutExchange
*/
@Bean
public DirectExchange deadExchange() {
return ExchangeBuilder.directExchange(RabbitDefine.DEAD_EXCHANGE)
// 开启持久化
.durable(true)
// 所有消费者都解除订阅此队列,autoDelete=true时,此队列会自动删除
.autoDelete()
.build();
}
/**
* 死信队列
*
* @return Queue
*/
@Bean
public Queue deadQueue() {
return QueueBuilder.durable(RabbitDefine.DEAD_QUEUE).build();
}
/**
* 绑定
*
* @return Binding
*/
@Bean
public Binding deadBinding(@Qualifier("deadExchange") DirectExchange deadExchange, @Qualifier("deadQueue") Queue deadQueue) {
return BindingBuilder.bind(deadQueue).to(deadExchange).with("dead");
}
}
3.6 临时队列
- 消息设置过期时间,过期消息转入死信队列
@Configuration
public class RabbitTtlDirectConfig {
/**
* 创建交换机
*/
@Bean
public DirectExchange directTtlExchange() {
return ExchangeBuilder
.directExchange(RabbitDefine.TTL_EXCHANGE)
// 开启持久化
.durable(true)
// 所有消费者都解除订阅此队列,autoDelete=true时,此交换机会自动删除
//.autoDelete()
.build();
}
/**
* 会过期的队列
*/
@Bean
public Queue directTtlQueue() {
return QueueBuilder.
durable(RabbitDefine.TTL_QUEUE)
// 设置队列的过期时间为10秒
.ttl(10000)
// 配置消息过期后的处理者(死信队列交换机)
.deadLetterExchange(RabbitDefine.DEAD_EXCHANGE)
// 死信队列路由
.deadLetterRoutingKey("dead")
// 所有消费者都解除订阅此队列,autoDelete=true时,此交换机会自动删除
// .autoDelete()
.build();
}
@Bean
public Binding directTtlBinding() {
return BindingBuilder.bind(directTtlQueue()).to(directTtlExchange()).with("test");
}
}
4. 消息发送测试
- 消息提供者
@Component
@RequiredArgsConstructor
@Slf4j
public class RabbitProducer {
private final RabbitTemplate rabbitTemplate;
/**
* 直接模式发送消息
*
* @param message 发送的信息
*/
public void sendDirect(Object message) {
rabbitTemplate.convertAndSend(RabbitDefine.DIRECT_QUEUE, message, this.getCorrelationData());
}
/**
* 分裂模式发送消息
*
* @param message 发送的信息
*/
public void sendFanout(Object message) {
rabbitTemplate.convertAndSend(RabbitDefine.FANOUT_EXCHANGE, "", message);
}
/**
* 主题模式发送消息
*
* @param message 发送的信息
* @param routingKey 匹配的队列名
*/
public void sendTopic(Object message, String routingKey) {
rabbitTemplate.convertAndSend(RabbitDefine.TOPIC_EXCHANGE, routingKey, message);
}
/**
* 发送延迟消息
*
* @param message 发送的信息
* @param delay 延迟时间ms
*/
public void sendDelay(String message, int delay) {
rabbitTemplate.convertAndSend(RabbitDefine.DELAY_EXCHANGE, "delay", message, msg -> {
msg.getMessageProperties().setDelay(delay);
return msg;
});
}
/**
* 发送临时消息10s过期
*/
public void sendAndExpire(Object message) {
rabbitTemplate.convertAndSend(RabbitDefine.TTL_QUEUE, message, this.getCorrelationData());
}
/**
* 生成消息标识
*/
private CorrelationData getCorrelationData() {
String messageId = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData();
correlationData.setId(messageId);
return correlationData;
}
}
- 编写测试方法
@SpringBootTest
@Slf4j
public class ProducerTest {
@Autowired
RabbitProducer rabbitProducer;
@Test
void sendDirect() {
rabbitProducer.sendDirect("直通消息9527!");
}
@Test
void sendFanout() {
rabbitProducer.sendFanout("分裂消息6666!");
}
@Test
void sendAndExpire() {
rabbitProducer.sendAndExpire("晚上10点老地方不见不散!该消息有效期10秒");
}
@Test
void sendDelay() {
rabbitProducer.sendDelay("有内鬼,终止交易~~", 5000);
}
@Test
void sendTopic() {
rabbitProducer.sendTopic("放学别走!", "test123.topic.test456");
}
}
5. 消费者
- yml配置
spring:
application:
name: rabbitmq-consumer
rabbitmq:
host: 192.168.41.128
port: 5672
username: guest
password: guest
virtualHost: /
# 手动提交ack消息
listener:
simple:
acknowledge-mode: manual
direct:
acknowledge-mode: manual
- 注解方式实现消息监听
@Component
@Slf4j
public class RabbitConsumer {
/**
* 普通消息
*/
@RabbitListener(queuesToDeclare = @Queue(RabbitDefine.DIRECT_QUEUE))
public void consumer1(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者1(直通消息)-队列索引:{},接收到消息:{}", tag, message);
channel.basicAck(tag, false);
}
/**
* 分裂消息
*/
@RabbitListener(queuesToDeclare = @Queue(RabbitDefine.FANOUT_QUEUE))
public void consumer2(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者2(分裂消息)-队列索引:{},接收到消息:{}", tag, message);
channel.basicAck(tag, false);
}
/**
* 临时消息
*/
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue(value = RabbitDefine.TTL_QUEUE,
arguments = {
// 指定一下死信交换机
@Argument(name = "x-dead-letter-exchange", value = RabbitDefine.DEAD_EXCHANGE),
// 指定死信交换机的路由key
@Argument(name = "x-dead-letter-routing-key", value = "dead"),
// 指定队列的过期时间
@Argument(name = "x-message-ttl", value = "10000", type = "java.lang.Long")
}),
exchange = @Exchange(name = RabbitDefine.TTL_EXCHANGE),
key = "test"
)
})
public void consumer3(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者3(临时消息)-队列索引:{},接收到消息:{}", tag, message);
channel.basicAck(tag, false);
}
/**
* 延时消息
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = RabbitDefine.DELAY_QUEUE),
exchange = @Exchange(name = RabbitDefine.DELAY_EXCHANGE, delayed = "true"),
key = "delay"))
public void consumer4(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者4(延时队列)-队列索引:{},接收到消息:{}", tag, message);
try {
// 接收成功
channel.basicAck(tag, false);
} catch (Exception e) {
log.error("发生异常,消息签收失败");
// 第三个参数 requeue = true为将消息重返当前消息队列,还可以重新发送给消费者
channel.basicNack(tag, false, true);
}
}
/**
* topic消息1
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = RabbitDefine.TOPIC_QUEUE_ONE, autoDelete = "false", durable = "true"),
exchange = @Exchange(value = RabbitDefine.TOPIC_EXCHANGE, type = ExchangeTypes.TOPIC),
key = "#.topic.#"
))
public void consumer5(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者5(topic模式one)-队列索引:{},接收到消息:{}", tag, message);
channel.basicAck(tag, false);
}
/**
* topic消息2
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = RabbitDefine.TOPIC_QUEUE_TWO, autoDelete = "false", durable = "true"),
exchange = @Exchange(value = RabbitDefine.TOPIC_EXCHANGE, type = ExchangeTypes.TOPIC),
key = "#.topic.#"
))
public void consumer6(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者6(topic模式two)-队列索引:{},接收到消息:{}", tag, message);
channel.basicAck(tag, false);
}
/**
* 死信队列消息
*/
@RabbitListener(queuesToDeclare = @Queue(RabbitDefine.DEAD_QUEUE))
public void consumer7(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
log.info("消费者7(死信队列)-队列索引:{},接收到过期消息:{}", tag, message);
channel.basicAck(tag, false);
}
}
6. 效果演示
- 先启动消息提供者,初始化一下队列
- 再启动消费者
- 运行测试用例
7. 源码分享
本项目已收录
Springboot、SpringCloud全家桶教程+源码,各种常用框架使用案例都有哦,具备完善的文档,致力于让开发者快速搭建基础环境并让应用跑起来,并提供丰富的使用示例供使用者参考,快来看看吧。