Jenkins+Jmeter 整合效能測試

發表於2020-05-06

環境準備

  1. 本機中 Jenkins 執行在 centOS 7.2 為了不出其他問題可以保持一致,正常情況下別的版本也不會有什麼問題
  2. 安裝方式我這裡是下載 war 包啟動,有很多方便的安裝不限於這一種方式
  3. 需要 java 環境最好是 1.8
  4. 需要 Jmeter 包,版本不限,但是本地和遠端最好都一樣
  5. 負載機(slave)機器對 Jenkins 執行機器(master)做免密認證
  6. 所有機器內網打通,目前阿里雲和騰訊雲都支援的。除非不在一個區有點麻煩

免密認證

下面的 shell 指令碼可直接使用,IP 列表替換一下即可。我這裡都是內網 IP,寫幾個例項也無所謂。前提所有機器有一個相同的使用者名稱和密碼。然後根據提示輸入密碼即可如果提示 id_rsa.pub 檔案不存在 執行下面命令即可。

ssh-keygen -t rsa
#!/bin/bash

ip="192.168.251.167
192.168.251.219
192.168.251.208
192.168.251.223
192.168.251.180
192.168.251.93
192.168.251.226
192.168.251.160"

read -p "請輸入密碼:" userpassword

for i in $ip
do
        sshpass -p ${userpassword} ssh-copy-id -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa.pub $i
done

Jenkins 新建幾個工具

一鍵傳送檔案到所有伺服器。比如把本地 Jmeter 傳送到負載機上

  1. Jenkins 新建普通專案,新增構建引數 1 個檔案引數,2 個字串引數。可以見截圖

  2. 新增構建步驟 Execute shell

cat > $WORKSPACE/ip.txt << EOF
192.168.251.139
192.168.251.218
192.168.251.217
192.168.251.183
EOF

while read ip
do
    scp  $WORKSPACE/$JOB_NAME root@$ip:$filePath$SeverFile_Name &

done < ip.txt

wait
echo -e "time-consuming: $SECONDS    seconds" 
  1. 儲存執行即可

切割檔案,併傳送到遠端伺服器。(原來業務有券,每次壓測不可重複)

  1. Jenkins 新建普通專案,新增構建引數 1 個檔案引數,2 個字串引數。可以見截圖

  2. 新增構建步驟 Execute shell

ip=(192.168.251.217
192.168.251.183
192.168.251.222
192.168.251.96
192.168.251.140
192.168.251.153
192.168.251.123
192.168.251.250
192.168.251.38
192.168.251.175
192.168.251.229
192.168.251.105
192.168.251.76
192.168.251.230
192.168.251.60
192.168.251.205
192.168.251.240
192.168.251.193
#192.168.251.139
#192.168.251.218
)

