V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wayn111
V2EX  ›  程序员

下单流程解耦新方案-Spring 事件监听机制

  •  
  •   wayn111 ·
    wayn111 · 2022-04-17 01:02:58 +08:00 · 1223 次点击
    这是一个创建于 955 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一、Spring 事件监听介绍

    Spring 对事件监听是通过事件类型、事件类型监听和事件发布器 3 个部分来完成的

    // 1. 自定义订单事件
    public class OrderEvent extends ApplicationEvent {
    ...
    }
    // 2. 定义订单监听器
    @Component
    public class OrderListener implements ApplicationListener<OrderEvent> {
        @Override
        public void onApplicationEvent(OrderEvent event) {
        // 生成订单、删除购物车、扣减库存
        ```
        }
    }
    // 3. 通过 applicationEventPublisher 发布事件
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    private void saveOrder(MallUserVO mallUserVO, Long couponUserId, List<ShopCatVO> shopcatVOList, String orderNo) {
        // 订单检查
        ```
        // 生成订单号
        String orderNo = NumberUtil.genOrderNo();
        // 发布订单事件,在事件监听中处理下单逻辑
        applicationEventPublisher.publishEvent(new OrderEvent(orderNo, mallUserVO, couponUserId, shopcatVOList));
        // 所有操作成功后,将订单号返回
        return orderNo;
        ```
    }
    

    上面的代码已经是将订单的保存逻辑从下单接口解耦到订单监听器中了,但是 Spring 使用默认自带的SimpleApplicationEventMulticaster事件监听发布类是同步通知事件监听器的,这里会阻塞下单主线程,影响接口响应时长。

    二、使用异步的事件监听发布类

    由于默认的SimpleApplicationEventMulticaster类是同步调用,这里可以从 2 个方面入手:

    1. 从事件监听器:将事件监听器的事件触发方法改为异步执行,例如加入将生成订单、删除购物车、扣减库存逻辑放入线程池,或者是在onApplicationEvent放上上加上@Async注解,表示该方法异步执行。
    2. 通过修改默认事件监听发布类的taskExecutor属性,这样可以使用已有的件监听发布类来优化相关逻辑
    /**
     * 系统启动时執行
     */
    @Component
    public class SpringBeanStartupRunner implements ApplicationRunner {
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            // 设置 spring 默认的事件监听为异步执行
            SimpleApplicationEventMulticaster multicaster = SpringContextUtil.getBean(SimpleApplicationEventMulticaster.class);
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    5,
                    10,
                    60L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(500),
                    new CustomizableThreadFactory("newbee—event-task"),
                    new ThreadPoolExecutor.CallerRunsPolicy()
            );
            multicaster.setTaskExecutor(threadPoolExecutor);
        }
    }
    

    在系统启动时反射修改SimpleApplicationEventMulticaster类的taskExecutor属性,从而让SimpleApplicationEventMulticaster类支持异步事件通知

    三、事件监听机制的代码思考

    通过事件监听机制,我们将下单逻辑拆分成如下步骤:

    1. 订单检查
    2. 生成订单号
    3. 发布订单事件,在事件监听中处理订单保存逻辑
    4. 所有操作成功后,将订单号返回 每个步骤都是各自独立不互相影响,后期引入消息队列,对代码的改动也是很少,只需将事件发布和事件监听的代码换成消息队列的消息发送和消息监听即可。

    最后贴一下实战项目地址newbeemall

    3 条回复    2022-04-18 22:02:42 +08:00
    jeesk
        1
    jeesk  
       2022-04-18 00:46:13 +08:00 via Android
    😅 如果宕机呢?
    jeesk
        2
    jeesk  
       2022-04-18 00:47:42 +08:00 via Android
    spring 消息不是可靠消息, 所以我还是用 kafka 之类的消息队列。
    wayn111
        3
    wayn111  
    OP
       2022-04-18 22:02:42 +08:00
    @jeesk 是的,这里只是写了下单解耦的思路,最后也说明了可以换成其他消息队列来写
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3201 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 13:11 · PVG 21:11 · LAX 05:11 · JFK 08:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.