项目迭代中新老逻辑切换入口

前言

​ 日常我们在项目开发中经常会进行项目迭代,比如说开发初期设定的代码逻辑根据功能需求迭代逐渐发现越来越难用,或者改动是对整体较大时,往往会进行专项处理,对这个逻辑进行改造。

​ 那么就会涉及到原先被调用方切换接口等问题,这种情况我们为了让外部接口无感知,都是采用内部切流的方式进行外部无感知的。

那么我们来构造一个场景来看看如何进行切流和代码改造。

正文

我们有几个逻辑需要改造的接口

java 复制代码
@Service
public class ARepository {
    @Resource
    private AMapper aMapper;

    public AEntity query(long id){
      //原有的接口逻辑处理
    }
}

@Service
public class BRepository {

    @Resource
    private BMapper bMapper;

    public List<BEntity> query(long id){
      //原有的接口逻辑处理
    }
}

思路

  1. 以接口的维度来控制每一个接口是否要进行切流
  2. 新建一个同级别的新接口实现改造后的逻辑,避免新老逻辑在同一个文件里混杂
  3. 以配置的形式来判断是否需要切换,若出现问题可单独进行回溯

实现方式

apollo

我们创建一个可以随时改动的配置,我这里使用 apollo 进行配置以便随时改动和发布生效:

  1. 每一个 Repository 有唯一一个 key 值,然后去 value 里面找方法 key。

  2. 布尔值 false 表示走原逻辑、true 表示走改造后的逻辑。

json 复制代码
{
  "ARepository":{
    "query":false
  },
  "BRepository":{
    "query":false
  }
}

新逻辑

java 复制代码
@Service
public class NewARepository {
    @Resource
    private AMapper aMapper;

    public AEntity query(long id){
      //新的接口逻辑处理
    }
}

@Service
public class NewBRepository {

    @Resource
    private BMapper bMapper;

    public List<BEntity> query(long id){
      //新的接口逻辑处理
    }
}

切流控制

创建一个切流控制类,使用上面的 apollo 来判断是否需要执行新逻辑并返回。

我们构造一个泛型类,统一注入新逻辑类,在调用 switchHandler 是传入新逻辑函数,通过 apollo 判断是否需要执行

java 复制代码
@Slf4j
@Getter
@Component
public class MapperSwitchHandler {
  
    @Resource
    private NewARepository newARepository;
    @Resource
    private NewBRepository newARepository;

  	//lombok忽略此属性,不生成 get 方法
    @Getter(AccessLevel.NONE)
  	//获取 apollo 配置信息
    @ApolloJsonValue(namespace = "namespace", config = "config", key = "key")
    private JSONObject mapperSwitch;


    /**
     * @Author: zhou
     * @Description: 根据传入参数获取 apollo 配置的 boolean 值
     * @Date: 2024/5/27 11:38
     * @Param: className apollo 中类名--一级
     * @Param: methodName apollo 中方法名--二级
     * @return: boolean
     */
    private boolean switchResult(String className, String methodName) {
        if (mapperSwitch == null) {
            return false;
        }
        return Optional.ofNullable(mapperSwitch.getJSONObject(className))
            .map(item -> item.getBoolean(methodName))
            .orElse(false);
    }

    /**
     * @Author: zhou
     * @Description: 根据 apollo 配置的 boolean 值来判断是否执行传入的 action 函数,并获取结果
     * @Date: 2024/5/27 11:36
     * @Param: className apollo 中类名
     * @Param: methodName apollo 中方法名
     * @Param: action 待执行函数
     * @return: MapperSwitchHandler.Result<T>
     */
    public <T> Result<T> switchHandler(String className, String methodName, Supplier action) {
        Result<T> result = new Result<>();
        boolean flag = switchResult(className, methodName);
        log.info("className:{}, methodName:{}, mapper switch : {}", className, methodName, flag);
        result.setFlag(flag);
        if (result.isFlag()) {
            result.setData((T) action.get());
            log.info("new logic result :{}", JSONObject.toJSONString(result.getData()));
        } else {
            log.info("old logic start...");
        }
        return result;
    }

