【MQ】酒店项目实战 - 下订单通知消息

400 阅读2分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战

一、前言

先来数据互联网酒店的业务。 再从订单入手,分析个订单场景。

互联网酒店下单业务流程:

  1. 客户查询酒店房间
  2. 预定房间
  3. 付款
  4. 更新优惠券状态
  5. 推送微信消息

下单流程.png

下单功能:预定、付款、生成订单信息、更新优惠券信息、推送微信通知、后台查看订单详情。

下单流程可分为:

  • 同步方式下单:上面就是
  • 异步方式下单:如下

异步方式下单,如图:

通过加入消息队列(RocketMQ)来进行异步话。

系统只需要发送一个订单创建的消息到 RocketMQ 中,推送微信通知的功能就交给消费服务去执行了。

不用同步等待推送微信消息成功后才返回结果,来提高系统的吞吐量。

异步下单.png



二、代码实战

代码部分分为:

  • 优惠券服务(CouponService
  • 订单服务(OrderService

订单服务(OrderService):创建订单

  • 保存订单数据
  • 保存订单商品数据
  • 更新优惠券状态

配置类如下:

@Configuration
public class OrderProducerConfiguration {

   @Value("${rocketmq.namesrv.address}")
   private String namesrvAddress;

   @Value("${rocketmq.order.producer.group}")
   private String orderProducerGroup;

   @Value("${rocketmq.order.finished.producer.group}")
   private String orderPayProducerGroup;

   /**
    * 订单消息生产者
    *
    * @return 订单消息rocketmq的生产者对象
    */
   @Bean(value = "orderMqProducer")
   public DefaultMQProducer orderMqProducer() throws MQClientException {
       DefaultMQProducer producer = new DefaultMQProducer(orderProducerGroup);
       producer.setNamesrvAddr(namesrvAddress);
       producer.start();
       return producer;
   }
}

订单服务如下:

@Service
public class OrderServiceImpl implements OrderService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
    /**
     * TODO 正常获取酒店房间数据 应该调用酒店服务的rpc接口 由于没分模块则本地调用
     */
    @Autowired
    private HotelRoomService hotelRoomService;

    /**
     * 订单事件通知管理组件
     */
    @Autowired
    private OrderEventInformManager orderEventInformManager;
    /**
     * mysql dubbo服务
     */
    @Reference(version = "1.0.0",
            interfaceClass = MysqlApi.class,
            cluster = "failfast")
    private MysqlApi mysqlApi;
    /**
     * redis dubbo服务
     */
    @Reference(version = "1.0.0",
            interfaceClass = RedisApi.class,
            cluster = "failfast")
    private RedisApi redisApi;
    /**
     * TODO 本质上是走rpc远程调用 这里由于没拆分模块即本地调用
     */
    @Autowired
    private CouponService couponService;
    /**
     * 完成定时事务消息topic
     */
    @Value("${rocketmq.order.finished.topic}")
    private String orderFinishedTopic;

    /**
     * 完成订单 下发权益消息事务消息
     */
    @Autowired
    @Qualifier(value = "orderFinishedTransactionMqProducer")
    private TransactionMQProducer orderFinishedTransactionMqProducer;

    @Override
    public CommonResponse<CreateOrderResponseDTO> createOrder(OrderInfoDTO orderInfoDTO) {
        // TODO 1.校验库存 由于我们后台系统配置为不减库存 这里库存不做校验

        // TODO 可以通过状态模式来校验订单的流转和保存订单操作日志
        // 保存订单数据
        this.saveOrderInfo(orderInfoDTO);

        // 保存订单商品数据
        this.saveOrderItemInfo(orderInfoDTO);

        // 调用优惠券服务更新优惠券状态:修改优惠券状态
        couponService.usedCoupon(orderInfoDTO.getId(), orderInfoDTO.getCouponId(), orderInfoDTO.getPhoneNumber());

        // 发送订单消息到mq中
        orderEventInformManager.informCreateOrderEvent(orderInfoDTO);

        CreateOrderResponseDTO createOrderResponseDTO = new CreateOrderResponseDTO();
        createOrderResponseDTO.setOrderNo(orderInfoDTO.getOrderNo());
        createOrderResponseDTO.setOrderId(orderInfoDTO.getId());
        return CommonResponse.success(createOrderResponseDTO);
    }
}

通知订单消息:

@Service
public class OrderEventInformManagerImpl implements OrderEventInformManager {
    private static final Logger LOGGER = 
        LoggerFactory.getLogger(OrderEventInformManagerImpl.class);

    @Autowired
    @Qualifier(value = "orderMqProducer")
    private DefaultMQProducer orderMqProducer;

    /**
     * 订单消息topic
     */
    @Value("${rocketmq.order.topic}")
    private String orderTopic;
    
    @Override
    public void informCreateOrderEvent(OrderInfoDTO orderInfoDTO) {
        // 订单状态顺序消息
        this.sendOrderMessage(MessageTypeEnum.WX_CREATE_ORDER, orderInfoDTO);
    }
    /**
     * 发送订单消息
     *
     * @param messageTypeEnum 订单消息类型
     */
    private void sendOrderMessage(MessageTypeEnum messageTypeEnum, 
                                  OrderInfoDTO orderInfoDTO) {
        OrderMessageDTO orderMessageDTO = new OrderMessageDTO();
        orderMessageDTO.setMessageType(messageTypeEnum);
        orderMessageDTO.setContent(JSON.toJSONString(orderInfoDTO));
        Message message = new Message();
        message.setTopic(orderTopic);
        message.setBody(JSON.toJSONString(orderMessageDTO)
                        .getBytes(StandardCharsets.UTF_8));
        try {
            orderMqProducer.send(message, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, 
                                           Object orderId) {
                    // 订单id
                    Integer id = (Integer) orderId;
                    int index = id % mqs.size();
                    return mqs.get(index);
                }
            }, orderInfoDTO.getId());
        } catch (Exception e) {
            // 发送订单消息失败
            LOGGER.error("send order message fail,error message:{}", e.getMessage());
        }
    }
}