Easytool 中文文档

作者: 天不生我小金 | 来源:发表于2022-10-19 19:18 被阅读0次

    前言:该博客主要是记录自己学习的过程,方便以后查看,当然也希望能够帮到大家。

    Easytool

    Easytool 的目标是干掉大部分冗余的复杂代码,从而最大限度的避免“复制粘贴”代码的问题,使我们能去更专注业务,提升我们的代码质量。

    简介

    Easytool 是一个小型的Java工具类库,封装了一些常用的通用的方法,降低了相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅。
    
    Easytool 中的大部分方法来自开发过程中的真实需求,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当。
    

    完整代码地址:https://github.com/Jinhx128/easytool

    模块

    名称 介绍
    easytool-all 包含所有模块的引用
    easytool-core 核心包,包括对集合处理、日期、各类Util等
    easytool-crypto 加密解密模块,提供对称、非对称和摘要算法封装
    easytool-process 基于spring封装的任务编排轻量级框架

    1. easytool-core

    1.1 待完善...

    2. easytool-crypto

    2.1 待完善...

    3. easytool-process

    easytool-process是一个基于spring封装的任务编排轻量级框架

    由来
    • 起初是因为自己在开发过程中遇到比较复杂的业务场景,还伴随着并发操作,就抽象了一层方法方便自己在项目中使用。
    • 后面在其他项目也遇到类似的场景,此时就想抽象出来公共的东西,方便在其他项目能快速接入,这样可以干掉大部分重复的冗余代码,同时也降低了出错的可能性。
    使用场景
    • 比较复杂的业务场景,比如订单详情,需要聚合大量的数据,而且根据订单状态的变化,需要的数据也有所变化。

    3.1 组成

    主要包含:上下文,节点,链路,其他组件

    easytool-process-组成.jpg
    3.1.1 上下文
    1. 所有节点之间的数据交互都通过上下文进行传递
    2. 上下文包含了整个链路执行过程中所需要用到的数据,包括入参,中间数据,出参等字段。
    3. 只需要创建一个类,把所需要用到的字段加入进去即可,命名一般是链路名+Context后缀。
    3.1.2 节点

    一个节点代表一个任务,节点通过编排组成链路。创建节点需要继承AbstractNode抽象类,并实现相关抽象方法,由于依赖spring,所以需要加@Component注解。

    easytool-process-节点执行流程.jpg

    详解

    名称 说明
    getDependsOnNodes() 抽象方法,返回当前节点依赖的前置节点集合,必须实现。通过该方法进行链路任务编排,注意:只需要返回直接前置节点,比如链路为A->B->C,此时C节点只需要返回B节点即可,另外如果无依赖的节点,比如A节点,此时返回空或空集合即可
    isSkip(ChainContext<DemoContext> chainContext) 抽象方法,用于跳过当前节点,必须实现。返回ture则跳过,false则不跳过,比如某种状态下不需要执行当前节点的场景
    execute(ChainContext<DemoContext> chainContext) 抽象方法,用于编写当前节点的逻辑,必须实现。重点在此通过上下文chainContext与其他节点进行数据传递,会按照链路编排的顺序执行各个节点,最终获取相应的数据/操作
    businessFail(String msg)/businessFail(int code, String msg) 内置方法,当遇到业务异常时,比如某个数据不存在,此时可以直接中断整个链路,返回业务错误提示,需要用到时调用即可
    onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) 抽象方法,是一个回调方法,必须实现。需要搭配businessFail方法使用,当执行了上述方法后会执行
    onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) 抽象方法,是一个回调方法,必须实现。当节点执行遇到未知错误时会执行
    onTimeoutFail(ChainContext<DemoContext> chainContext) 抽象方法,是一个回调方法,必须实现。当节点执行遇到超时错误时会执行
    onSuccess(@NonNull ChainContext<T> chainContext) 内置方法,是一个回调方法,当节点执行成功时会执行,需要用到时重写即可
    afterExecute(@NonNull ChainContext<T> chainContext) 内置方法,是一个回调方法,当节点执行遇到完成时会执行。注意,是执行完成,就是无论有没有出现异常,需要用到时重写即可
    3.1.3 链路

    一个链路包含了多个已经按照顺序编排好的节点,创建链路需要继承AbstractChain抽象类,并实现相关抽象方法,由于依赖spring,所以需要加@Component注解。

    easytool-process-链路执行流程.jpg

    详解

    名称 说明
    getChainTimeout() 内置方法,返回的是该链路的超时时间,执行超过该时间则中断链路并返回超时异常结果,默认值是200ms,需要替换时重写即可
    getThreadPool() 内置方法,返回的是执行该链路的线程池,内置默认线程池,需要替换时重写即可
    execute(ChainContext<DemoContext> chainContext) 抽象方法,用于编写当前节点的逻辑,必须实现。重点在此通过上下文chainContext与其他节点进行数据传递,会按照链路编排的顺序执行各个节点,最终获取相应的数据/操作
    KeyThreadContextConfig/SingletonThreadContextConfig 内置类,用于配置线程上下文信息,包含新增,获取,删除三个动作
    getThreadContextInitConfigs() 内置方法,返回的是用于配置线程上下文信息集合,也就是4中的两种,一种需要key,一种不需要。最常见的场景就是用于链路追踪的traceId,需要用到时重写即可。
    checkParams(ChainContext<DemoContext> chainContext) 抽象方法,用于在执行链路之前做参数校验使用,必须实现
    businessFail(String msg)/businessFail(int code, String msg) 内置方法,当遇到业务异常时,比如某个数据不存在,此时可以直接中断整个链路,返回业务错误提示,需要用到时调用即可行
    onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) 抽象方法,是一个回调方法,必须实现。需要搭配businessFail方法使用,当执行了上述方法后会执行
    onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) 抽象方法,是一个回调方法,必须实现。当链路执行遇到未知错误时会执行
    onTimeoutFail(ChainContext<DemoContext> chainContext) 抽象方法,是一个回调方法,必须实现。当链路执行遇到超时错误时会执行
    onSuccess(@NonNull ChainContext<T> chainContext) 内置方法,是一个回调方法,当链路执行成功时会执行,需要用到时重写即可
    afterExecute(@NonNull ChainContext<T> chainContext) 内置方法,是一个回调方法,当链路执行遇到完成时会执行。注意,是执行完成,就是无论有没有出现异常,需要用到时重写即可
    openMonitor() 内置方法,用于开启链路监控,会记录并定时打印链路及各节点执行的耗时等信息。返回ture则开启,false则不开启,需要用到时重写即可
    setNodeInfo() 抽象方法,用于添加链路节点,必须实现。内部通过下面方法添加节点,注意:链路的执行顺序跟此处的添加顺序无关
    addInterruptNode/addInterruptNodes 内置方法,增加一个中断节点。执行遇到异常时中断链路,并返回异常信息。参数:节点类,获取节点超时时间方法
    addAbandonNode/addAbandonNodes 内置方法,增加一个抛弃节点。执行遇到异常时抛弃该节点,继续执行后续节点。参数:节点类,获取节点超时时间方法
    addRetryNode/addRetryNodes 内置方法,增加一个重试节点。执行遇到异常时重试执行该节点,达到最大重试次数后还未执行成功,则中断链路,并返回异常信息。参数:节点类,获取节点超时时间方法,重试次数
    3.1.4 其他组件
    1. ChainContext:链路上下文,用于创建统一的上下文,该上下文还包含了其他信息,我们只需要关心内部的contextInfo即可,也就是操作chainContext.getContextInfo()。
    2. ChainHandler:链路执行器,用于执行指定链路,并可指定需要返回的数据。
    3. Monitor:监控器,开启后会记录并定时打印链路及各节点执行的耗时等信息。
    4. 查看日志:需要注意链路内部打印的日志级别为info!可以通过getThreadContextInitConfigs()内置方法设置链路id,然后通过下面的命令查询
    ## 会有整个执行过程的细节日志
    grep '链路id' xx.log
    
    ## 会有整个链路过程的细节日志,过滤一些可能无关的日志
    grep '链路id' xx.log | grep 'process'
    
    ## 会有整个链路过程的细节日志,包括一些错误日志(错误栈信息可能换行)
    grep -A 50 '链路id' xx.log | grep 'process'
    

    3.2 简单讲一下Demo

    easytool-process-demo链路结构.jpg

    第一步,在pom.xml加入依赖,如下

    <dependency>
        <groupId>cc.jinhx</groupId>
        <artifactId>easytool-all</artifactId>
        <version>自行查看最新版本</version>
    </dependency>
    

    可以根据需求对每个模块单独引入,也可以通过引入easytool-all方式引入所有模块。

    第二步,创建上下文,如下

    DemoContext

    import lombok.Data;
    
    /**
     * DemoContext
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Data
    public class DemoContext {
    
        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 入参 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 入参 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
        private String req;
    
        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 中间数据 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    
        private String dataA;
    
        private String dataB;
    
        private String dataC;
    
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 中间数据 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
    
        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 结果 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    
        private String dataD;
    
        private String dataE;
    
        private String dataF;
    
        private String dataG;
    
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 结果 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
    }
    

    第三步,创建多个节点,DemoGetDataANode,DemoGetDataBNode,DemoGetDataCNode,DemoGetDataDNode,DemoGetDataENode,DemoGetDataFNode,DemoGetDataGNode,如下

    DemoGetDataANode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Set;
    
    /**
     * DemoGetDataANode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataANode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return null;
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("req".equals(demoContextInfo.getReq())){
                demoContextInfo.setDataA(demoService.getDataA());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataBNode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataBNode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataBNode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Collections.singletonList(DemoGetDataANode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataA".equals(demoContextInfo.getDataA())){
                demoContextInfo.setDataB(demoService.getDataB());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataCNode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataBNode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataCNode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Collections.singletonList(DemoGetDataANode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataA".equals(demoContextInfo.getDataA())){
                demoContextInfo.setDataC(demoService.getDataC());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataDNode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataDNode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataDNode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Collections.singletonList(DemoGetDataANode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataA".equals(demoContextInfo.getDataA())){
                demoContextInfo.setDataD(demoService.getDataD());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataENode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataENode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataENode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Arrays.asList(DemoGetDataBNode.class, DemoGetDataCNode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataB".equals(demoContextInfo.getDataB()) && "dataC".equals(demoContextInfo.getDataC())){
                demoContextInfo.setDataE(demoService.getDataE());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataFNode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataENode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataFNode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Collections.singletonList(DemoGetDataCNode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataC".equals(demoContextInfo.getDataC())){
                demoContextInfo.setDataF(demoService.getDataF());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    

    DemoGetDataGNode

    import cc.jinhx.easytool.process.BusinessException;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.service.DemoService;
    import cc.jinhx.easytool.process.node.AbstractNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * DemoGetDataENode
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Component
    public class DemoGetDataGNode extends AbstractNode<DemoContext> {
    
        @Autowired
        private DemoService demoService;
    
        @Override
        public Set<Class<? extends AbstractNode>> getDependsOnNodes() {
            return new HashSet<>(Collections.singletonList(DemoGetDataENode.class));
        }
    
        @Override
        protected boolean isSkip(ChainContext<DemoContext> chainContext) {
            return false;
        }
    
        @Override
        protected void execute(ChainContext<DemoContext> chainContext) {
            DemoContext demoContextInfo = chainContext.getContextInfo();
            if ("dataE".equals(demoContextInfo.getDataE())){
                demoContextInfo.setDataG(demoService.getDataG());
            }
        }
    
        @Override
        public void onUnknowFail(ChainContext<DemoContext> chainContext, Exception e) {
    
        }
    
        @Override
        public void onBusinessFail(ChainContext<DemoContext> chainContext, BusinessException e) {
    
        }
    
        @Override
        public void onTimeoutFail(ChainContext<DemoContext> chainContext) {
    
        }
    
    }
    
    注意
    1. 关注getDependsOnNodes()方法,要确定好各个节点的依赖情况,也就是正确编排好各个任务的顺序,注意只需要返回直接前置节点即可。
    2. chainContext链路上下文,该上下文还包含了其他信息,我们只需要关心内部的contextInfo即可,也就是操作chainContext.getContextInfo()

    第四步,创建链路

    DemoChain

    import cc.jinhx.easytool.process.chain.*;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import cc.jinhx.easytool.process.demo.node.*;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.MDC;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * DemoChain
     *
     * @author jinhx
     * @since 2022-03-29
     */
    @Slf4j
    @Component
    public class DemoChain extends AbstractChain<DemoContext> {
    
        private static final AtomicInteger CHAIN_THREAD_POOL_COUNTER = new AtomicInteger(0);
    
        private static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
    
        /**
         * 自定义链路线程池
         */
        public static final ThreadPoolExecutor CHAIN_THREAD_POOL =
                new ThreadPoolExecutor(
                        2, CPU_NUM * 2,
                        10, TimeUnit.MINUTES,
                        new LinkedBlockingQueue<>(1024),
                        (Runnable r) -> new Thread(r, "chain_thread_" + CHAIN_THREAD_POOL_COUNTER.incrementAndGet()),
                        (r, executor) -> log.info("chain has bean rejected" + r));
    
        private final static long DEFAULT_CHAIN_TIMEOUT = 1000L;
    
        private final static long DEFAULT_NODE_TIMEOUT = 500L;
    
        @Override
        protected long getChainTimeout() {
            return DEFAULT_CHAIN_TIMEOUT;
        }
    
        @Override
        protected ExecutorService getThreadPool() {
            // 执行该链路的线程池
            return CHAIN_THREAD_POOL;
        }
    
        @Override
        protected Set<AbstractThreadContextConfig> getThreadContextInitConfigs() {
            return new HashSet<>(Collections.singletonList(new KeyThreadContextConfig<>("traceId", MDC::get, MDC::put, MDC::remove)));
        }
    
        @Override
        protected void checkParams(ChainContext<DemoContext> chainContext) {
    
        }
    
        /**
         * 设置节点信息,链路的执行顺序跟此处的添加顺序无关
         */
        @Override
        protected void setNodeInfo() {
            // 添加重试节点DemoGetDataANode,重试次数为1,并配置节点超时时间。执行遇到异常时重试执行该节点,达到最大重试次数后还未执行成功,则中断链路,并返回异常信息
            this.addRetryNode(DemoGetDataANode.class, ChainNode.RetryTimesEnum.ONE, DemoChain::getNodeTimeout);
            // 添加中断节点DemoGetDataBNode,DemoGetDataCNode,并配置节点超时时间。执行遇到异常时中断链路,并返回异常信息
            this.addInterruptNodes(Arrays.asList(DemoGetDataBNode.class, DemoGetDataCNode.class), DemoChain::getNodeTimeout);
            // 添加抛弃节点DemoGetDataDNode,并配置节点超时时间。执行遇到异常时抛弃该节点,继续执行后续节点
            this.addAbandonNode(DemoGetDataDNode.class, DemoChain::getNodeTimeout);
            // 添加中断节点DemoGetDataENode,DemoGetDataFNode,DemoGetDataGNode,并配置节点超时时间。执行遇到异常时中断链路,并返回异常信息
            this.addInterruptNodes(Arrays.asList(DemoGetDataENode.class, DemoGetDataFNode.class, DemoGetDataGNode.class), DemoChain::getNodeTimeout);
        }
    
        public static long getNodeTimeout() {
            return DEFAULT_NODE_TIMEOUT;
        }
    
    }
    
    注意
    1. 关注setNodeInfo()方法,如果少添加了某个节点,则链路没法正常执行起来,会报异常提示链路不完整。注意:链路的执行顺序跟此处的添加顺序无关。

    第五步,编写单元测试类,如下

    DemoTest

    import cc.jinhx.easytool.process.ProcessResult;
    import cc.jinhx.easytool.process.SpringContextConfig;
    import cc.jinhx.easytool.process.chain.ChainContext;
    import cc.jinhx.easytool.process.chain.ChainHandler;
    import cc.jinhx.easytool.process.demo.chain.DemoChain;
    import cc.jinhx.easytool.process.demo.context.DemoContext;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * DemoTest
     *
     * @author jinhx
     * @since 2022-03-21
     */
    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringContextConfig.class)
    public class DemoTest {
    
        @Test
        public void test1() {
            // 创建上下文
            ChainContext<DemoContext> chainContext = ChainContext.create(DemoContext.class);
            // 设置入参
            chainContext.getContextInfo().setReq("req");
            
            // 执行指定链路,并返回所有数据
            ProcessResult<DemoContext> processResult1 = ChainHandler.execute(DemoChain.class, chainContext);
            System.out.println(processResult1);
            
            // 执行指定链路,并返回指定数据
            ProcessResult<String> processResult2 = ChainHandler.execute(DemoChain.class, chainContext, DemoContext::getDataG);
            System.out.println(processResult2);
        }
    
    }
    
    说明
    1. ChainContext.create(TestContext.class)方法用于创建统一的上下文,该上下文还包含了其他信息,我们只需要关心内部的contextInfo即可,也就是操作chainContext.getContextInfo()
    2. ChainHandler.execute(DemoChain.class, chainContext)方法用于执行指定链路,包含多个重载方法,入参包括:链路类,上下文,要获取的数据的方法。
    3. 查看日志:需要注意链路内部打印的日志级别为info!可以通过getThreadContextInitConfigs()内置方法设置链路id,然后通过下面的命令查询
    ## 会有整个执行过程的细节日志
    grep '链路id' xx.log
    
    ## 会有整个链路过程的细节日志,过滤一些可能无关的日志
    grep '链路id' xx.log | grep 'process'
    
    ## 会有整个链路过程的细节日志,包括一些错误日志(错误栈信息可能换行)
    grep -A 50 '链路id' xx.log | grep 'process'
    

    后记:本次分享到此结束,本人水平有限,难免有错误或遗漏之处,望大家指正和谅解,欢迎评论留言。

    相关文章

      网友评论

        本文标题:Easytool 中文文档

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