两个list怎么对比数据_基于日志的回放对比系统设计

‍‍

‍‍

‍‍87284ca203efe8d90a1a995ff7b63c27.png

点击关注“有赞coder”

获取更多技术干货哦~

1a1322ecee28457b74cd690a51627001.png作者:马力部门:新零售测试

一、背景

上半年公司的网关系统进行了重构,需要把零售业务已有的网关接口迁移到新网关上。这些接口每天都有成千上万次请求,为商家提供各种服务,稍有不慎就容易出现较大故障,所以如何迁移是个比较慎重的问题。这个迁移项目主要的验证重点是:确保新网关对于接口的请求和返回的处理和老网关一致;而主要的验证难点在于,仅从功能层面进行手工验证很难覆盖各种场景,尤其是如何构造各种请求参数以及检查各种返回的内容。若要人肉进行细致的接口级别的验证,那么花费的时间就会很长、效率很低。经过统计和梳理,涉及的接口超过了 1000 个,在这个数量级上,总花费的时间成本很高,研发团队难以承受。这不得不让我们停下来思考一下,是否有另一种高效的方式来解决这个问题。这时就想到了采用录制回放对比的方式。随着业务的快速发展,回归时需要考虑的场景越来越多,测试的耗时越来越长,越来越多的公司提出并采用录制回放的方式来提高效率,比如阿里开源的 jvm-sandbox-repeater,基于已有的流量进行录制,无需人肉准备脚本和数据,通过将录制的流量进行回放,还原真实的场景,最后提供回放结果判断是否符合预期,提高业务回归效率。从场景上来说,网关迁移验证做的也是回归测试,这个思路也是通用的,所以设计了一个类似的系统进行回放对比验证。

二、系统设计

如果需要采用录制回放对比的方式,首先第一步就是录制,在录制前需要确认下数据和流量的来源。数据越丰富、多样性越强,回放能够发现的问题越多,一些实时的流量采集方式通过拦截转发的方式获取数据,所以需要一定的配置和改造成本。幸运的是网关的日志对于接口的请求参数会进行记录,实际上已经完成了录制这个过程,可以采用成本最小的方式:通过拉取测试环境老网关的日志来获取请求数据,然后进行解析再进行回放和对比。基本流程如下,下面会对各步骤的逻辑进行说明af2a8aacbb425ebe8d5fe879f63f1d6a.png

2.1 日志拉取和清洗

作为整个流程的第一步:通过拉取日志获得需要回放的流量。回放对实时性要求不高,但是当初请求的时刻和回放的时刻之间的时间差也不能太长:比如早上创建一个商品并进行了查询操作,下午可能进行了删除,如果选择晚上再进行回放的话,查询结果就会为空,数据的质量就会下降,造成数据损耗。需要考虑设置一个较短的时间间隔进行回放,目前通过定时任务每隔一小时启动一次进行拉取和回放,根据当前启动时间自动生成对应的时间范围,去日志平台获取日志:比如任务启动时间是 11:10 分,那么自动转换成 10:00-11:00 的时间范围去拉取日志进行回放。获得日志后,第二步就是日志清洗。日志清洗的作用有两个:
  1. 过滤。有一些接口是不需要迁移到新网关的,进行过滤,减少对最后统计结果的干扰。另外,录制回放运行时也会请求网关,需要过滤掉这部分人工流量。

  2. 采样。虽然是测试环境,一天的数据量也有 10W+ ,全部处理时耗较长;同时,初期会有大量重复问题爆出,增大排查成本。根据二八定律以及实际的观察,小部分接口贡献了大部分流量,考虑每个接口雨露均沾和数据的多样性,选择接口+场景作为采样纬度,返回结果中的不同 code 认为是不同的场景,这样除了正常请求,也考虑到异常请求的回放。 

854138b1fcc756f3e610862b4e7d053e.png初期可以设置一个较小的采样阈值 Limit 来收集问题,减少人工排查问题的成本。等问题修复稳定运行一段时间后,再考虑逐步增大阈值。

2.2 进行回放

考虑到数据是不断变化的,同时对于登陆态生命周期是否正常也需要进行观察,所以回放并不是拿新网关请求的返回和日志中记录的老网关进行对比,而是同时请求两个网关,再对各自的返回进行比较。在请求过程中,可能遇到接口超时等情况,遵循尽可能成功的原则,做对应的处理,比如超时进行重试等。另一个影响比对效率的因素就是性能,每一个请求都可以独立处理,就意味着可以并发执行,如 python 的 multiprocessing 多进程的方法:
    p = multiprocessing.Pool(8)    while line:        p.apply_async(process_line,                      args=(...))        line = file.readline()    p.close()    p.join()