fileNum=${#ip[@]}

mv file3 files.csv
cp files.csv $fileName_New.csv

# 此處需要python環境。把FileSplit.py放到當前workspace路徑下。
python FileSplit.py $fileNum $fileRows fileF files.csv

# 此處0 19 因為有20臺伺服器。伺服器數量暫時固定所以沒有動態計算上面伺服器個數。
for i in `seq 0 19 `
do 
    scp  $WORKSPACE/fileF$i.csv root@${ip[$i]}:/MCD/Jmeter/Product/$fileName_New.csv  &
done

wait
echo -e "time-consuming: $SECONDS    seconds"
# FileSplit.py
# coding:utf-8
from datetime import datetime
import sys
from os import path

def Main():
#    source_dir = 'C:/Users/WL/Desktop/code/file/fileTest.csv'
#    target_dir = 'C:/Users/WL/Desktop/code/file/'
#    print(path.sep, sys.path[0])

    target_dir = sys.path[0]+path.sep
    # 獲取檔案數,每個檔案行數,檔名字,原檔名稱
    files, rows, name, source_dir = int(sys.argv[1]), int(sys.argv[2]), target_dir+sys.argv[3], sys.argv[4]
    # 計數器,檔案字尾,存放資料
    flag, index, dataList = 0, 0, []
    print("切割為{0}個檔案,每個檔案{1}行".format(files, rows))
    print("Start {}".format("="*20), datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

    with open(source_dir, 'r') as f_source:
        for line in f_source:
            flag += 1
            dataList.append(line)
            if flag == rows:
                with open(name + str(index) + ".csv", 'w+') as f_target:
                    for data in dataList:
                        f_target.write(data)
                print(name + str(index) + ".csv")
                index += 1
                flag = 0
                dataList = []
            if index == files:
                break

    print("finish {}".format("="*20), datetime.now().strftime('%Y-%m-%d %H:%M:%S'))


if __name__ == "__main__":
    Main()    

批次執行 shell 專案,用於批次重啟 Jmeter-server,以及批次修改伺服器配置等

  1. Jenkins 新增普通專案,新增構建步驟 Execute shell
cat > $WORKSPACE/ip.txt << EOF
192.168.251.139
192.168.251.218
192.168.251.217
192.168.251.183
192.168.251.222
192.168.251.96
192.168.251.140
192.168.251.153
192.168.251.123
192.168.251.250
192.168.251.38
192.168.251.175
192.168.251.229
192.168.251.105
192.168.251.76
192.168.251.230
192.168.251.60
192.168.251.205
192.168.251.240
192.168.251.193
EOF

while read ip
do
    # 重啟Jmeter,需要伺服器存在AutoJmeter.sh檔案,在下面提供
    ssh -n  -o StrictHostKeyChecking=no   root@$ip "sh /data/shell/AutoJmeter.sh restart" &
    # 釋放linux快取佔用記憶體,在下面提供
#   ssh -n  -o StrictHostKeyChecking=no   root@$ip "sh /data/shell/dropCaches.sh" &
    # 更改 核心資訊-不瞭解勿用,需重啟or過載生效,此處不提供
#   ssh -n  -o StrictHostKeyChecking=no   root@$ip "echo 15 > /proc/sys/net/ipv4/tcp_fin_timeout 
    # 同上
#   echo 15 > /proc/sys/net/ipv4/tcp_tw_timeout
done < ip.txt

wait
echo -e "time-consuming: $SECONDS    seconds"
# dropCaches.sh
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches

# AutoJmeter.sh
# description: jmeter agent
myip=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"`
cmd="/MCD/Jmeter/bin/jmeter-server -Djava.rmi.server.hostname=$myip"
start(){
  echo "Start" $myip
  $cmd > /dev/null &
}

stop(){
    jmeter_pid=`ps aux | grep jmeter-server | grep -v grep | awk '{print $2}'`
    for pid in $jmeter_pid;do
    kill -9 $pid
    echo "Stop"$myip:$pid
    done
}

act=$1
case $act in
 'start')
   start;;
 'stop')
   stop;;
 'restart')
   stop
   sleep 2
   start;;
  *)
   echo '[start|stop|restart]';;
esac

上傳更新 Jmeter 指令碼到伺服器

  1. Jenkins 新增普通專案,增加構建 1 個檔案構建引數,2 個字串引數,見下圖

  2. 新增構建步驟 Execute shell

mv file $JENKINS_HOME/workspace/$filePath/$fileName

wait
echo -e "time-consuming: $SECONDS    seconds"
  1. 儲存執行就可以了。

Jenkins 與 Jmeter 結合指令碼配置

Jmeter 指令碼要求

  1. 編寫壓測指令碼要求:每個場景單獨使用一個執行緒組!!!!!非常重要,1 個執行緒組就是 1 個場景
  2. 測試計劃下新增 “使用者定義的變數”,“HTTP 請求預設值”,一定按照順序來。使用者定義的變數在測試計劃下面第一個。第二個是 HTTP 請求預設值,其次才是執行緒組等元件
  3. 使用者定義的變數新增變數有多少個場景(執行緒組)就新增多少個變數,變數名稱對應執行緒組名稱(全英文,不可有空格),對應的值是 ${__property(執行緒組名稱,,)},見截圖
  4. filePath,host,port,Ramp_Up,RunTime 這 5 個變數不是執行緒組的名稱,但是是必須的。格式見上圖。Jenkins 裡用,後面講。
  5. HTTP 請求預設值元件裡面伺服器地址寫 ${host},埠 ${port}。必須要這樣寫!!見下圖
  6. 執行緒組裡的執行緒數,ramp_up,持續時間。分別寫 ${執行緒組名稱},${Ramp_Up},${RunTime},見下圖。不同執行緒組只是執行緒數不同。ramp_up,持續時間都是相同的。
  7. 引數化檔案配置
  8. 確保指令碼沒有問題。儲存即可

