- 项目迭代中新老逻辑切流-切换入口
- 项目迭代中新老逻辑切流 - 异步查询新接口
- 项目迭代中新老逻辑切流 - 新老数据对比—待更新
前言
前面说到了 项目迭代中新老逻辑切换入口 ,有兴趣的可以先看一下,不知道大家有什么疑问吗?
怎么知道自己新接口逻辑对不对?
怎么知道自己新接口返回的数据对不对?
能否兼容老逻辑?
能否对上游调用方无感知呢?
答案只有一个:新老接口的返回数据永远保持一致!
所以我们前面文章说的切流还差下面的两步操作:
- 在查询老接口的通知查询新接口
- 将查询的新接口数据和老接口数据进行对比
一旦发现对比期间不一致的情况,就要排查问题,然后修改新逻辑问题。只有当所有的结果都一致是才能安心的切换到新逻辑里面,晚上才能睡个好觉。
正文
首先我们需要在原先的 MapperSwitchHandler
中添加线程池配置,用来执行新逻辑的异步操作,目的是不阻塞原方法的执行。
@Slf4j
@Getter
@Component
public class MapperSwitchHandler {@Resourceprivate NewARepository newARepository;@Resourceprivate NewBRepository newARepository;//添加的线程池配置(需要提前配置)@Resourceprivate ThreadPoolTaskExecutor threadPoolTaskExecutor;//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@Datapublic static class Result<T> {/*** 逻辑标识*/private boolean flag;/*** 获取的结果*/private T data;}
}
我们用前文的 Repository
的基础上继续改造,下面是改造前的代码,请和改造后的代码进行对比。
@Service
public class ARepository {@Resourceprivate AMapper aMapper;//注入开关@Resourceprivate 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();}//原有的接口逻辑处理AEntity entity = aMapper.query(id);return entity;}
}
我们要在这个代码的基础上添加异步代码,让程序能够在切流之前执行老逻辑的同时执行新逻辑,同时拿到新老结果。
@Service
public class ARepository {@Resourceprivate AMapper aMapper;//注入开关@Resourceprivate MapperSwitchHandler mapperSwitchHandler;public AEntity query(long id) {Supplier<AEntity> supplier = () -> mapperSwitchHandler.getNewARepository().query(id);MapperSwitchHandler.Result<AEntity> result = mapperSwitchHandler.switchHandler("ARepository","query",supplier);// flag 判断是否走新 sql ,true 标识走新 sql,直接返回;false 标识走老逻辑if (result.isFlag()) {return result.getData();}//原有的接口逻辑处理AEntity entity = aMapper.query(id);//下面是异步操作,不影响老逻辑接口返回mapperSwitchHandler.getThreadPoolTaskExecutor().submit(() -> {AEntity newAEntityResult = supplier.get();//我们在这里对比老数据 entity 和 newAEntityResult 的数据是否一致,下面的对比逻辑肯定是不对的,下节介绍if (!newAEntityResult.equals(entity)) {System.out.println("老数据:" + JSONObject.toJSONString(entity) + "\r\n" +"新数据:" + JSONObject.toJSONString(newAEntityResult) + "\r\n");throw new RuntimeException("数据不一致,请检查");}});return entity;}
}
这样我们就能够在执行老逻辑的同时执行新逻辑,同时拿到两个数据之后进行数据对比,以此来确认新接口的数据是否正确。
尾言
在上一节介绍了日常工作中对项目进行迭代是新老逻辑接口的切换办法和思路,这一章介绍了在切换之间,对新老逻辑的返回数据的收集,以便于分析和对比,以对比结果来决定是否需要进行新接口切流。
在下一章,就说一说如何对于新老数据做对比,以及收集、排错等办法。