在 6C 的机器上对 2000 条日志进行回放,通过不同的并发数观察结果如下:
pool(1) 耗时:1292spool(6) 耗时:212spool(8) 耗时:155spool(12) :耗时:102s
发现性能提升还是很明显的,根据实际运行机器的CPU性能配置适合的并发数即可。

2.3 结果比较

整个系统中最核心的逻辑就是结果对比逻辑了。从新网关获取到的返回 response1 和老网关获取到的返回 response2 ,理论上是要一致的。初步的比较逻辑就是采用递归比较,将返回对象中的每个元素进行比较,判断内容是否一致。实际的运行过程中,往往会出现各种正常情况下返回结果不一致的情况:
  1. 接口请求返回含有时间字段,而时间字段是根据当前处理时间生成的。

  2. 一些写接口返回的是新生成的对象 id ,每次请求返回都不同。比如创建一个商品,下了一笔订单,返回的都是新生成的商品 id 或者订单号。

  3. 返回数据列表时,有些场景返回的数据是不保证顺序的,导致偶尔的比对失败。

针对上述情况,设计了兼容模式进行特殊处理。首先定义一个 dict 存储接口名以及需要忽略的字段
ignore_dict = {    'youzan.retail.stock.receiving.order.export.1.0.0': ['response'],    'youzan.retail.trademanager.refundorder.export.1.0.0': ['response'],    'youzan.retail.trade.api.service.pay.qrcode.1.0.1': ['url'],    'youzan.retail.product.spu.queryone.1.0.0': ['list']}
其中定义两个特殊字段 response 和 list:
  1. 对于 response 字段,只检查字段长度和类型,不检查内容是否一致,主要针对返回序列号、订单号的场景

  2. 对于 list 字段,对于其中的list类型做无序比较处理

核心对比方法如下,对于需要忽略的字段和类型进行特殊处理,减小误报:
def compare_data(data_1, data_2, COMP_SWITCH, ignore_list):    if isinstance(data_1, dict) and isinstance(data_2, dict):        diff_data = {}        only_data_1_has = {}        only_data_2_has = {}        d2_keys = list(data_2.keys())        for d1k in data_1.keys():            # 忽略某些字段            if COMP_SWITCH and __doignore(d1k, ignore_list):                continue            if d1k in d2_keys:                 d2_keys.remove(d1k)                temp_only_data_1_has, temp_only_data_2_has, temp_diff_data = compare_data(data_1.get(d1k),                                                                                          data_2.get(d1k), COMP_SWITCH,                                                                                          ignore_list)                if temp_only_data_1_has:                    only_data_1_has[d1k] = temp_only_data_1_has                if temp_only_data_2_has:                    only_data_2_has[d1k] = temp_only_data_2_has                if temp_diff_data:                    diff_data[d1k] = temp_diff_data            else:                only_data_1_has[d1k] = data_1.get(d1k)          for d2k in d2_keys:              if COMP_SWITCH and __doignore(d2k, ignore_list):                continue            only_data_2_has[d2k] = data_2.get(d2k)        return only_data_1_has, only_data_2_has, diff_data    else:        if data_1 == data_2:            return None, None, None        else:            if COMP_SWITCH and isinstance(data_1, list) and isinstance(data_2, list):                if __process_list(data_1, data_2, COMP_SWITCH, ignore_list):                    return None, None, None                else:                    return None, None, [data_1, data_2]            else:                return None, None, [data_1, data_2]
比对逻辑中加入了 COMP_SWITCH 开关,先按常规方式进行比较,当常规方式比较失败,再开启兼容模式进行比较,这样的做法是为了统计和观察哪些接口会使用兼容模式,使用兼容模式是否合理(还有兼容模式本身有没有bug~~)。4f7f6a0618dc0d37ba4b0ecd5f534b94.png

2.4 处理失败

每次执行任务会生成一个批次号,每个接口请求回放对比完成后,保存结果时也会带上批次号写入数据库。当任务执行完成后,会根据批次号将失败的结果再拉取出来进行重试。失败重试时,回放的逻辑和正常运行时略有不同,两次回放间隔了 30s 依次进行,这是避免当多次请求操作同一个对象时,因为第一次请求执行比较慢导致第二次请求命中唯一性校验而失败。当重试结果仍然失败,那么需要人工介入进行排查,判断是否是新网关逻辑的问题还是有字段需要特殊兼容的问题,进行处理。

2.5 结果判断和迁移策略

