接口測試 通用流量錄制回放工具 jvm-sandbox-repeater 嘗鮮記錄 (0716 跑通基于 console 的錄制回放)

陳恒捷 for PPmoney · 2019年07月07日 · 最後由 陳恒捷 回複于 2019年08月06日 · 4174 次閱讀
本帖已被設為精華帖!

背景

流量錄制回放近幾年已經越來越火。如阿裡開放的 doom 、滴滴開源的 RDebug 。而公司随着業務的快速發展,回歸測試的耗時越來越長,而且可以留給腳本維護的時間也并不多。因此也期望通過流量錄制回放,提高回歸測試的效率。

而剛好前幾天看到阿裡技術公衆号推送的一篇 jvm-sandbox 的文章,裡面有提到開源了其中做流量錄制回放的 repeater 模塊,所以嘗鮮一下。

為了方便記錄和閱讀,目前已經調整為系列文章。導航地址如下:

通用流量錄制回放工具 jvm-sandbox-repeater 嘗鮮記錄(本文)
通用流量錄制回放工具 jvm-sandbox-repeater 嘗鮮 (二)——repeater-console 使用
通用流量錄制回放工具 jvm-sandbox-repeater 嘗鮮 (三)—— repeater plugin 開發

jvm-sandbox-repeater 簡介

直接搬運官方文檔裡面的介紹吧

image_1df2dcu799j9pif16k3kje1s1j9.png-187.1kB
image_1df2ddd0jf2sk7rvjlqkduavm.png-61.8kB

github 地址:#

簡單的說,就是一個可以在不修改程序的情況下,進行一個服務http、java、dubbo入參及返回值的錄制。也支持快速擴展 api ,實現自己的插件。

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'

執行結果如下:

訪問鍊接時,repeater插件通過Repeat-TraceId=127000000001156034386424510000ed,唯一追蹤到了這一次請求,後台服務返回了JAVA是世界上最好的語言!,repeater把畫面定格在了這一秒并将結果和firstId綁定

step2 開始回放

curl -s 'http://127.0.0.1:8001/regress/slogan?Repeat-TraceId-X=127000000001156034386424510000ed'

無論我們多少次訪問這個地址,都将返回 Repeat-TraceId=127000000001156034386424510000ed 綁定的錄制信息JAVA是世界上最好的語言!;如果重新訪問Slogan後又會将最新的返回結果綁定到Repeat-TraceId=127000000001156034386424510000ed(為了快速演示,将鍊路追蹤的标志提到參數中進行透傳了)

完整執行結果如下:

  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>%

光是執行官方用例,當然不能滿足我們需要啦。我們來測試下,如果有多個 Repeat-TraceId ,是否可以分别錄制?

測試一下:

# 錄制一個 128 開頭的 traceId ,返回結果是 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 ,返回結果是 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

項目地址:#(官方文檔:# ,在官方的基礎上增加了請求日志打印的功能,便于查看回放效果)

clone 後,直接用 complete 裡面的完整示例,當做被測程序。

程序本身功能:當請求 http://localhost:8080/greeting?name=User 時,返回 {"id":2,"content":"Hello, User!"} 。其中 Hello 後面的名稱根據請求參數的 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,啟用攔截點和插件信息

根據需要修改repeater-config.json配置文件,具體配置含義參見:RepeaterConfig.java

repeater-config.json 配置文件,位置是 ~/.sandbox-module/cfg/repeater-config.json

具體的配置含義,官方提供的鍊接相對路徑有問題,無法跳轉。可以直接看這個鍊接:RepeaterConfig.java

此處根據這個被測項目的需要,進行了調整:

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更新:官方已經更新回放文檔啦,接着進行下去。

方式一:利用模塊暴露的http接口發起回放

官方的說明:

模塊暴露了回放接口,用于服務端發起遠程回放,具體如下:

url : #
params : _data

其中 port 是jvm-sandbox啟動時候綁定的port,可以在attach sandbox時增加-P
12580指定,或者執行~/sandbox/bin/sandbox.sh -p {pid} -v 查看SERVER_PORT _data
是由RepeatMeta經過hessian序列化之後的值,具體調用方式參見AbstractRecordService
和RecordFacadeApi

沒說明是用什麼 http 方法(後面通過看 AbstractRecordService.java 看出是 post ),而且 _data 需要用程序做RepeatMeta的 hessian 序列化。。。看起來就不是給我們這種命令行觸發用的。先跳過。

方式二:針對HTTP接口,可以像Slogan Demo一樣進行參數或者Header透傳方式進行MOCK回放