Jenkins 配置

  1. Jenkins 新增普通專案,增加字串引數。數量由場景(執行緒組)個數 +4 確定
  2. 把指令碼里面使用者定義的變數所有名稱分別對應字串引數,一個個對應上。預設值是 0
  3. host:是服務端 IP 或者域名地址,可以給個預設值,避免每次輸入。或者改成選項引數,配置多個地址
  4. port:伺服器埠。開發沒有給的,按照協議型別確認。http 預設 80,https 預設 443。
  5. Ramp_Up:啟動執行緒花費時間,根據場景自定義,預設 0 也可以
  6. RunTime:壓測指令碼執行時間,單位是秒
  7. 新增構建步驟,Execute shell
echo 生成當前日期
date=$(date +%Y%m%d%H%M)
echo 當前時間: $date

if [ ! -d "$JENKINS_HOME/workspace/$JOB_NAME/Report" ];then
mkdir $JENKINS_HOME/workspace/$JOB_NAME/Report
else
echo "Skip"
fi

echo 配置地址
# Jenkins程式在伺服器上面的路徑
jmxPath=/root/.jenkins/workspace/$JOB_NAME/pickup.jmx
# 生成的報告路徑,可在Jenkins對應專案工作空間內直接開啟
ReportPath=$JENKINS_HOME/workspace/$JOB_NAME/Report/$date
# Jmeter執行結果檔案。比較大。建議伺服器重新掛個ssd磁碟專門儲存。也可定期清理
jtlPath=/data2/jtl/$JOB_NAME$date.jtl

echo 建立日期資料夾
mkdir $ReportPath


# Jmeter非GUI啟動命令 -nt 
# -l 指定日誌路徑,-R 負載機IP這裡直接指定。不需要更改.propreties檔案
# 重點是 -G 引數,對應上面Jenkins所有的構建引數,格式:-GGetDish=$GetDish 這是一個引數
# filePath:這裡如果指令碼有引數化檔案。要用這個引數。-GfilePath=伺服器儲存引數化檔案路徑
echo 執行Jmeter
cd /MCD/Jmeter/bin
sh jmeter.sh -nt $jmxPath -l $jtlPath -GoffLineRedeem=$offLineRedeem -GGetCouponDetail=$GetCouponDetail -GGetStoresExtra=$GetStoresExtra -GGetDynamicBaseMenuInfo=$GetDynamicBaseMenuInfo -Ghost=$host -Gport=$port  -GfilePath=/MCD/Jmeter/Product/ -GGetdish=$Getdish -GOrderReview=$OrderReview -GOrderSubmit=$OrderSubmit -GOrderDetail=$OrderDetail -GonePopUp=$onePopUp -GonGoingOrder=$onGoingOrder -GGetPackageDish=$GetPackageDish -GGetSingleDish=$GetSingleDish -GGetDishToShopCar=$GetDishToShopCar -GLogin=$Login -GRamp_Up=$Ramp_Up -GRunTime=$RunTime -GhomePage=${homePage} -GLoggerTest=${LoggerTest} -GnewOrderSubmit=$newOrderSubmit -GnewOrderReview=$newOrderReview -R 192.168.251.218,192.168.251.139,192.168.251.217,192.168.251.183,192.168.251.222,192.168.251.96,192.168.251.140,192.168.251.193 -e -o $ReportPath

# 輸出報告路徑
#echo ${JENKINS_URL}view/MCD58/job/$JOB_NAME/ws/Report/$date/index.html

  1. Jenkins 專案儲存之後。引數都是預設執行一次(相當於初始化)。在用上傳指令碼的專案上傳指令碼。
  2. 上傳指令碼。啟動即可壓測。下圖執行日誌

報告檢視

  1. 進入 Jenkins 專案內,點選工作空間(workspace)看到會有個 Report 資料夾和指令碼,進入 Report 資料夾就是根據壓測啟動時間生成的報告資料夾,如下圖

擴充套件

記錄請求日誌,方便定位業務問題(穩定性壓測不建議使用,beanshell 動態編譯執行有記憶體洩漏風險)

  1. 測試計劃下新增 beanshell 後置處理器,放到最後一個執行緒組下面,如下圖
import java.util.Date;
import java.text.SimpleDateFormat;

