美文网首页产品系统架构收藏
复杂任务中,流程的解耦设计

复杂任务中,流程的解耦设计

作者: 知了一笑 | 来源:发表于2022-05-08 18:29 被阅读0次

    做事不能急,得一步异步的来;

    一、业务场景

    在系统开发的过程中,必然存在耗时极高的动作,是基于请求响应模式无法解决的问题,通常会采用解耦的思维,并基于异步或者事件驱动的方式去调度整个流程的完整执行;

    文件任务:在系统解析大文件数据时,在获取任务之后,会异步处理后续文件读写流程;

    中间表:执行复杂场景的数据分析时,收集完待分析的对象之后,会并发执行各个维度的采集动作,并依次将数据写入临时的中间表中,方便数据查询动作;

    在上述场景中,基于单次请求响应无法执行整个过程,必须对流程分段分步和异步推进,在流程中根据场景去判断,是异步有序驱动,还是异步并发处理,并基于各个节点的执行状态判断动作是否成功。

    二、任务管理

    复杂任务的执行周期相对偏长,要确保稳定的执行则需要对任务做精细的设计和管理,通常会基于如下几个因素去描述任务:

    02-1.png
    • 场景:定义任务的主题场景,便于将多种任务做统一管理和调度,例如:文件、数据、报表等;
    • 计划:对任务做好步骤的拆分,并制定和推进相应的执行计划,例如:有序调度、并发执行等;
    • 状态:针对任务和节点的执行计划,都要提供细节的状态定义,例如:开始/结束,进行中/已完成,成功/失败等;

    设计合理的任务结构,以便更高效的管理流程,根据主题场景做任务分类,添加相应的执行计划,根据状态跟踪任务执行过程,并对失败动作进行捕捉和重试;

    三、设计思路

    1、同步请求响应

    服务之间的通信模式一般分为:同步和异步两种;同步是指在请求端发出动作之后,会一直等待响应端完成,或者响应超时导致熔断,即在一次请求调用中耦合所有的处理流程;

    02-2.png

    服务中大部分的请求都是同步响应模式,可以提高系统的响应速度;但是在分布式中,首先要控制超时熔断的时间,避免在流量高峰期请求堆积,拖垮整个服务;另外对于被大量调用的公共服务,要提高并发的支撑能力,降低对请求链路的性能影响。

    2、异步解耦模式

    异步模式的最大优点就是实现请求和响应的完全解耦,任务只需要触发一次开始动作,后续的流程就会逐步的推进直到结束;各个服务节点处理逻辑不会受到整个请求链路的耗时限制;

    02-3.png

    实现异步有多种方式,例如:请求回调、发布订阅、Broker代理等;在之前异步章节中有详细描述,这里不再赘述;异步消除了服务节点之间的依赖关系,但是也同样提高了流程的复杂性;

    3、事件驱动设计

    事件驱动是一个抽象的概念,即通过事件的方式实现多个服务间的协同,驱动整个流程的处理逻辑;在业务层面是一种设计思想,在技术层面通常采用发布订阅的方式,同样也可以消除服务间的强依赖关系;

    02-4.png

    事件和异步在模式上很类似,事件驱动在设计上更加精细,例如在订单场景中:将订单的状态变化作为一个事件,服务间通过消息传递的方式,依次处理库存服务、物流服务等;由于事件携带了一定的业务信息和状态,流程解耦更加彻底的同时复杂度也会更高。

    四、实践总结

    1、结构设计

    在结构设计中围绕任务、节点、数据三个核心要素,以确保对任务的执行过程有完整的跟踪和管理,要实现对任务的节点及相关的操作,具备执行重试或者直接取消撤回的控制;

    02-5.png

    状态管理是一项很复杂的工作,要衡量任务中各个状态标识是否合理,就要实时监控状态的变化,并且基于各种极端情况去验证流程,例如:重试设计、任务取消、任务暂停。

    2、高并发管理

    任务型的场景加上复杂的管理流程,执行时间自然也很长,如果场景中涉及到大文件的解析、或者数据调度,自然会引入任务分割与并发执行的机制;

    比较常用的思路:根据任务调度的集群数,对数据核心编号进行哈希计算,可以采用取模和分段两种算法,然后基于多线程的方式并发处理各自服务内的分管任务。

    3、管理模型

    不管是观察者模式,或者发布订阅模型,又或者说事件驱动设计,都可以理解为生产/消费的关系模型,围绕生产、存储、消费三个节点做管理;

    02-6.png
    • 生产端:负责创建具体的消息主体,在总线模式中,通常将消息进行入库存储,然后再执行队列推送,并跟踪该过程的状态变化,保证库和队列的一致性;
    • 消息体:描述动作的发布方和消费方,关键的状态信息变化,唯一标识和创建时间及版本,其余则根据场景需要定义即可;
    • 消费端:在消费时要关注的核心问题即失败重试,要避免重试机制引起数据不一致的问题,可以对消费进行加锁或者消息状态校验,以实现幂等的效果;
    • 存储端:通常采用数据库和消息中间件双存储的模式,并且需要保证二者动作的同时成功或者失败,顺序为先入库再执行队列推送;

    整个模型在设计思路上比较合理,但是架构的复杂性也变的很高,比如数据一致性问题、状态机制、事务、幂等性、流程中断等;整个链路需要详细的追踪记录并且可视化管理,开发补偿动作的接口,用来及时解决可能出现的突发问题。

    4、组件案例

    Spring框架本身就极具复杂度,这里单看事件模型的设计,包含三个核心角色:事件、发布、监听;与观察者设计模式在理念上相同;

    02-7.png

    事件:ApplicationEvent基础抽象类继承自JDK中EventObject类,具体事件要继承该类;source事件源,timestamp发生的系统时间;

    public class OrderState {
        // 基础要素
        private Integer eventId ;
        private String version ;
        private Long createTime ;
    
        // 消息定位
        private String source ;
        private String target ;
    
        // 状态变化
        private Integer orderId ;
        private Integer stateFrom ;
        private Integer stateTo ;
    }
    public class OrderStateEvent extends ApplicationEvent {
        public OrderStateEvent (OrderState orderState){
            super(orderState);
        }
    }
    

    发布者:Spring定义的顶级接口ApplicationEventPublisher,提供事件发布的能力;

    @Service
    public class EventService implements ApplicationContextAware, ApplicationEventPublisherAware {
        private ApplicationEventPublisher applicationEventPublisher;
        
        public void changeState (Integer orderId,Integer stateFrom,Integer stateTo){
            OrderState orderState = new OrderState() ;
            OrderStateEvent orderStateEvent = new OrderStateEvent(orderState) ;
            logger.info(Thread.currentThread().getName()+";"+orderStateEvent);
            applicationEventPublisher.publishEvent(orderStateEvent);
        }
    }
    

    监听者:实现JDK中顶级接口EventListener,Spring扩展了多种事件监听器,以实现各种场景的需求,例如:有序无序、同步异步等;

    @Component
    public class OrderStateListener implements ApplicationListener<OrderStateEvent> {
        private static final Logger logger = LoggerFactory.getLogger(OrderStateListener.class) ;
    
        @Async
        @Override
        public void onApplicationEvent(OrderStateEvent orderStateEvent) {
            logger.info(Thread.currentThread().getName()+";"+orderStateEvent);
        }
    }
    

    五、参考源码

    应用仓库:
    https://gitee.com/cicadasmile/middle-ware-parent
    
    编程文档:
    https://gitee.com/cicadasmile/butte-java-note
    

    相关文章

      网友评论

        本文标题:复杂任务中,流程的解耦设计

        本文链接:https://www.haomeiwen.com/subject/ufpqurtx.html