本文作者陳恒捷是TesterHome社區(qū)主編抚太,第十屆MTSC大會上海站-開源專場出品人对湃。先后在PP助手养铸、PPmoney讼积、荔枝等公司從事測試效能提升相關工作扛门,在測試技術及效率提升方面有豐富的經(jīng)驗積累沐祷。
背景
流量錄制回放近幾年已經(jīng)越來越火。如阿里開放的 doom 攒岛、滴滴開源的 RDebug 赖临。而公司隨著業(yè)務的快速發(fā)展,回歸測試的耗時越來越長灾锯,而且可以留給腳本維護的時間也并不多兢榨。因此也期望通過流量錄制回放,提高回歸測試的效率顺饮。
而剛好前幾天看到阿里技術公眾號推送的一篇 jvm-sandbox 的文章吵聪,里面有提到開源了其中做流量錄制回放的 repeater 模塊,所以嘗試一下领突。
jvm-sandbox-repeater 簡介
直接搬運官方文檔里面的介紹吧
github 地址:https://github.com/alibaba/jvm-sandbox-repeater
簡單的說暖璧,就是一個可以在不修改程序的情況下案怯,進行一個服務http君旦、java、dubbo入?yún)⒓胺祷刂档匿浿瞥凹睢R仓С挚焖贁U展 api 金砍,實現(xiàn)自己的插件。
standalone 快速開始
step0 安裝sandbox/啟動bootstrap
cd bin
./bootstrap.sh
等待SpringBoot應用啟動完成 -> Started Application in 4.797 seconds (JVM running for 6.586)
step1 開始錄制
curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId=127000000001156034386424510000ed'
執(zhí)行結(jié)果如下:
訪問鏈接時麦锯,repeater插件通過Repeat-TraceId=127000000001156034386424510000ed恕稠,唯一追蹤到了這一次請求,后臺服務返回了
JAVA是世界上最好的語言!
扶欣,repeater把畫面定格在了這一秒并將結(jié)果和firstId綁定
step2 開始回放
curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId-X=127000000001156034386424510000ed'
無論我們多少次訪問這個地址鹅巍,都將返回 Repeat-TraceId=127000000001156034386424510000ed 綁定的錄制信息
JAVA是世界上最好的語言!
;如果重新訪問Slogan后又會將最新的返回結(jié)果綁定到Repeat-TraceId=127000000001156034386424510000ed(為了快速演示料祠,將鏈路追蹤的標志提到參數(shù)中進行透傳了)
完整執(zhí)行結(jié)果如下:
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId=127000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">JAVA是世界上最好的語言!</h1>%
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId-X=127000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">JAVA是世界上最好的語言!</h1>%
光是執(zhí)行官方用例骆捧,當然不能滿足我們需要啦。我們來測試下髓绽,如果有多個 Repeat-TraceId 敛苇,是否可以分別錄制?
測試一下:
# 錄制一個 128 開頭的 traceId 顺呕,返回結(jié)果是 java
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId=128000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">JAVA是世界上最好的語言!</h1>%
# 錄制一個 129 開頭的 traceId 枫攀,返回結(jié)果是 Python
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId=129000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">Python是世界上最好的語言!</h1>%
# 回放前面 128 的流量
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId-X=128000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">JAVA是世界上最好的語言!</h1>%
# 回放前面 129 的流量
? bin git:(master) curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId-X=129000000001156034386424510000ed'
<h1 align="center" style="color:red;margin-top:300px">Python是世界上最好的語言!</h1>%
看來確實是有效的岸啡。
錄制目標服務
前面只是個簡單的練手阎毅,實際用不用得了,當然的實際項目說話啦梅割。
為了簡單启盛,此處使用了幾個 spring boot 的示例項目當做實際項目使用扫夜。
- restful-api
項目地址:https://github.com/chenhengjie123/gs-rest-service(官方文檔:https://spring.io/guides/gs/rest-service/ 楞泼,在官方的基礎上增加了請求日志打印的功能,便于查看回放效果)
clone 后笤闯,直接用 complete
里面的完整示例堕阔,當做被測程序。
程序本身功能:當請求 http://localhost:8080/greeting?name=User
時颗味,返回 {"id":2,"content":"Hello, User!"}
超陆。其中 Hello 后面的名稱根據(jù)請求參數(shù)的 name 自動替換,id 會自動遞增浦马。
接下來时呀,按照官方的說明,進行操作:
step0 安裝sandbox和插件到應用服務器
curl -s http://sandbox-ecological.oss-cn-hangzhou.aliyuncs.com/install-repeater.sh | sh
正常日志輸出:
====== begin to install sandbox and repeater module ======
====== step 0 begin to download sandbox package ======
====== step 1 begin to download repeater module package ======
====== install finished ======
step1 修改repeater-config.json晶默,啟用攔截點和插件信息
根據(jù)需要修改repeater-config.json配置文件谨娜,具體配置含義參見:RepeaterConfig.java
repeater-config.json 配置文件,位置是 ~/.sandbox-module/cfg/repeater-config.json
具體的配置含義磺陡,官方提供的鏈接相對路徑有問題趴梢,無法跳轉(zhuǎn)”宜可以直接看這個鏈接:RepeaterConfig.java
此處根據(jù)這個被測項目的需要坞靶,進行了調(diào)整:
20190710更新:之前的配置遺漏了 javaSubInvokeBehaviors 的設定,會導致回放的時候返回沒有被 mock 掉蝴悉,看不出效果彰阴。下面為更正后的配置
{
"degrade": false,
"exceptionThreshold": 1000,
"httpEntrancePatterns": [
"^/greeting.*$"
],
"javaEntranceBehaviors": [
],
"javaSubInvokeBehaviors": [
{
"classPattern": "hello.GreetingController",
"includeSubClasses": false,
"methodPatterns": [
"greeting"
]
}
],
"pluginIdentities": [
"http",
"java-subInvoke"
],
"repeatIdentities": [
"java",
"http"
],
"sampleRate": 10000,
"useTtl": false
}
step2 attach sandbox到目標進程
先到剛才 clone spring boot 示例項目的根目錄,啟動被測應用
# 在示例項目 clone 后的根目錄中運行
cd complete
mvn install && java -jar target/*.jar
# 查看進程 id
ps aux | grep target/gs-rest-service-0.1.0.jar
hengjiechen 7737 0.0 0.0 4267932 616 s007 R+ 10:23AM 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn rest-service
hengjiechen 7306 0.0 3.2 7935464 269884 s000 S+ 10:23AM 0:14.84 /usr/bin/java -jar target/gs-rest-service-0.1.0.jar
可以看到拍冠,進程 id 為 7306 尿这。然后開始 attach
cd ~/sandbox/bin
# 假設目標JVM進程號為'7306' 。-P 是設定 jvm-sandbox 的端口號庆杜,后面回放需要用到
./sandbox.sh -p 7306 -P 12580
小技巧:上述的找進程 id + attach 過程射众,可以用這個命令一鍵達成:
# -P 是設定 jvm-sandbox 的端口號,后面回放需要用到
sh ~/sandbox/bin/sandbox.sh -p `ps -ef | grep "target/gs-rest-service-0.1.0.jar" | grep -v grep | awk '{print $2}'` -P 12580
控制臺輸出:
NAMESPACE : default
VERSION : 1.2.1
MODE : ATTACH
SERVER_ADDR : account.jetbrains.com
SERVER_PORT : 53866
UNSAFE_SUPPORT : ENABLE
SANDBOX_HOME : /Users/hengjiechen/sandbox/bin/..
SYSTEM_MODULE_LIB : /Users/hengjiechen/sandbox/bin/../module
USER_MODULE_LIB : /Users/hengjiechen/sandbox/sandbox-module;~/.sandbox-module;
SYSTEM_PROVIDER_LIB : /Users/hengjiechen/sandbox/bin/../provider
EVENT_POOL_SUPPORT : DISABLE
查看repeater日志看模塊和插件加載情況
$ tail -200f ~/logs/sandbox/repeater/repeater.log
...
2019-07-07 10:24:14 INFO initializing logback success. file=/Users/hengjiechen/.sandbox-module/cfg/repeater-logback.xml;
2019-07-07 10:24:14 INFO module on loaded,id=repeater,version=1.0.0,mode=ATTACH
2019-07-07 10:24:14 INFO onActive
2019-07-07 10:24:14 INFO pull repeater config success,config=com.alibaba.jvm.sandbox.repeater.plugin.domain.RepeaterConfig@4dddeb36
2019-07-07 10:24:15 INFO enable plugin http success
2019-07-07 10:24:15 INFO add watcher success,type=http,watcherId=1000
2019-07-07 10:24:16 INFO register event bus success in repeat-register
step3 開始錄制和回放
錄制幾個請求:
? complete git:(master) ? curl -s 'http://localhost:8080/greeting?name=User'
{"id":1,"content":"Hello, User!"}%
? complete git:(master) ? curl -s 'http://localhost:8080/greeting?name=User2'
{"id":2,"content":"Hello, User2!"}%
? complete git:(master) ? curl -s 'http://localhost:8080/greeting?name=User3'
{"id":3,"content":"Hello, User3!"}%
? complete git:(master) ? curl -s 'http://localhost:8080/greeting'
{"id":4,"content":"Hello, World!"}%
對應看到 repeater 的日志增加了幾個輸出:
...
2019-07-09 16:31:20 INFO broadcast success,traceId=192168015059156266108005510001ed,resp=success
2019-07-09 16:31:26 INFO broadcast success,traceId=192168015059156266108604210002ed,resp=success
2019-07-09 16:31:31 INFO broadcast success,traceId=192168015059156266109135010003ed,resp=success
2019-07-09 16:31:36 INFO broadcast success,traceId=192168015059156266109622310004ed,resp=success
好了欣福,試試回放责球。
怎么回放?文檔完全沒提到拓劝,一臉懵逼雏逾。。郑临。等官方文檔更新把栖博。
20190709更新:官方已經(jīng)更新回放文檔啦,接著進行下去厢洞。
方式一:利用模塊暴露的http接口發(fā)起回放
官方的說明:
模塊暴露了回放接口仇让,用于服務端發(fā)起遠程回放典奉,具體如下:
url : http://ip:port/sandbox/default/module/http/repeater/repeat
params : _data其中 port 是jvm-sandbox啟動時候綁定的port,可以在attach sandbox時增加-P
12580指定丧叽,或者執(zhí)行~/sandbox/bin/sandbox.sh -p {pid} -v 查看SERVER_PORT _data
是由RepeatMeta經(jīng)過hessian序列化之后的值卫玖,具體調(diào)用方式參見AbstractRecordService
和RecordFacadeApi
沒說明是用什么 http 方法(后面通過看 AbstractRecordService.java 看出是 post ),而且 _data 需要用程序做RepeatMeta的 hessian 序列化踊淳。假瞬。∮爻ⅲ看起來就不是給我們這種命令行觸發(fā)用的脱茉。先跳過。
方式二:針對HTTP接口垄开,可以像Slogan Demo一樣進行參數(shù)或者Header透傳方式進行MOCK回放
從前面的 repeater 日志琴许,找到了幾個 traceId 。對應把它填到 Repeat-TraceId-X 參數(shù)中溉躲。(特別留意:回放會根據(jù)錄制時的 url 進行匹配榜田。如果有參數(shù)是通過 url 傳遞的,必須錄制和回放都用一樣的參數(shù))
# 第一種寫法:
$ curl -s 'http://localhost:8080/greeting' -H "Repeat-TraceId-X:192168015059156266109622310004ed"
{"id":4,"content":"Hello, World!"}%
$ curl -s 'http://localhost:8080/greeting?name=User3' -H "Repeat-TraceId-X:192168015059156266109135010003ed"
{"id":3,"content":"Hello, User3!"}%
# 第二種寫法:
$ curl -s 'http://localhost:8080/greeting?Repeat-TraceId-X=192168015059156266109622310004ed'
{"id":4,"content":"Hello, World!"}%
id 還在遞增签财,回放沒生效串慰。但看了下 plugin 的源碼 偏塞,確實是有這樣的邏輯唱蒸。而且上面兩個請求發(fā)出的時候, repeater.log 并沒有輸出錄制到請求的日志灸叼。
20190710更新:問題已解決神汹,原因是前面的 repeater.json 配置不正確,遺漏了 javaSubInvokeBehaviors
相關配置古今,導致返回值沒有被錄制到屁魏。
修正后,已經(jīng)可以輸出正確的返回了捉腥。此時 repeater.log 也會對應輸出日志:
2019-07-10 17:19:25 INFO initializing logback success. file=/Users/chenhengjie/.sandbox-module/cfg/repeater-logback.xml;
2019-07-10 17:19:25 INFO module on loaded,id=repeater,version=1.0.0,mode=ATTACH
2019-07-10 17:19:25 INFO onActive
2019-07-10 17:19:25 INFO pull repeater config success,config=com.alibaba.jvm.sandbox.repeater.plugin.domain.RepeaterConfig@61e09144
2019-07-10 17:19:25 INFO enable plugin http success
2019-07-10 17:19:26 INFO add watcher success,type=http,watcherId=1000
2019-07-10 17:19:26 INFO enable plugin java-subInvoke success
2019-07-10 17:19:26 INFO add watcher success,type=java,watcherId=1003
2019-07-10 17:19:27 INFO register event bus success in repeat-register
2019-07-10 17:19:31 INFO broadcast success,traceId=192168015059156275037036610001ed,resp=success
2019-07-10 17:19:32 INFO broadcast success,traceId=192168015059156275037271710002ed,resp=success
2019-07-10 17:19:41 INFO find target invocation by PARAMETER_MATCH,identity=java://hello.GreetingController/greeting~S,invocation=com.alibaba.jvm.sandbox.repeater.plugin.domain.Invocation@3af687c7
2019-07-10 17:19:52 INFO find target invocation by PARAMETER_MATCH,identity=java://hello.GreetingController/greeting~S,invocation=com.alibaba.jvm.sandbox.repeater.plugin.domain.Invocation@6b6f0533
最后兩行就是對應返回錄制的 response 了氓拼。
方式三:使用 repeater-console 做回放
官方文檔沒有明確給出這個方式,但通過查看 repeater-console 里面的 readme 抵碟,可以看到它也是有暴露接口供調(diào)用的桃漾。因此也試試。
結(jié)果看了下拟逮,里面提供的 standalone 和 mysql 兩種數(shù)據(jù)存儲方式撬统,都不支持前面回放的存儲方法(存在 ~/.sandbox-module/repeater-data/record
中)。還得調(diào)整錄制方式才能進行回放敦迄。
7.16更新:此回放方式已跑通恋追。詳細的記錄凭迹,將在《通用流量錄制回放工具 jvm-sandbox-repeater 實踐 (二)——repeater-console 使用》中分享。
- mybatis + redis
待補充
- mq
待補充
源碼結(jié)構簡析
項目的源碼目錄如下:
$ tree -L 2 | grep -v iml
.
├── LICENSE
├── Readme.md
├── bin
│ ├── bootstrap.sh
│ ├── health.sh
│ ├── install-local.sh
│ ├── install-repeater.sh
│ ├── package.sh
│ ├── repeater-config.json
│ ├── repeater-logback.xml
│ └── repeater.properties
├── docs
│ ├── plugin-development.md
│ ├── slogan-demo.md
│ └── user-guide-cn.md
├── hessian-lite
│ ├── pom.xml
│ └── src
├── pom.xml
├── repeater-client
│ ├── pom.xml
│ └── src
├── repeater-console
│ ├── Readme.md
│ ├── pom.xml
│ ├── repeater-console-common
│ ├── repeater-console-dal
│ ├── repeater-console-service
│ ├── repeater-console-start
├── repeater-module
│ ├── pom.xml
│ └── src
├── repeater-plugin-api
│ ├── pom.xml
│ └── src
├── repeater-plugin-core
│ ├── pom.xml
│ └── src
├── repeater-plugins
│ ├── dubbo-plugin
│ ├── http-plugin
│ ├── ibatis-plugin
│ ├── java-plugin
│ ├── mybatis-plugin
│ ├── pom.xml
│ ├── redis-plugin
└── travis.sh
整體結(jié)構還是比較清晰的苦囱,有 plugin 目錄嗅绸,便于擴展。也有 console 提供最簡要的流量管理撕彤。更詳細的朽砰,后續(xù)再慢慢研究。
吐槽
20190709更新:之前提到的3個吐槽點官方都第一時間修復了喉刘,效率很高瞧柔。
這次補充兩個點:
1、repeater-console 的說明還是太少睦裳,雖然通過閱讀源碼大致了解了它的功能造锅,但不如有文檔方便,對新手不大友好廉邑。建議補充對應的說明哥蔚。
2、回放提供的2個方式都不是太友好蛛蒙,方式二比較簡單糙箍,但不支持批量,不適合項目使用牵祟。方式一基本上只能通過編程方式進行深夯,無法直接通過接口進行。
建議提供一個命令行工具诺苹,可以直接通過命令行參數(shù)自動組裝和序列化輸出 _data 參數(shù)咕晋,便于用最簡單的方式調(diào)用回放功能。
小結(jié)
這個開源項目是 7月4日 出來的收奔,由于是直接在 jvm 層控制掌呜,從原理上是通用性、擴展性最強的坪哄。而且目前也提供了 mybatis质蕉、http、dubbo 翩肌、Java 等插件模暗,redis 預計7月放出,基本上覆蓋了項目中最常用的幾個中間件了摧阅。
雖然目前文檔還不是非常完整汰蓉,但看得出來是有在用心維護的,昨天周六也有更新了一版文檔棒卷,補充了關于原理方面的說明顾孽。相信只要有足夠的時間祝钢,會變得更完善的。
最后若厚,非常感謝阿里能開源一個這么強大的組件拦英,這樣一些質(zhì)量技術方面沒法有太多資源投入的公司,也可以把流量錄制回放搞起來了测秸。
本文首發(fā)于TesterHome社區(qū)疤估,點此鏈接可查看原文并與作者直接交流。
今日份的知識已攝入~
想了解更多前沿測試開發(fā)技術:歡迎關注「第十屆MTSC大會·上忽耄」>>>
1個主會場+11大專場铃拇,大咖云集精英齊聚