//  獲取請求Data
String requestData = prev.getSamplerData();
//  擷取requestBody
requestData = requestData.replaceAll("data:","");
requestData = requestData.replaceAll("no cookies","");
requestData = requestData.replaceAll("[\n\\s?]","");
// 獲取響應結果
String lableName = prev.getSampleLabel();
String responseData = prev.getResponseDataAsString().replaceAll("[\\n\\s]","");
String runTime = "/MCD/Jmeter/Product/log/Pickup"+bsh.args[0]+".csv";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");//設定日期格式
String nowTime = df.format(new Date()); // new Date()為獲取當前系統時間

FileWriter fstream = new FileWriter(runTime, true);
BufferedWriter out = new BufferedWriter(fstream);
out.write(nowTime + lableName + "requestData=="  + requestData + "responseData==" + responseData);
out.write(System.getProperty("line.separator"));
out.close();
fstream.close();
  1. Jenkins 新增普通專案,新增 1 個字串引數。2 個選擇引數,Project 和上面的 beanshell 中 runTime 引數欄位的路徑 Pickup 對應。這裡檢視日誌可以看不同專案。透過資料夾區分。
  2. 構建步驟 Execute shell
cat > $WORKSPACE/ip.txt << EOF
$SlaveIP
EOF

while read ip
do
    ssh -n  -o StrictHostKeyChecking=no   root@$ip "tail -n 10000 /MCD/Jmeter/Product/log/$Project$date.csv"

done < ip.txt



wait
echo -e "time-consuming: $SECONDS    seconds"

  1. 構建時間引數寫壓測執行的時間即可,格式:“201902031856”,檢視構建日誌即可

檢視所有 slaveJmeter 日誌。指令碼報錯。異常等資訊

  1. Jenkins 新建普通專案,新增構建步驟 Execute shell
cat > $WORKSPACE/ip.txt << EOF
#$SlaveIP
192.168.251.139
192.168.251.218
192.168.251.217
192.168.251.183
192.168.251.222
192.168.251.96
192.168.251.140
192.168.251.153
192.168.251.123
192.168.251.250
192.168.251.38
192.168.251.175
192.168.251.229
192.168.251.105
192.168.251.76
192.168.251.230
192.168.251.60
192.168.251.205
192.168.251.240
192.168.251.193
EOF

echo 生成當前日期
date=$(date +%Y%m%d%H%M)
echo 當前時間: $date
mkdir $WORKSPACE/log/$date

while read ip
do
    scp root@$ip:/root/jmeter-server.log $WORKSPACE/log/$date/${ip}_Jmeter_Server.log &
done < ip.txt

cp /MCD/Jmeter/bin/jmeter.log $WORKSPACE/log/$date/MasterJmeter.log

wait
echo -e "time-consuming: $SECONDS    seconds"
  1. 會在工作空間內新建一個當前時間的資料夾,把每個 slave 的執行日誌放進去,以 IP 命名對應檔案。

注意事項

指令碼內使用者定義的變數名稱,和執行緒組名稱,執行緒數變數,Jenkins 構件引數名稱,Jmeter 啟動指令碼的—G 引數務必相同。不能有空格,沒有任何空格。

記錄請求的 beanshell 會影響 Jmeter 本身效能。

Jenkins 開啟報告如果圖片空白,可以在專案內加個構件步驟 Execute system Groovy script,或者在啟動 Jenkins 的時候加引數指定。或者啟動後在 Jenkins 執行安全命令啥的忘記了。也可自行百度,原因是 Jenkins 遮蔽外部 Js 好像。

Jenkins 執行執行緒數量必須設定為 1,Jenkins 管理 -- 系統設定 -- 執行者數量

RunTime(壓測執行時間)最好不要低於 10 秒。原來啟動的 slave 過多 10 秒可能剛剛啟動完成。。。

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
# 我的Jenkins啟動命令。war包形式
nohup java -Xms2g -Xmx2g -Dhudson.model.DirectoryBrowserSupport.CSP="sandbox; default-src 'self';" -jar jenkins.war --httpPort=8002 >jenkins.log 2>jenkins.log &

寫的有點快。可能哪裡圖片少了或者啥。可以評論我補充一下。IP 地址我就不隱藏了。都是內網 ip。檔案路徑也不隱藏了。前公司伺服器都回收過了。按照我這個改改,有點動手和百度能力都能搞好。

後面有機會補充 Jenkins 賬號管理。賦權壓測給開發

有機會在補充執行緒設定為 1,壓測過程中其他任務都會等待的解決方法 -- 加 Jenkins 負載機,都搞好了。改天再更新怎麼操作··

相關文章