流量回放
看了群里讨论的问题,也是我一直在考虑如何解决的问题,这里做个梳理和总结,希望能在梳理的基础上理清思路,统一问题难点,希望有能力,有想法的同学能够更有针对性的提出解决方案
首先,将流量回放进行分类,无非就是读和写两大类:
读流量:处理起来比较简单,录制好后直接回放即可(注意是相同环境回放,或相同数据库基础上,如果是线上录制,可以直接在灰度或预发环境回放),总结下来读流量的特点是:
1. 不用担心数据没有准备好(因为录制时已经创建好了)
a. 问题:如果是不同环境,或影子库,就要考虑数据准备的问题
2. 也不用担心污染(只是读)
3. 可以重复回放(请求是幂等的)
写流量:与读流量的特点相反,读流量回放的优势,正是写流量回放的难点
1. 写流量本身就可以看做是准备数据的过程
a. 问题:写入新的数据后,如何通知到下游
2. 要么写入失败,要么产生新的数据(脏数据,产生污染,写影子库是一种解法)
a. 如创建用户,相同姓名和身份证号的,只能有一个,再次创建就会失败
b. 如创建订单,每次都生成新的订单号,问题同1.a是这个新生成的订单号,如何通知下游
3. 关于写接口的重复回放
a. 同2.a如果是写接口是在参数相同的情况下是排他的,则不能重复回放
b. 同2.b创建订单类的可以重复回放,但是一是会产生很多脏数据,二是如何通知下游
总结一下问题和群里讨论的解决方案:
1. 读流量的数据如何准备?
解1:人工介入,提前准备,回放时读取配置进行替换
解2:录制时也包括写流量,回放时也回放写流量,但是要提取写流程的出参,放入环境变量或本次回放的局部变量中,供下游接口在需要时进行替换
总的来说这两种解法都是一样的,就是数据偏移,个人感觉这也是柯南开源的一个亮点,提出了数据偏移的概念(当然具体实现是什么相的我还不清楚,但我个人觉得这个指明了解决问题的方向,让我们在解决这个问题上不再迷茫)
2. 写流量产生的响应出参,如何通知下游进行替换?
基于我个人对流量回放的思考和理解,我知道替换是必须要做的,具体怎么做,有一点思路,但是也没有实践过,希望柯南开源的大佬,能讲讲这部分是如何做到的?
3. 3.1 排他性的写流量,如何做到重复回放时不失败报错?
当然,这个问题的答案,最终也得回到偏移替换上,对入参进行偏移替换就可以了吗?如何做到,同问题2,希望有大佬回答下。
3.2 脏数据的问题,如何避免污染?
回答可能是影子库。但是从群内的讨论来看,好像意思是,在回放的时候写的流量可以写到影子库,但是读的流量,仍然可以读正式库。(请柯南的大佬确认一下,可以这么做吗?)这么做似乎没有什么问题,但是个人认为读写还是保持连贯比较高,即写在哪里,读就在哪里。
另外,多说一句,有人可能会问,重复回放失败的问题,写影子库不也就解决了吗?我之所以在3.1时没提影子库,就是因为这个问题不是影子库能解决的,因为一旦在影子库回放过一次之后,影子库里也就有相同的数据了,再次回放肯定还是会报错的。
这里再引申一下,其实关于重复写的问题我跟同事讨论过,他提出了一个很大胆的想法,即影子库镜像,即在流量录制前,将影子库做成镜像,每次流量回放时,将镜像库还原,这样做的话,可以做到录制流量一丝一毫修改都不需要帮,100%真实流量回放。我觉得这个想法很有创意,至于实践起来会遇到什么样的困难,大家可以思考一下,我这里就算抛转引玉了。
另外,总结了这么多,一直没有提到mock,这里再追加一下写流量的另外一种解法:即采用mock,如果是用jvm-sandbox-repeater做流量回放的话,可以做到方法级别的mock,即遇到写库的操作,并不真的执行写入操作,可以mock为返回指定的数据。仔细想想,如果采用mock的话即不用写影子库,又不用考虑数据偏移,真是个好东西,对于服务端是java应用来说,是个不错的选择。(当然,通用性有一定的限制,必须是java应用,另外,对于mock针对其他数据源的支持情况还有待考证,如ES,hbase,redis等)