👆 这是第 372 篇不掺水的原创,想要了解更多,请戳下方卡片关注我们吧~
jvm-sandbox-repeater 是阿里开源的一款可基于 jvm-sandbox (阿里另一开源项目)可对应用目标 jvm 进行动态增强同时对目标服务的指定流量进行录制及回放的工具,使用过程中遇到如下问题:
目前流量的录制仅能通过采样率来降低录制的频次,比如采样率为 10% ,那么一天之中将有 10% 的时间在录制流量,但这 10% 的时间内录制的流量也可能存在很多的重复流量或者很多相似的流量,从业务场景的角度去分析,这些重复或者相似的流量很大程度上业务场景是一致的,而对于功能测试或者接口测试而言,需要测试的是场景,重复的场景流量重复录制、重复回放是一种极大的浪费,而且对流量用例的分类及回放结果的分析及其不友好。所以我们需要对流量的录入进行降噪处理。
目前回放结果的对比是全量对比,即对录制的主调用结果和回放的主调用的结果进行全面(返回对象的所有的 key 和 value )对比,通常情况下,全量对比是非常有必要的,但存在一些场景:比如流量回放不支持 mock 的 native 方法(像时间戳、时间、随机数等等),比如业务改造新增的一些返回字段或者较少的一些返回字段,这些场景会对我们的结果对比产生干扰,所以需要在结果对比时对特定的纬度进行降噪处理。
基于上述背景,我们打算对流量回放进行如下改造:
配置对象 RepeaterConfig 新增属性 List
/**
* 需要忽略对比的列信息
*/
private List<String> ignoreFiled = Lists.newArrayList();
对比器接口 com.alibaba.jvm.sandbox.repeater.aide.compare.Comparable#compare 新增入参 List
/**
* compare to object
*
* @param left left object to be compare
* @param right right object to be compare
* @param ignoreFiled compare ignore some filed
* @return compare result
*/
CompareResult compare(Object left, Object right,List<String> ignoreFiled);
对比器 Map 对比器实现 com.alibaba.jvm.sandbox.repeater.aide.compare.comparator.MapComparator 进行改造,根据降噪字段进行忽略对比。
if (ignoreFiled != null && key instanceof String && ignoreFiled.contains(key)) {
continue;
}
结果对比,再保存回放结果处 com.alibaba.repeater.console.service.impl.ReplayServiceImpl#saveRepeat 进行改造。
List<String> ignoreFiledLists = null;
if (moduleConfigBORepeaterResult.getData() != null) {
ignoreFiledLists = moduleConfigBORepeaterResult.getData().getConfigModel().getIgnoreFiled();
}
CompareResult result = comparable.compare(actual, expect, ignoreFiledLists);
其他关联影响:由于对比器入参新增,导致所有调用对比器处都需要进行入参添加,不使用降噪配置的地方传 null 即可,比如
关键信息图解:
下面是一些基础代码:
public class AiDenoiseOfferJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
}
}
Actor 发送消息、消费消息
public class AkkaConfig {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ApolloConfig apolloConfig;
@Bean
public ActorSystem actorSystem() {
return ActorSystem.create("my-actor-system");
}
@Bean
public ActorRef myActor(ActorSystem actorSystem) {
return actorSystem.actorOf(new RoundRobinPool(apolloConfig.getActorNum()).props(Props.create(MyActor.class,applicationContext)), "router");
}
}
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//获取降噪流量详情
List<RecordBO> recordBOList = recordDenoise.getRecordDetailBOList(apolloConfig.getPageSize());
// 计算每个分区的大小
int partitionSize = (int) Math.ceil((double) recordBOList.size() / apolloConfig.getActorNum());
// 使用 IntStream 创建一个范围从 0 到recordBOList.size()-1 的流
IntStream.range(0, recordBOList.size())
.boxed()
.collect(Collectors.groupingBy(index -> index / partitionSize))
.values()
.stream()
.map(indices -> indices.stream().map(recordBOList::get).collect(Collectors.toList()))
.forEach(t -> {
// 向 MyActor 发送 MyMessage 消息,消息内容为 RecordBO 列表
myActorRef.tell(new MyMessage(t), ActorRef.noSender());
});
}
public class MyActor extends AbstractActor {
private final ApplicationContext applicationContext;
@Autowired
public MyActor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(MyMessage.class, message -> {
List<RecordBO> recordBOList = message.getRecordBOList();
log.info("接受到的消息大小,{},当前线程:{}",recordBOList.size(),Thread.currentThread().getName());
RecordDenoise recordDenoise = applicationContext.getBean(RecordDenoise.class);
if (!recordBOList.isEmpty()) {
long startTime = System.currentTimeMillis();
//消费Actor消息
recordDenoise.doRecordDenoise(recordBOList);
long endTime = System.currentTimeMillis();
log.info("接受消息到消费完成耗时:{}",endTime-startTime);
}
})
.build();
}
}
private void processRecordDetailBO(RecordBO recordBO, List<RecordFlowEntity> idCosineSimilarity) {
//接口路径个性化处理
.....
//缓存处理的流量信息
....
//根据缓存流量id获取降噪纬度
...
//相似度计算
...
//根据相似度转用例
....
}
现阶段针对结果对比提供了手动降噪的能力,并将录制的流量进行了结果降噪对比同时将其转为用例,对于测试同学很难分辨这些用例覆盖了哪些代码,后续计划结合公司二开的基于 jacoco 的精准提供的能力对沉淀的用例进行训练,确保用户知晓用例对覆盖了哪些代码,甚至可能知道用例覆盖了哪些业务场景。目前正在调研中,尽请期待。
如果你觉得这篇内容对你挺有启发,我想邀请你帮我两件小事
1.点个「在看」,让更多人也能看到这篇内容(点了「在看」,bug -1 😊)
招贤纳士
政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。
如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com