rabbitmq死信队列,订单队列

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

durable 持久化

transient 及时保存 : 在宕机后就消失了

猜你喜欢

转载自blog.csdn.net/m0_68935893/article/details/143079789