RabbitMQ 结构
- 生产者
- 消费组
- 交换机 四种交换机类型
direct 点对点
fanout 广播
topic 订阅发布
heanders 废弃
常用topic - 绑定
- 队列
- 消费者
- 连接 tcp长连接
- 信道
- 消息:消息头 ,消息体 ,路由键 routing key
- 消息建 存到哪个队列交换机 交换标识
- virtual 虚拟环境 默认 / 用于跨平台不同环境 或不同语言
- Erlang 编写 rabbit mq 语言
运行一个 rabbitmq
docker run -d --name rabbitmq
-p 5671:5671
-p 5672:5672
-p 4369:4369
-p 25672:25672
-p 15671:15671
-p 15672:15672
rabbitMQ:image
- 4369,25672 Erlang 发现 集群端口
- 5672,5671 AMQP端口
- 1572 web 管理后台
- 61613,61614 STMP 协议端口
- 1883,8883 MQTT 协议端口
访问后台
192.168.70.15 (机器ip).15672
默认账号密码
账号 密码 : guest
模板
SpringBoot 提供jms 模板 提供 RabbitMq 模板 直接使用 @EnabelRabbit
@RabbitListener
<!-- rabbit mq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
#rabbitmq连接参数
spring.rabbitmq.host=192.168.70
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#rabbitmq消息确认
spring.rabbitmq.publisher-confirms=true
rabbitmq:
host: rabbitmq
port: 5672
virtual-host: /
username: guest
password: guest
#确认消息已发送到交换机(Exchange) 发送确认
#publisher-confirms: true
publisher-confirm-type: correlated
#确认消息已发送到队列(Queue) 发送回调
publisher-returns: true
listener:
simple:
# 默认自动提交 手动提交 manual 需要手动提交 channel.basicAck
acknowledge-mode: auto
# 海量数据过来一次读取几个mq信息
prefetch: 1
retry:
# rabbit 自带重试机制打开
enabled: true
# 重试次数
max-attempts: 3
# 最大间隔重试时间 单位毫秒
max-interval: 20000
# 重试间隔时间 3秒一次
initial-interval: 3000
# 重试间隔递增 第一次: 3秒 第二次 2*3=6 2*6=12 12*2=24
multiplier: 2
# 重试超过一定时间时间后是否丢弃 false 如果丢弃需要开发死信队列绑定 会放到死信队里面
default-requeue-rejected: false
基础配置
也可以不配置
package com.after.sale.biz.configs.rabbit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import javax.annotation.Resource;
/**
* FileName: RabbitBasicConfig.java
* Author: 徐寿春
* Date: 2023/5/11 16:18
* <p>
* 名称: Rabbit基础配置
*/
@Slf4j
@Configuration
public class RabbitBasicConfig {
@Resource
RabbitProperties rabbitProperties;
/**
* 创建链接
*
* @author 徐寿春
* 2023/5/10 18:12
*/
@Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitProperties.getHost(), rabbitProperties.getPort());
connectionFactory.setUsername(rabbitProperties.getUsername());
connectionFactory.setPassword(rabbitProperties.getPassword());
connectionFactory.setVirtualHost(rabbitProperties.getVirtualHost());
return connectionFactory;
}
/**
* 生成rabbitMQ 设置全局作用域单利
* <p>
* 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
* template.setMandatory(true);
*
* @author 徐寿春
* 2023/5/10 18:12
*/
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
}
订阅发布
package com.after.sale.biz.configs.rabbit;
import com.after.sale.common.basics.RabbitMqKey;
import lombok.extern.slf4j.Slf4j;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* FileName: TopicExchangeConfig.java
* Author: 徐寿春
* Date: 2023/5/11 16:25
* <p>
* 名称: 订阅发布 根据routerkey 完全匹配消费 配置
*/
@Slf4j
@Configuration
public class TopicExchangeConfig {
/**
* 创建队列
* Queue 可以有4个参数
* String name: 队列名
* boolean durable: 持久化消息队列,rabbitmq 重启的时候不需要创建新的队列,默认为 true
* boolean exclusive: 表示该消息队列是否只在当前的connection生效,默认为 false
* boolean autoDelete: 表示消息队列在没有使用时将自动被删除,默认为 false
* Map<String, Object> arguments:
* <p>
* <p>
* durable 持久性–如果我们正在声明一个持久性队列,则为true(该队列将在服务器重新启动后幸存)
*/
@Bean(name = "topicQueueA")
public Queue queue() {
// 队列持久化
return new Queue(RabbitMqKey.TRADE_ORDER_TOPIC_QUEUE_A, true);
}
@Bean(name = "topicQueueB")
public Queue queue1() {
// 队列持久化
return new Queue(RabbitMqKey.TRADE_ORDER_TOPIC_QUEUE_B, true);
}
@Bean(name = "topicExchange")
public TopicExchange topicExchange() {
return new TopicExchange(RabbitMqKey.TRADE_ORDER_TOPIC_EXCHANGE);
}
@Bean
Binding topicExchangeBinder(@Qualifier("topicQueueA") Queue queue,
@Qualifier("topicExchange") TopicExchange topicExchange) {
return BindingBuilder.bind(queue).to(topicExchange).with(RabbitMqKey.TRADE_ORDER_TOPIC_QUEUE_ROUTING_KEY_A);
}
@Bean
Binding topicExchangeBinder1(@Qualifier("topicQueueB") Queue queue,
@Qualifier("topicExchange") TopicExchange topicExchange) {
return BindingBuilder.bind(queue).to(topicExchange).with(RabbitMqKey.TRADE_ORDER_TOPIC_QUEUE_ROUTING_KEY_B);
}
}
死信
package com.after.sale.biz.configs.rabbit;
import com.after.sale.common.basics.RabbitMqDelayKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* FileName: DirectExchange.java
* Author: 徐寿春
* Date: 2023/5/11 16:32
* <p>
* 名称: 通配交换机配置 --> 装配为延时队列
*/
@Configuration
public class DirectExchangeConfig {
/**
* 创建延时队列
*
* @author 徐寿春
* 2023/5/11 16:56
*/
@Bean("delayExchange")
public DirectExchange delayExchange() {
return new DirectExchange(RabbitMqDelayKey.DELAY_EXCHANGE_NAME);
}
/**
* 声明延时队列A 延时10s
* 并绑定到对应的死信交换机
* x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
* x-dead-letter-routing-key 这里声明当前队列的死信路由key
* x-message-ttl 声明队列的TTL 单位毫秒 1000 等于1秒
* @author 徐寿春
* 2023/5/11 16:56
*/
@Bean("delayQueueA")
public Queue delayQueueA() {
Map<String, Object> args = new HashMap<>(2);
args.put("x-dead-letter-exchange", RabbitMqDelayKey.DEAD_LETTER_EXCHANGE);
args.put("x-dead-letter-routing-key", RabbitMqDelayKey.DEAD_LETTER_QUEUE_A_ROUTING_KEY);
args.put("x-message-ttl", 6000);
return QueueBuilder.durable(RabbitMqDelayKey.DELAY_QUEUE_NAME_A).withArguments(args).build();
}
@Bean("delayQueueB")
public Queue delayQueueB() {
Map<String, Object> args = new HashMap<>(2);
args.put("x-dead-letter-exchange", RabbitMqDelayKey.DEAD_LETTER_EXCHANGE);
args.put("x-dead-letter-routing-key", RabbitMqDelayKey.DEAD_LETTER_QUEUE_B_ROUTING_KEY);
args.put("x-message-ttl", 60000);
return QueueBuilder.durable(RabbitMqDelayKey.DELAY_QUEUE_NAME_B).withArguments(args).build();
}
/**
* 绑定延时队列和交换机
*
* @author 徐寿春
* 2023/5/11 17:00
*/
@Bean
public Binding delayBindingA(@Qualifier("delayQueueA") Queue queue,
@Qualifier("delayExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(RabbitMqDelayKey.DELAY_QUEUE_A_ROUTING_KEY);
}
@Bean
public Binding delayBindingB(@Qualifier("delayQueueB") Queue queue,
@Qualifier("delayExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(RabbitMqDelayKey.DELAY_QUEUE_B_ROUTING_KEY);
}
//----------------------------------------------------- 死信队列 -----------------------------------------------------
/**
* 声明死信交换机
*
* @author 徐寿春
* 2023/5/11 17:11
*/
@Bean("deadLetterExchange")
public DirectExchange deadLetterExchange() {
return new DirectExchange(RabbitMqDelayKey.DEAD_LETTER_EXCHANGE);
}
/**
* 声明死信交换机队列
*
* @author 徐寿春
* 2023/5/11 17:11
*/
@Bean("deadLetterQueueA")
public Queue deadLetterQueueA() {
return new Queue(RabbitMqDelayKey.DEAD_LETTER_QUEUE_A);
}
@Bean("deadLetterQueueB")
public Queue deadLetterQueueB() {
return new Queue(RabbitMqDelayKey.DEAD_LETTER_QUEUE_B);
}
@Bean
public Binding deadLetterBindingA(@Qualifier("deadLetterQueueA") Queue queue,
@Qualifier("deadLetterExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(RabbitMqDelayKey.DEAD_LETTER_QUEUE_A_ROUTING_KEY);
}
@Bean
public Binding deadLetterBindingB(@Qualifier("deadLetterQueueB") Queue queue,
@Qualifier("deadLetterExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(RabbitMqDelayKey.DEAD_LETTER_QUEUE_B_ROUTING_KEY);
}
}
扇形直连
package com.after.sale.biz.configs.rabbit;
import com.after.sale.common.basics.RabbitMqKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* FileName: QueueExchangeConfig.java
* Author: 徐寿春
* Date: 2023/5/11 16:17
* <p>
* 名称: 点对点 扇形交换机配置
*/
@Slf4j
@Configuration
public class QueueExchangeConfig {
@Bean(name = "simpleQueue")
public Queue createSimpleQueue() {
// 队列持久化
return new Queue(RabbitMqKey.TRADE_ORDER_QUEUE, true);
}
/**
* 扇形交换机 , 平坦到所有队列,消费方轮询消费信息
*
* @author 徐寿春
* 2023/5/11 16:23
*/
@Bean(name = "fanoutExchange")
public FanoutExchange fanoutExchange() {
return new FanoutExchange(RabbitMqKey.TRADE_ORDER_EXCHANGE);
}
/**
* 绑定订阅发布交换机
*
* @author 徐寿春
* 2023/5/11 15:13
*/
@Bean
Binding fanoutExchangeBinder(@Qualifier("simpleQueue") Queue queue,
@Qualifier("fanoutExchange") FanoutExchange topicExchange) {
return BindingBuilder.bind(queue).to(topicExchange);
}
}
确认机制
package com.after.sale.biz.configs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* FileName: RabbitAck.java
* Author: 徐寿春
* Date: 2023/5/11 09:17
* <p>
* 名称:
*/
@Configuration
@Slf4j
public class RabbitAckConfig implements RabbitTemplate.ConfirmCallback {
@Resource
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init() {
//指定 ConfirmCallback
//rabbitTemplate如果为单例的话,那回调就是最后设置的内容
rabbitTemplate.setConfirmCallback(this);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info(String.format("ACK --- MQ message id: {}%s", correlationData));
if (ack) {
log.info("ACK --- Message sent confirmation success!");
} else {
log.info("ACK --- MQ message id: {}", correlationData.getId());
log.info("ACK --- MQ confirmetion: {}", ack);
log.info("ACK --- Message sending confirmation failed, reason for failure:" + cause);
}
}
}
监听
@Component
@RabbitListener(queues = "queue")
public class HelloReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("Receiver1 : " + hello);
}