自动化工具 [GoReplay 加 Diffy] 利用线上流量做回归测试

黑水 · 2019年10月14日 · 最后由 daylin 回复于 2024年03月22日 · 7464 次阅读

GoReplay 项目
Diffy 项目

对于没有副作用的接口(重复发送不会产生两份数据、不会产生多余的监控统计等等),就可以用这种方式方便的做回归测试。
部署三个不接外部流量的服务,两份老版本、一份新版本,把生产环境的流量复制到 Diffy 上。
如果生产环境支持通过请求头之类的方式区分测试流量和真实流量,就可以扩大使用范围。

本机启动一个服务 serverA,监听 8000 端口,访问 127.0.0.1:8000/testerhome 会返回一个 JSON

docker run --detach --publish=8000:80 --name=serverA nginx
docker exec -it serverA bash
apt update
apt install vim
vim /etc/nginx/conf.d/default.conf
# 添加一个 Path
location /testerhome {
      default_type application/json;
      return 200 '{"tag":"old","noise":"1"}';
}
# :wq 保存退出,重载 Nginx
nginx -s reload
# 退出容器
exit
# 输出 Nginx log 到终端上
docker logs -f serverA

访问 http://127.0.0.1:8000/testerhome

curl http://127.0.0.1:8000/testerhome 

可以看到返回了 {"tag":"old","noise":"1"},终端也输出了如下日志

172.17.0.1 - - [13/Oct/2019:07:04:04 +0000] "GET /testerhome HTTP/1.1" 200 28 "-" "curl/7.54.0" "-" "-"

再打开一个终端,启动一个服务 serverB,监听 8010 端口,配置同样的 Path 和响应

docker run --detach --publish=8010:80 --name=serverB nginx
docker exec -it serverB bash
apt update
apt install vim
vim /etc/nginx/conf.d/default.conf
# 添加一个 Path
location /testerhome {
      default_type application/json;
      return 200 '{"tag":"old","noise":"1"}';
}
# :wq 保存退出,重载 Nginx
nginx -s reload
# 退出容器
exit
# 输出 Nginx log 到终端上
docker logs -f serverB

再打开一个终端,下载解压 gor 后启动,将 8000 端口监听到的请求复制一份发送到 8010 端口

curl -L -O https://github.com/buger/goreplay/releases/download/v1.0.0/gor_1.0.0_x64.tar.gz
tar xvzf gor_1.0.0_x64.tar.gz
sudo ./gor --input-raw :8000 --output-http http://127.0.0.1:8010

访问 http://127.0.0.1:8000/testerhome

curl http://127.0.0.1:8000/testerhome 

可以看到 8010 端口上的 serverB 也产生了访问日志

172.17.0.1 - - [13/Oct/2019:07:14:04 +0000] "GET /testerhome HTTP/1.1" 200 28 "-" "curl/7.54.0" "-" "-"

再打开一个终端,启动一个服务 serverC,监听 8020 端口,配置同样的 Path ,响应是 {"tag":"old","noise":"2"}

docker run --detach --publish=8020:80 --name=serverC nginx
docker exec -it serverC bash
apt update
apt install vim
vim /etc/nginx/conf.d/default.conf
# 添加一个 Path
location /testerhome {
      default_type application/json;
      return 200 '{"tag":"old","noise":"2"}';
}
# :wq 保存退出,重载 Nginx
nginx -s reload
# 退出容器
exit
# 输出 Nginx log 到终端上
docker logs -f serverC

再打开一个终端,启动一个服务 serverD,监听 8030 端口,配置同样的 Path ,响应是 {"tag":"new","noise":"3"}

docker run --detach --publish=8030:80 --name=serverD nginx
docker exec -it serverD bash
apt update
apt install vim
vim /etc/nginx/conf.d/default.conf
# 添加一个 Path
location /testerhome {
      default_type application/json;
      return 200 '{"tag":"new","noise":"3"}';
}
# :wq 保存退出,重载 Nginx
nginx -s reload
# 退出容器
exit
# 输出 Nginx log 到终端上
docker logs -f serverD

