使用消息队列ActiveMQ处理超时订单

264 阅读2分钟

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

背景:

在商城项目中,有这样的需求:针对用户已下单但是一直未去付款的订单做超时处理,因为如果一个商品有库存数量的概念,在用户每次下单时做库存减操作,在用户取消订单时做库存返还操作。这时候为了防止恶意刷库存,就需要针对已下单但是长时间未支付的订单做超时处理,返还库存的操作,此时,就要涉及到了订单超时的处理。

思路:

使用activeMQ的延时投递功能,在创建订单成功之后,向队列中存入订单id,设置延时发送,延时时长为15分钟,此时订单id将会在15分钟后存入到activeMq的消息队列中,然后mq监听器监听到消息,拿到队列中的订单id进行处理,拿到订单id以后去查询该订单是否完成付款,如果未付款,则更新为订单超时交易关闭。

ActiveMq四种策略

  • AMQ_SCHEDULED_DELAY 延迟投递的时间
  • AMQ_SCHEDULED_PERIOD 重复投递的时间间隔
  • AMQ_SCHEDULED_REPEAT 重复投递的次数
  • AMQ_SCHEDULED_CRON Cron表达式

实现:

1、修改activemq配.xml配置文件,启用延时投递。 在activemq安装文件 conf文件下找到activemq.xml:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">

2、项目中配置 applicationContext-activemq.xml

 <!-- 这个是队列 -->
    <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg index="0" value="spring-active-queue"></constructor-arg>
    </bean>

    <!-- 这个是主题 -->
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg index="0" value="spring-active-topic"></constructor-arg>
    </bean>

    <!-- Spring提供的JMS工具类,它可以进行消息发送,接收等 -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="jmsFactory"></property>
        <property name="defaultDestination" ref="destinationQueue"></property>
        <property name="messageConverter">
            <bean class="org.springframework.jms.support.converter.SimpleMessageConverter"></bean>
        </property>
    </bean>

     <!--配置监听程序-->
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsFactory"></property>
        <property name="destination" ref="destinationQueue"></property>
        <!-- 需要实现一个myMessageListener接口 -->
        <property name="messageListener" ref="myMessageListener"></property>
    </bean>

3、创建消息监听类

/**
 * 类描述:ActiveMQ 消息监听器
 * @date:
 */
@Service
public class MyMessageListener implements MessageListener {
    private static final Logger LOGGER = Logger.getLogger(SpringMQ_Produce.class);

//    @Resource
//    private OrderService orderService;

    @Override
    public void onMessage(Message message) {
        try {
            JSONObject object = (JSONObject)((ObjectMessage) message).getObject();
            Long orderId = object.getLong("orderId");
            Integer orderType = object.getInteger("orderType");
            LOGGER.info("MQ接收到的消息:" + orderId + " + " + TimeUtil.nowTime());
            OrderService orderService = SpringContextHolder.getBean("orderService");
            orderService.updateDelayOrder(orderId, orderType);
        } catch (Exception e) {
            LOGGER.error("MessageListener异常:" + MyPubUtil.getError(e));
        }
    }
}

问题:

记录下遇到的问题: 在mq的监听器中,通过@Resource和@Autowired注入service实例失败,出现了个获取不到bean的bug。 查找原因发现spring容器中根本没有完成对注解bean的扫描,因为dispatcher.xml中配置的注解bean的优先级肯定没有框架中的contextListener的优先级高,所以这里获取的实例是null。

解决方法是通过在applicationContext.xml中事先声明一个bean进行使用,实现ApplicationContextAware接口的context注入函数, 将其存入静态变量中。