当前位置: 欣欣网 > 码农

京东商城高性能多线程并发编程与动态编排框架

2024-06-07码农

一、项目介绍

Gobrs-Async 是一款功能强大、配置灵活、带有全链路异常回调、内存优化、异常状态管理于一身的高性能多线程并发编程和动态编排框架。为企业提供在复杂应用场景下动态任务编排的能力。针对于复杂场景下,异步线程复杂性、任务依赖性、异常状态难控制性;

二、解决什么问题

在开发复杂中台业务过程中,难免会遇到调用各种中台业务数据, 而且会出现复杂的中台数据依赖关系,在这种情况下。代码的复杂程度就会增加。如下图所示:

在请求调用各大中台数据时,难免会出现多个中台数据互相依赖的情况,现实开发中会遇到如下场景。

并行常见的场景 1 客户端请求服务端接口,该接口需要调用其他N个微服务的接口

譬如 请求我的购物车,那么就需要去调用用户的rpc、商品详情的rpc、库存rpc、优惠券等等好多个服务。同时,这些服务还有相互依赖关系,譬如必须先拿到商品id后,才能去库存rpc服务请求库存信息。最终全部获取完毕后,或超时了,就汇总结果,返回给客户端。

2 并行执行N个任务,后续根据这1-N个任务的执行结果来决定是否继续执行下一个任务,如用户可以通过邮箱、手机号、用户名登录,登录接口只有一个,那么当用户发起登录请求后,我们需要并行根据邮箱、手机号、用户名来同时查数据库,只要有一个成功了,都算成功,就可以继续执行下一步。而不是先试邮箱能否成功、再试手机号。

再如某接口限制了每个批次的传参数量,每次最多查询10个商品的信息,我有45个商品需要查询,就可以分5堆并行去查询,后续就是统计这5堆的查询结果。就看你是否强制要求全部查成功,还是不管有几堆查成功都给客户做返回

再如某个接口,有5个前置任务需要处理。其中有3个是必须要执行完毕才能执行后续的,另外2个是非强制的,只要这3个执行完就可以进行下一步,到时另外2个如果成功了就有值,如果还没执行完,就是默认值。

3 需要进行线程隔离的多批次任务。

如多组任务, 各组任务之间彼此不相关,每组都需要一个独立的线程池,每组都是独立的一套执行单元的组合。有点类似于hystrix的线程池隔离策略。

4 单机工作流任务编排。

5 其他有顺序编排的需求。

三、核心能力

四、框架设计

Gobrs-Async 在设计时,就充分考虑了开发者的使用习惯, 没有依赖任何中间件。对并发框架做了良好的封装。主要使用 CountDownLatch ReentrantLock volatile 等一系列并发技术开发设计。

任务触发器

任务流的启动者, 负责启动任务执行流

任务触发器

负责解析使用者配置的规则,同时于Spring结合,将配置的 Spring Bean 解析成 TaskBean,进而通过解析引擎加载成 任务装饰器。进而组装成任务树

任务启动器

负责通过使用解析引擎解析的任务树。结合 JUC 并发框架调度实现对任务的统一管理,核心方法有

  • trigger 触发任务加载器,为加载任务准备环境

  • 任务加载器

    负责加载任务流程,开始调用任务执行器执行核心流程

  • load 核心任务流程方法,在这里阻塞等待整个任务流程

  • getBeginProcess 获取子任务开始流程

  • completed 任务完成

  • errorInterrupted 任务失败 中断任务流程

  • error 任务失败

  • 任务执行器

    最终的任务执行,每一个任务对应一个 TaskActuator 任务的 拦截、异常、执行、线程复用 等必要条件判断都在这里处理

  • prepare 任务前置处理

  • preInterceptor 统一任务前置处理

  • task 核心任务方法,业务执行内容

  • postInterceptor 统一后置处理

  • onSuccess 任务执行成功回调

  • onFail 任务执行失败回调

  • 任务总线

    任务流程传递总线,包括 请求参数、任务加载器、 响应结果, 该对象暴露给使用者,拿到匹配业务的数据信息,例如:返回结果、主动中断任务流程等功能 需要任务总线(TaskSupport)支持

    核心类图

    五、落地场景

    目前 Gobrs-Async 已经在京东商城商详团队落地使用,经受严酷的并发考验。对各种中台调用应对自如

    通过 Gobrs-Async 管理中台接口请求 耗时任务请求 请求依赖关系等核心场景。充分利用CPU资源

    六、规则示例

    场景一

    如图1-1

    说明 任务A 执行完了之后,继续执行 B、C、D

    配置

    gobrs:
    async:
    rules:
    - name: "ruleName1"
    content: "A->B,C,D"

    场景二

    如图1-2

    说明 任务A 执行完了之后执行B 然后再执行 C、D

    配置

    gobrs:
    async:
    rules:
    - name: "ruleName1"
    content: "A->B->C,D"

    场景三

    如图1-3

    说明 任务A 执行完了之后执行B、E 然后按照顺序 B的流程走C、D、G。E的流程走F、G

    配置

    gobrs:
    async:
    rules:
    - name: "ruleName1"
    content: "A->B->C->D->G;A->E->F->G"

    场景四

    如图1-4

    说明 这种任务流程 Gobrs-Async 也能轻松支持

    配置

    gobrs:
    async:
    rules:
    - name: "ruleName1"
    content: "A->B->C,D,E;A->H->I,J,K"

    场景五

    如图1-5

    示例一

    说明 A、B、C 执行完之后再执行D

    配置

    gobrs:
    async:
    rules:
    - name: "ruleName1"
    content: "A,B,C->D"

    示例二

    说明 A、B、C 任务任意一个执行完成,则立即执行任务D( 谁最快执行谁执行, 类似于任务流程竞争关系 ) 此时可以使用 配置关键字 :any

    配置

    gobrs:
    async:
    ## :any 是关键字 表示任意 依赖的任务执行完成立即执行自己
    rules:
    - name: "ruleName1"
    content: "A,B,C->D:any"

    示例三

    说明 A、B、C 任务任意一个执行完成,则立即执行任务D( 谁最快执行谁执行, 类似于任务流程竞争关系 ) 与示例不同的是, 如果 D拿到执行权后,会将自身所依赖的未完成的任务 强制中断执行(避免浪费资源,业务运行等) 此时可以使用 配置关键字 :exclusive

    配置

    gobrs:
    async:
    ## :exclusive 是关键字
    rules:
    - name: "ruleName1"
    content: "A,B,C->D:any:exclusive"

    示例四

    同示例二有点类似,在示例二的场景下,无法根据某一个任务的执行成功或者失败进行后续任务的处理,示例二完全根据线程调度执行的随机顺序进行执行,即谁先执行完 谁有资格继续往下执行,所以如果想 执行结果的条件 即: task 方法返回 true 则立即执行,返回false则不执行的判断条件进行控制。那么就有以下的实现方式。

    源代码下载地址:

    https://gitee.com/dromara/gobrs-async.git

    看到最后,如果这个项目对你有用,一定要给我点个「 在看和赞 」。