再打开一个终端,下载或自己编译 Diffy 并启动

# 编译 Diffy 需要 jdk8,注意要用 opendiffy/diffy,和 twitter/diffy 不一样
git clone https://github.com/opendiffy/diffy.git
cd diffy
./sbt assembly
# 最终会生成 ./target/scala-2.12/diffy-server.jar,启动它
java -jar ./target/scala-2.12/diffy-server.jar \
-candidate=localhost:8030 \
-master.primary=localhost:8010 \
-master.secondary=localhost:8020 \
-service.protocol=http \
-serviceName=My-Service \
-proxy.port=:8880 \
-admin.port=:8881 \
-http.port=:8889 \
-rootUrl='localhost:8889' \
-summary.email="happy@a.com"

浏览器打开 http://127.0.0.1:8889,看到展示 Diffy 结果的界面

重启 gor,这次把将 8000 端口监听到的请求复制一份转发到 Diffy 的 8880 端口

sudo ./gor --input-raw :8000 --output-http http://127.0.0.1:8880

访问http://127.0.0.1:8000/testerhome ,可以看到 8010、8020、8030 端口上的三个服务都产生了访问日志

curl http://127.0.0.1:8000/testerhome 

Diffy 上出现了对比结果:

如果一个字段在 master.primary 和 master.secondary 上不一致,很有可能不是 bug,时间戳或者个性推荐之类的数据会这样。这时候可以 把 Exclude Noise 开关打开,排除这些 “噪声”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 18 条回复 时间 点赞

收藏了!!

opendiffy/diffy 和 twitter/diffy 有什么不一样的地方?

少年 回复

twitter/diffy 没法编译成功,有些依赖地址貌似是私有仓库。没仔细看其他有没有不一样

对于会关联数据库数据或者 redis 缓存数据的接口(如读取并返回数据、插入/更新数据),如果数据不一致就会导致 diff 误报。想请教下实际项目实践中,怎么保障三个节点的数据一致性的?

最关键的地方
对于写操作,对于线上流量势必是在线上特殊集群上回放,你们是怎么做到隔离流量并能保证不影响接口返回值的校验
参考阿里的 doom,是用的特殊 mock,但不知道具体是怎么 mock,这个确实非常麻烦
完全 mock,不去写数据库,diff 测试貌似没什么意义
不 mock,又回影响线上数据

陈恒捷 回复

有影响的接口现在都没做啦

有影响的接口太多了。只做没影响的都是边边角角,又没意义了

数据隔离的部分怎么处理,现在看不少是 jvm 探针➕桩和回放关联的方式?

至于缓存和写入 sql 的部分更多是靠单元测试和 code review

匿名 #10 · 2019年11月11日

-isotope.config='/Users/puneetkhanduri/code/sn126/isodemo/local.isotope' \
local.isotope 是什么文件呢,方便发给我一下吗?

黑水 #13 · 2019年11月17日 Author

这不是必填的,不加这个参数就行,opendiffy/diffy 这个项目现在是 sn126 在维护,用他们的服务才需要这个

8楼 已删除

在编译过程中有大量安装包安装失败,有相同问题吗?请问如何解决的

黑水 #15 · 2020年06月18日 Author
hebinying 回复

日志不全,用 markdown 文本,别截图

接口返回长度长的时候,有没有遇到文本被截断的情况,怎么解决的?

黑水 #16 · 2021年07月06日 Author
陈雷 回复

我记得有几个参数和限制有关的,看看文档

黑水 回复

多谢看了,也试了,由于只有一些大 response body 体会出现截断。最后发现升级到最新 27 天的 1.3.0_RC5 这个问题已经 fix,建议大家别用 1.2.0 版本。还是有一些抓包小问题。

真实场景中 同一个请求在 3 个环境不能同时有效 (cookie 校验失败,接口无权限),这种情况,大佬你们是怎么做的?

diffy 跑完后 log 文件存放在哪个目录底下?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册