    @Slf4j
    @Data
    public static class Result<T> {
        /**
         * 逻辑标识
         */
        private boolean flag;

        /**
         * 获取的结果
         */
        private T data;
    }
}

这样我们就实现好了新逻辑的控制和改造,现在需要对原逻辑进行改造判断。

老逻辑改造

java 复制代码
@Service
public class ARepository {
    @Resource
    private AMapper aMapper;
  
  	//注入开关
    @Resource
    private MapperSwitchHandler mapperSwitchHandler;

    public AEntity query(long id){
        MapperSwitchHandler.Result<AEntity> result = mapperSwitchHandler.switchHandler(
            "ARepository",
            "query",
            () -> mapperSwitchHandler.getNewARepository().query(id)
        );
        // flag 判断是否走新 sql ,true 标识走新 sql,直接返回;false 标识走老逻辑
        if (result.isFlag()) {
            return result.getData();
        }
      
      //原有的接口逻辑处理
    }
}

@Service
public class BRepository {

    @Resource
    private BMapper bMapper;
  
  	//注入开关
    @Resource
    private MapperSwitchHandler mapperSwitchHandler;

    public List<BEntity> query(long id){
      MapperSwitchHandler.Result<List<BEntity>> result = mapperSwitchHandler.switchHandler(
            "BRepository",
            "query",
            () -> mapperSwitchHandler.getNewBRepository().query(id)
        );
        // flag 判断是否走新 sql ,true 标识走新 sql,直接返回;false 标识走老逻辑
        if (result.isFlag()) {
            return result.getData();
        }
      //原有的接口逻辑处理
    }
}

这样我们就实现了通过 apollo 配置来灵活的判断每一个接口层面的逻辑控制。待灰度完成或者新逻辑全部正确的时候我们有两种方式来进行代码清理:

  • 完全删掉老的类文件,原调用方只需要重新引用新的接口类
  • 只删除老逻辑,老接口的入口调用新逻辑即可。

尾言

​ 本文介绍了在进行代码改造进行逻辑切流失的逻辑操作,通过 apollo 配置来实时动态的判断各个接口的控制效果,通过统一的处理类避免了每一个需要改造的代码都需要引用新的逻辑类和开关配置。总体来说是一个比较不错的方案,第一次实现此场景,如果更好的建议可以交流一下。

相关推荐
wangchunyu1141 小时前
JAVA CompletableFuture异步编程实战
java·开发语言·python
paopaokaka_luck2 小时前
流浪动物救助与收养系统(DeepSeek AI对话、 ECharts多维分析、救助流程衔接、领养与回访衔接、 捐赠确认与公示、志愿者协作)
java·前端·javascript·人工智能·mysql·echarts·推荐算法
Thomas.Sir2 小时前
第9课:SpringBoot4.x Web开发基础:WebMvc自动配置、静态资源、请求参数接收
java·spring boot
程序 代码狂人2 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
m0_639906943 小时前
Linux 操作系统入门:目录结构、服务排查常用命令、Shell 脚本基础
linux·运维·后端
2501_944521593 小时前
Deepmerge 跨端三方库仓颉适配全流程
java·python·eclipse·github
meiying88383 小时前
快递单号批量查询Python脚本:从手动到自动化的完整实现
开发语言·python·自动化
楚山之石3 小时前
【Medical Image Analysis】深度学习医学图像合成综述:伪CT、合成MRI与合成PET的架构、性能与临床边界
图像处理·人工智能·学习·mr·mri
holly就是我4 小时前
AI 编程助手为什么跑偏?用开源 ccglass 看清 Prompt、工具调用和 Token 消耗
人工智能·后端
鸿蒙开发4 小时前
共享 Schema 与 SchemaFactory
前端·后端·harmonyos