rabbitmq配置类
config
package cn.tedu.charging.order.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 思考 RabbitConfiguration 配置连接信息 没有配置连接信息
*/
@Configuration
public class RabbitConfiguration {
public static final String EXCHANGE_NAME = "charging_order_exchange";
private static final String QUEUE_NAME = "charging_order_queue";
public static final String ROUTING_KEY = "charging_order_routing_key";
private static final String DEAD_LETTER_EXCHANGE_NAME = "dead_letter_charging_order_exchange";
public static final String DEAD_LETTER_QUEUE_NAME = "dead_letter_charging_order_queue";
private static final String DEAD_LETTER_ROUTING_KEY = "dead_letter_charging_order_routing_key";
@Bean
public DirectExchange orderExchange(){
//消息持久化,
Boolean durable = true;
//自动删除 一次性
Boolean autoDel = false;
return new DirectExchange(EXCHANGE_NAME,durable,autoDel);
}
@Bean
public Queue orderQueue(){
Map<String,Object> args = new HashMap<>();
//设置消息的TTL 存活时间 模拟充电充满需要 2 分钟
Integer ttl = 2 * 60 * 1000;
args.put("x-message-ttl",ttl);
//设置queue的死信Exchange
args.put("x-dead-letter-exchange",DEAD_LETTER_EXCHANGE_NAME);
//设置queue和死信Exchange 绑定 routing_key
args.put("x-dead-letter-routing-key",DEAD_LETTER_ROUTING_KEY);
//惰性队列,在消息很多的时候,把消息存储到磁盘,避免消息积压,占用内存
args.put("x-queue-mode","lazy");
//消息持久化,
Boolean durable = true;
//自动删除 一次性
Boolean autoDel = false;
return new Queue(QUEUE_NAME,durable,true,autoDel,args);
}
/**
* 把queue和Exchange通过routing-key绑定
* @return
*/
@Bean
public Binding orderBinding(){
return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ROUTING_KEY);
}
/**
* 定义死信 Exchange
* @return
*/
@Bean
public DirectExchange deadLetterOrderExchange(){
//消息持久化,
Boolean durable = true;
//自动删除 一次性
Boolean autoDel = false;
return new DirectExchange(DEAD_LETTER_EXCHANGE_NAME,durable,autoDel);
}
/**
* 定义订单死信Queue
* @return
*/
@Bean
public Queue deadLetterOrderQueue(){
//消息持久化 消息不能丢失
Boolean durable = true;
return new Queue(DEAD_LETTER_QUEUE_NAME,durable);
}
/**
* 把死信queue和死信Exchange通过死信routing-key进行绑定
* @return
*/
@Bean
public Binding deadLetterOrderBinding(){
return BindingBuilder.
bind(deadLetterOrderQueue()).
to(deadLetterOrderExchange()).
with(DEAD_LETTER_ROUTING_KEY);
}
}
原生方式 - 发消息
exchange queue routingkey 在发送时的目的
测试方法观察控制台
queues新建一个queue hello-queue里面有消息
/**
* 发消息
*/
@Test
public void publish() throws IOException, TimeoutException {
Connection connection = getConnection();
//通过连接创建Channel
Channel channel = connection.createChannel();
//通过观察 Channel 可以操作 Exchange Queue
//topic 在RabbitMQ 里 是怎么个存在?
String exchange = "hello-exchange";
String routingKey = "routingKey";
String queue = "hello-queue";
AMQP.BasicProperties properties = null;
String message = "hello-rabbit";
try {
//定义 exchange
channel.exchangeDeclare(exchange, BuiltinExchangeType.DIRECT);
//定义了 queue
channel.queueDeclare(queue,true,false,false,null);
//通过routingKey把Exchange和queue进行绑定
channel.queueBind(queue,exchange,routingKey);
channel.basicPublish(exchange,routingKey,properties,message.getBytes());
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("发送成功");
}
private static Connection getConnection() throws IOException, TimeoutException {
//创建连接的工厂 ,工厂模式 设计模式 抽象工厂,简单工厂...
//工厂是用来生产东西的, 连接工厂是用来生产连接的
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setHost("localhost");
factory.setPort(5672); //15672 管理的端口
factory.setUsername("guest");
factory.setPassword("guest");
//通过工厂创建连接
Connection connection = factory.newConnection();
return connection;
}
收消息- 原生方式
执行消费消息 subscribe()观察控制台
public void subscribe() throws IOException,TimeoutException{
//获取连接
Connneciton connection = getConnection();
//通过连接创建channel
Channel channel = connection.createChannel();
String queue = "hello-queue";
DeliverCallback deliverCallback = (consumerTag,message)->{
byte[] body = message.getBody();
String msg = new String(body);
System.out.println(msg);
};
channel.basicConsume(queue,true,deliverCallback,consumerTag->{
});
}
发消息
通过spring提供的api定义Exchange Queue通过RoutingKey绑定
public class RabbitConfiguration{
public static final String EXCHANGE_NAME ="charging_order_exchange";
public static final String QUEUE_NAME = "charging_order_queue";
public static final String ROUTING_KEY ="charging_order_routing_key";
@Bean
public DirectExchange orderExchange(){
//消息持久化
Boolean durable = true;
//自动删除一次性
Boolean autoDel = false;
return new DirectExchange(EXCHANGE_NAME,durable,autoDel);
}
@Bean
public Queue orderQueue(){
//消息持久化,
Boolean durable = true;
//自动删除 一次性
Boolean autoDel = false;
return new Queue(QUEUE_NAME,durable,true,autoDel);
}
/*
把queue 和 exchange 通过routing-key绑定
*/
@Bean
public Binding orderBinding(){
return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ROUTING_KEY);
}
}
RabbitMQOrderProducer
通过Spring RabbitTemplate发送消息
@Component
public class RabbitMQOrderProducer{
@Autowired
private RabbitTemplate rabbitTemplate;
/*发送消息*/
public void sendOrder(String message){
rabbitTemplate.convertAndSend(RabbitConfiguration.EXCHANGE_NAME,
RabbitConfiguration.ROUNTING_KEY,message);
}
}
收消息Spring
通过@RabbitLinstener监听Queue消费消息
@Component
public class RabbitMQOrderConsumer{
@RabbitListener(queues = RabbitConfiguration.QUEUE_NAME)
public void consumerChargingQueue(String msg){
log.debug("消息消息:msg:{}",msg);
}
}
设备充电过程中异常
场景
订单服务给充电桩发送充电指令
startChargin(orderNo , orderAddParam.getPileId(),orderAddParam.getGunId());
正常情况
100w个订单 100W条状态为 充电中 的订单记录
定时任务 要扫描100w条数据
100w订单 每个订单充满电 需要 平均1个小时
每个订单 1 分钟 同步一次数据
问 100W订单 充满 会产生多少数据?
1个订单 1 个小时 60 * 1 = 60 1个订单充满会 60条同步数据 + 1条订单数据
61 * 100w = 6100w
定时扫描缺点
死信队列
消费者不能正常消费
- 消费者消息消费消息了好几次,都不能消费成功,消息会转发到死信队列
- 消息设置了TTL生存时间,同时不设置消费者,超过了生存时间,过期,消息会转发到死信队列
业务集成
配置
配置Exchange 和 Queue
修改RabbitConfiguration
订单Exchange 订单Queue 并通过ROUTINGKEY绑定
死信Exchange 死信 Queue并通过 死信 的RountingKey绑定
订单 Queue和死信 EXCHANGE 绑定
持久化
通过设置durable为true Exchange和queue持久化消息不能丢失
设置消息相关参数
设置订单queue消息过期后,转发到死信队列
x-dead-letter-exchange 设置消息的死信exchange
x-dead-letter-routing-key设置消息的死信routingkey
x-message-ttl 设置生存时间
/*
rabbit-mq 配置类
配置连接 连接信息 ,用户名,密码
配置 交换机 Exchange 队列Queue 绑定 通过RoutingKey
*/
private static final String DEAD_LETTER_EXCHANGE_NAME="dead_letter_charging_order_exchange";
private static final String DEAD_LETTER_QUEUE_NAME="dead_letter_charging_order_queue";
private static final String DEAD_LETTER_ROUNTING_KEY="dead_letter_charging_order_routing_key";
/*定义正常订单queue*/
public Queue orderQueue(){
Map<String,Object> args = new HashMap<>();
//设置消息的TTL, 存活时间 模拟充电满需要2分钟
Integer ttl = 2 * 60 * 100;
args.put("x-message-ttl",ttl);
//设置queue的死信EXCHANGE
args.put("x-dead-letter-exchange",DEAD_LETTER_EXCHANGE_NAME);
//设置queue和死信exchange 绑定routingkey
args.put("x-dead-letter-routing-key",DEAD_LETTER_ROUTING_KEY);
//惰性队列,在消息很多的时候,把消息存储到磁盘,避免消息积压,占用内存
args.put("x-queue-mode","lazy");
//消息持久化 消息不能丢失
Boolean durable = true;
return new Queue(QUEUE_NAME,durable,true,false,args);
}
//订单死信交换机
//订单死信队列
//把 死信 交换机 和死信队列通过routingkey绑定
定义死心队列的消费者
死信队列的消费者 不是正常队列的消费者
@RabbitListener(queues = RabbitCofiguration.DEAD_LETTER_QUEUE_NAME)
@Componenet
public class RabbitMQOrderConsumer{
@RabbitListener(queues=RabbitConfiguration.DEAD_LETTER_QUEUE_NAME)
public void consumerChargingQueue(OrderMQPO orderMQPO,Message message,Channel channel){
log.debug("消费死信队列消息:{}",orderMQPO);
}
}
订单服务业务集成
修改 OrderServiceImpl # createOrder
注入RabbitMQOrderProducer
在OrderServiceImpl 注入 RabbitMQOrderProducer
@Autowired
private RabbitMQOrderProducer rabbitMQOrderProducer;
定义sendOrderMessage
定义发送消息方法sendOrderMessage
/*
给RabbitMQ发送延迟消息 ,用来处理设备故障导致的超时订单
*/
private void sendOrderMessage(String orderNo){
OrderMQPO orderMQPO = new OrderMQPO();
orderMQPO.setOrderNo(orderNo);
rabbitMQOrderProducer.sendOrder(orderMQPO);
}
定义消息OrderMQPO
订单服务和MQ传输的数据对象 用DTO可以
订单服务发送给 MQ MQ需要存储消息PO 可以
需要实现序列化
implements Serializable
接口没有任何方法 叫做 标记接口
@Data
@FieldDefaults(level=AccessLevel.PRIVATE)
public class OrderMQPO implements Serializable{
//订单编号
String orderNo;
}
在创建订单方法 发送消息
创建订单
创建订单号
Feign 获取场站的信息和设备信息(服务之间的调用 订单服务调用设备服务
Feign 获取用户信息(车辆信息) (服务之间的调用 订单服务调用用户服务)
Feign 修改充电器的状态为 使用中 (服务之间的调用 订单服务调用设备服务)
物联网 给设备发送开始充电指令(物联网
public String createOrder(OrderAddParam orderAddParam){
//1生成订单号
String orderNo = getOrderNo();
//2 获取场站的信息和设备信息
StationInfoVO stationInfoVO = getStationInfoByGunId(orderAddParam.getGunId());
//3获取用户信息(车辆信息)
UserInfoVO userInfoVO = getUserInfoByUserId(orderAddParam.getUserId());
//4 修改充电枪 的状态为 使用中
Boolean success = updateGunStatusBusy(orderAddParam.getGunId());
//5 给设备发送开始充电指令
startCharging(orderNo,orderAddParam.getPileId(),orderAddParam.getGunId());;
//延迟处理
sendOrderMessage(orderNo);
return orderNo;
}
给不同类型的Exchange发送消息,观察控制台,查看消息发送的结果
basicPublic调整入参
exchange
routingKey
channel.basicPublish(exchange,routingKey,properties,message.getBytes());
queue截图
保证Queue里有消息, Messages里 消息数量不能为0