從前面的 repeater 日志,找到了幾個 traceId 。對應把它填到 Repeat-TraceId-X 參數中。(特别留意:回放會根據錄制時的 url 進行匹配。如果有參數是通過 url 傳遞的,必須錄制和回放都用一樣的參數

# 第一種寫法:
$ 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 的源碼 ,确實是有這樣的邏輯。而且上面兩個請求發出的時候, repeater.log 并沒有輸出錄制到請求的日志。

20190710更新:問題已解決,原因是前面的 repeater.json 配置不正确,遺漏了 javaSubInvokeBehaviors 相關配置,導緻返回值沒有被錄制到。

修正後,已經可以輸出正确的返回了。此時 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 ,可以看到它也是有暴露接口供調用的。因此也試試。

結果看了下,裡面提供的 standalone 和 mysql 兩種數據存儲方式,都不支持前面回放的存儲方法(存在 ~/.sandbox-module/repeater-data/record 中)。還得調整錄制方式才能進行回放。

7.16更新:此回放方式已跑通。詳細的記錄,請參照:通用流量錄制回放工具 jvm-sandbox-repeater 嘗鮮 (二)——repeater-console 使用

  • mybatis + redis

待補充

  • mq

待補充

源碼結構簡析

項目的源碼目錄如下:

$ 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

整體結構還是比較清晰的,有 plugin 目錄,便于擴展。也有 console 提供最簡要的流量管理。更詳細的,後續再慢慢研究。

吐槽

20190709更新:之前提到的3個吐槽點官方都第一時間修複了,效率很高。

這次補充兩個點:

1、repeater-console 的說明還是太少,雖然通過閱讀源碼大緻了解了它的功能,但不如有文檔方便,對新手不大友好。建議補充對應的說明。

2、回放提供的2個方式都不是太友好,方式二比較簡單,但不支持批量,不适合項目使用。方式一基本上隻能通過編程方式進行,無法直接通過接口進行。

建議提供一個命令行工具,可以直接通過命令行參數自動組裝和序列化輸出 _data 參數,便于用最簡單的方式調用回放功能。

小結

這個開源項目是 7月4日 出來的,由于是直接在 jvm 層控制,從原理上是通用性、擴展性最強的。而且目前也提供了 mybatis、http、dubbo 、Java 等插件,redis 預計7月放出,基本上覆蓋了項目中最常用的幾個中間件了。

雖然目前文檔還不是非常完整,但看得出來是有在用心維護的,昨天周六也有更新了一版文檔,補充了關于原理方面的說明。相信隻要有足夠的時間,會變得更完善的。

最後,非常感謝阿裡能開源一個這麼強大的組件,這樣一些質量技術方面沒法有太多資源投入的公司,也可以把流量錄制回放搞起來了。

共收到 16 條回複 時間 點贊

馬克,本社區說明最詳細最接地氣的一篇流量回放的文章。
期待樓主持續更新,樓主吃螃蟹,我吃蟹小爪😂

感謝大佬嘗鮮,期待更新!!

贊。效率真高!周末沒閑着,這麼快就出來這麼詳細的文章了。

有嘗試通過的大神嗎?

hello 回複

你這個嘗試出效果了嗎?

yuzijinrxh 回複

沒有,我在等陳大師出後續呢。

026 将本帖設為了精華貼 07月12日 11:12

大佬能給解釋下javaSubInvokeBehaviors,javaEntranceBehaviors這倆配置項的作用嘛,官方文檔看的不是太懂。。。

僅樓主可見
oh_test 回複

個人理解,javaSubInvokeBehaviors對應子調用,回放時會被 mock 掉(前提是回放時給到的入參和錄制時一緻),javaEntranceBehaviors是入口調用,回放時會被作為輸入。

舉個例子,a 接口對應的 controller 方法是 A,而 A 的實現裡面有調用另一個系統的接口,方法是 B 。錄制回放主要測試的是 A 方法的邏輯,不想依賴另一個系統。
此時,應該配置 javaEntranceBehaviors 為 A ,javaSubInvokeBehaviors 為 B 。

這樣回放時,A這個入口調用會被原封不動地發給應用進行回放,而 B 這個調用會被 mock 掉,應用實際不會發請求給另一個系統。

GCpigsic 回複

執行 cd complete mvn install && java -jar target/*.jar 之後,你是新開窗口執行 ps aux | grep target/gs-rest-service-0.1.0.jar 的嗎?

從你的描述上看,這個 jar 包在 attach 的時候是沒有在運行的。

僅樓主可見
GCpigsic 回複

剛找了台 windows 機器結合 git bash 試了下,貌似 ps 不會顯示所有進程。可能是 windows 下不支持。

可以試試直接改為用控制台找到的 pid ,繼續進行後續的 attach 步驟。

陳恒捷 回複

我試過直接綁定 控制台找到的 pid,繼續attach ,就會出現報這樣的錯

😭 😭

GCpigsic 回複

好吧。。。

建議弄台 linux 虛拟機,或者給官方提個 issue 。這塊比較深入 windows 和 linux 的差異了,我也不大知道怎麼解決比較好。

陳恒捷 回複

感謝你!!不得不說,文章真的棒很詳細!!!

GCpigsic 回複

感謝支持~

需要 登錄 後方可回複, 如果你還沒有賬号請點擊這裡 注冊
http://m.juhua446633.cn|http://wap.juhua446633.cn|http://www.juhua446633.cn||http://juhua446633.cn