对于回放成功的接口,会记录当时回放成功返回的code等信息到结果表,同时记录对应code的次数加1,多次回放后会形成如下的统计结果:
{'200': 94, '234000001': 16, '234000002': 1}
当统计结果中存在正常的code和异常的code,同时没有回放失败记录的情况,就可以认为新网关对该接口的返回和老网关是一致的。回放通过的次数越多,可信性就越强。在这种情况下再将接口的流量切换到新网关,观察日常业务调用的情况,通过拉取新网关日志,记录返回的code和数目,当新网关返回的结果中也包含正常和异常的code时,认为在新网关也能正常请求,可以准备线上迁移了。通过QA环境的对比回放,同时在预发环境全量迁移后观察业务功能是否正常,最后线上逐步灰度流量和监控,最后确保接口迁移到新网关是正常的。

三、面向普通测试场景的解决方案

因为属于回归类的工具,这套系统除了可以解决验证新老网关返回对比是否一致的问题外,还能够帮助验证分支环境和基础环境之间请求对比是否一致的问题(尤其是系统重构的情况),对业务场景进行回归。为支持普通回归场景,只要在原来的系统上进行少量的改动:f5d10d2043cb417f190e004a9cfff6b8.png主要的改动就是配置需要回放的接口和需要回放到的环境。在回放阶段,之前是回放到新网关和老网关,现在都回放到同一网关,通过不同的 X-Service-Chain 请求头,控制请求到达不同的后端环境,然后获得返回值进行对比。

四、最后

本文提供了一种通过采集日志进行回放对比来解决接口对比一致性的思路,运用到了新老网关重构验证等回归场景。在新老网关迁移验证过程中,该系统起到了非常大的作用。通过该系统校验了接口 1200+,产生回放记录数目 30w 条,发现问题 30+ ,最终保障了线上切换工作的平稳进行。由于能够获取了接口和参数,可以进一步做一些权限、后端参数校验这样的检查,减少测试人员在这方面投入的测试时间,这套系统已经投入实际使用,后面有机会可以再介绍其实现原理和使用效果。期待未来能够挖掘出更多的使用场景,解决更多的验证问题,提高测试的效率和准确性。扩展阅读:
  1. 有赞业务中台测试团队介绍
  2. Dubbo 压测插件 2.0 —— 基于普通 API 调用
  3. 有赞精准测试实践
  4. K8S 在有赞 PaaS 测试环境中的实践
  5. 一次 Logback 发现的隐患
  6. 有赞持续集成容器化实践
  7. 前端精准测试探索:覆盖率实时统计工具

Vol.335

最后打个小广告:

有赞新零售测试团队长期招人,期待你的加入~,

如果你也是聪明、皮实、有要性的小伙伴

如果你对零售、SaaS有更多想法

欢迎投递简历:mali@youzan.com

加入我们,一起enjoy

ad00affd751ecffb898068eb315444e1.png‍‍‍

‍‍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

让人眼花缭乱的视错觉,太酷炫了!

盯~ 原理:这只死盯着人看的小短手并不是一个普通的纸模,它的眼睛鼻子和嘴也都不在正常的位置上,它的脸其实是凹下去的。 错觉的关键在于我们的大脑对凹面和凸面的加工方式,以及大脑对从图形中挑出一张脸的执着和能力。大脑想要…

swot分析模板_什么是SWOT分析图?怎样绘制SWOT分析思维导图,这样操作很简单

什么是SWOT分析图?大到企业小到个人都会使用到SWOT来对自身所具备的能力进行分析,这也印证了SWOT分析图的重要性,下面我们一起来深度了解SWOT分析吧!!一:什么是SWOT分析图所谓SWOT分析图是基于内外部竞争环…

有哪些命令行的软件堪称神器?

ag 比grep、ack更快的递归搜索文件内容。 tig 字符模式下交互查看git项目,可以替代git命令。 mycli mysql客户端,支持语法高亮和命令补全,效果类似ipython,可以替代mysql命令。 jq json文件处理以及格式化显示,支持高…

代码 | 一天一点代码坏味道(1)

【代码精进】| 总结/Edison Zhou作为一个后端工程师,想必在职业生涯中都写过一些不好维护的代码。本文是我学习《代码之丑》的学习总结,今天第一天发车,先来看看在命名上的一些常犯的坏味道。0为何要品代码坏味道Martin Flower在《重构》一书…

python base64编码_JS和Python实现AES算法

1. AES原理AES算法是典型的对称加密算法,AES原理可以学习这两篇文档:漫画:什么是AES算法:https://www.toutiao.com/i6783550080784794124/AES加密算法的详细介绍与实现:https://blog.csdn.net/qq_28205153/article/det…

当你老了,一生最后悔什么?大数据告诉你!

‍ 当你老了,一生最后悔什么?

@scheduled注解配置时间_SpringBoot2.0实战(32)配置定时任务

定时任务的几种实现方式:Timer:Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。Quartz:使用Quartz&…

小心 Enum Parse 中的坑

小心 Enum Parse 中的坑Intro最近使用枚举的时候,踩了一个小坑,分享一下,主要是枚举从 int 值转成枚举时可能会遇到Sample来看下面的示例:首先定义一个枚举:public enum Color : byte {Red 0,Green 1,Blue 2, }来看…

python判断列表是否为空_Jinja2: 判断返回的列表是否为空

我们在使用 Python 或者 Ansible 来进行自动化任务的时候常常会进行一些数据的组合和提取来生成文件。但是我们需要为不同的情况来做分析和进行判断。如果我们需要对返回的 list 来进行提取的时候我们常常只是运行一个 for loop 就解决了问题。如果输出如下所示:{&q…

在php中使用kind,KindEditor 4.x在PHP中的应用实例!

1.解压放入php项目静态资源文件夹,如下图:Paste_Image.png2.如果只是php使用,可以删除其它类型语言的文件夹,文件结构如下图:Paste_Image.png3.打开php文件夹,更改upload_json.php里文件上传目录文件夹至Up…

你的朋友国庆假期都去了哪里玩?微信大数据告诉你!最远的朋友圈签到竟然来自……

国庆中秋八天假 你是出门四处浪浪浪了 还是躺在家里看朋友圈里的世界名景 10月8日,微信发布《国庆假期微信大数据报告》 从出境人数、热门地区、境外消费等角度 全方位展示国庆期间微信用户的出游情况 哪些城市的人最爱出境游? 哪个国家是最热门的出境目…

mysql安装版和解压版哪个好_红米k30pro变焦版和荣耀30pro哪个好-哪个更值得入手...

红米k30pro变焦版和荣耀30pro,两款手机都有着很强的性能配置,也在同等的价位上,今天我们就来对比一下,看看红米k30pro变焦版和荣耀30pro哪个性价比更高,有哪些配置区别!一、主要参数对比荣耀30 Pro红米K30 …

记一次CPU持续100%及分析方法

背景 某天晚上八点多,突然收到一个 CPU 爆表的告警。过了一会,几个业务线就开始反馈系统变慢了。后面紧急处理了这台机器后,让业务先恢复正常。后续看了一下监控,拔凉拔凉的。这个服务是比较重要的一个老业务,.NET Fra…

php中请写出定义变量的两种方法,php定义变量几种

1、定义常量define("CONSTANT", "Hello world.");常量只能包含标量数据(boolean,integer,float 和 string),调用常量时,只需要简单的用名称取得常量的值,而不能加“$”符号。注: 常量和…

c语言三目运算符_C语言中的三目运算符是啥?有何用处?

一般来说,C语言中的三目运算符为a?b:c即有三个参与运算的量。由条件运算符组成条件表达式的一般形式为:表达式1? 表达式2:表达式3求值规则为:如果表达式1的值为真,则以表达式2 的值作为条件表达式的值,否…

Dotnet的局部函数和委托的对比

上一篇说了一下委托,这篇来说说局部函数和委托的对比。把委托和局部函数放成前后篇,是因为这两个内容很像,用起来容易混。需要了解委托相关内容,可以看这一篇 【传送门】使用委托表达式(Lambda)假设一个场景:我们有一个…

经纬度 c代码中定义_如何将TXT文本格式的批量经纬度值导入到奥维成为标签

文本编辑:示例1:最基本的,只批量导入WGS-84经纬度值成为标签,不需要导入标签名称。 文本编辑格式:经度值空格纬度值换行,如下图:示例2:除WGS-84经纬度外,还要导入标签名称…

中国式创新技术“步态识别”终于来临,你大胆地走两步,我就知道你是谁

放完假的数据君,回到办公室,苦恼该码一篇什么文章,来给各位送上“节后的祝福”。 这么想着,数据君便开始浏览最新的科技报道: 什么鬼!这难道是什么新兴的黑科技吗?! 数据君赶紧查了…

帆软获取上月的第一天与最后一天_《原神》岩港打工第一天怎么玩 岩港打工第一天玩法攻略...

《原神》在11月2日开启了岩港奇珍行记,玩家可以在璃月港进行打工了,可能有的小伙伴还不清楚第一天的打工要怎么做,所以小编这次就为大家带来了《原神》岩港打工第一天玩法攻略,感兴趣的小伙伴可以来看一下。岩港打工第一天玩法攻略…