1. 程式人生 > >效能測試中標記每個請求

效能測試中標記每個請求

在做效能測試過程中,遇到一個棘手的問題,開發讓我們復現幾個請求時間較長的請求,他們看日誌進行鏈路追蹤,查詢瓶頸所在。

這裡說一下框架中的處理邏輯:每個請求有一個唯一的requestid,由幾部分組成,還有一些演算法保證其唯一性。然後這個requestID貫穿整個請求過程的日誌,服務間的相互呼叫,與資料庫中介軟體的互動都依賴於這個requestID。

以往壓測都是寫了一個請求ID,並未對這個header做處理,現在得搞起來了。

首先我先新建了一個Java interface,用於使用閉包直接完成這個功能,還有就是其他標記方法:

package com.fun.base.interfaces;

import org.apache.http.client.methods.HttpRequestBase;

import java.io.Serializable;

/**
 * 用來標記request,為了記錄超時的請求
 */
public interface MarkRequest extends Serializable {

    /**
     * 用來標記base,刪除header其中一項,新增一項
     *
     * @param base
     * @return
     */
    public String mark(HttpRequestBase base);


}

然後我再ThreadLimitTimesCountThreadLimitTimeCount實現類中使用這個介面物件,兩個實現類的程式碼已經發過了效能測試框架第二版,這裡只寫一個:

  • 中間用到了深拷貝的方法,在之前也記錄過了拷貝HttpRequestBase物件,目前是每一個執行緒對應一個mark物件,而不是多執行緒共享,下面會看到效果。
package com.fun.frame.thead;

import com.fun.base.constaint.ThreadLimitTimesCount;
import com.fun.base.interfaces.MarkRequest;
import com.fun.config.Constant;
import com.fun.config.HttpClientConstant;
import com.fun.frame.Save;
import com.fun.frame.excute.Concurrent;
import com.fun.frame.httpclient.FanLibrary;
import com.fun.frame.httpclient.FunRequest;
import com.fun.frame.httpclient.GCThread;
import com.fun.utils.Time;
import org.apache.http.client.methods.HttpRequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * http請求多執行緒類
 */
public class RequestThreadTimes extends ThreadLimitTimesCount {

    private static final long serialVersionUID = -2751325651625435070L;

    static Logger logger = LoggerFactory.getLogger(RequestThreadTimes.class);

    /**
     * 記錄總的請求超時的情況
     */
    public static Vector<String> requestMark = new Vector<>();

    /**
     * 請求
     */
    public HttpRequestBase request;

    /**
     * 標記物件
     */
    public MarkRequest mark;

    /**
     * 記錄當前執行緒超時請求
     */
    public List<String> marks = new ArrayList<>();

    /**
     * 單請求多執行緒多次任務構造方法
     *
     * @param request 被執行的請求
     * @param times   每個執行緒執行的次數
     */
    public RequestThreadTimes(HttpRequestBase request, int times) {
        this.request = request;
        this.times = times;
        this.mark = new MarkRequest() {

            private static final long serialVersionUID = 5599842482575655279L;

            @Override
            public String mark(HttpRequestBase base) {
                return EMPTY;
            }
        };
    }

    /**
     * 應對對每個請求進行標記的情況
     *
     * @param request
     * @param times
     * @param mark
     */
    public RequestThreadTimes(HttpRequestBase request, int times, MarkRequest mark) {
        this(request, times);
        this.mark = mark;
    }

    protected RequestThreadTimes() {
        super();
    }

    @Override
    public void before() {
        super.before();
        GCThread.starts();
    }

    /**
     * @throws Exception
     */
    @Override
    protected void doing() throws Exception {
        FanLibrary.excuteSimlple(request);
    }

    @Override
    protected void after() {
        requestMark.addAll(marks);
        GCThread.stop();
        synchronized (RequestThreadTimes.class) {
            if (countDownLatch.getCount() == 0) Save.saveStringList(requestMark, Constant.DEFAULT_STRING);
        }
    }

    @Override
    public void run() {
        try {
            before();
            List<Long> t = new ArrayList<>();
            long ss = Time.getTimeStamp();
            for (int i = 0; i < times; i++) {
                try {
                    String m = this.mark.mark(request);
                    long s = Time.getTimeStamp();
                    doing();
                    long e = Time.getTimeStamp();
                    long diff = e - s;
                    t.add(diff);
                    if (diff > HttpClientConstant.MAX_ACCEPT_TIME) marks.add(diff + CONNECTOR + m);
                    excuteNum++;
                    if (status()) break;
                } catch (Exception e) {
                    logger.warn("執行任務失敗!", e);
                    errorNum++;
                }
            }
            long ee = Time.getTimeStamp();
            logger.info("執行次數:{},錯誤次數: {},總耗時:{} s", times, errorNum, (ee - ss) / 1000 + 1);
            Concurrent.allTimes.addAll(t);
        } catch (Exception e) {
            logger.warn("執行任務失敗!", e);
        } finally {
            if (countDownLatch != null)
                countDownLatch.countDown();
            after();
        }

    }

    @Override
    public RequestThreadTimes clone() {
        RequestThreadTimes threadTimes = new RequestThreadTimes();
        threadTimes.times = this.times;
        threadTimes.request = FunRequest.cloneRequest(request);
        threadTimes.mark = deepClone(mark);
        return threadTimes;
    }

}

我自己寫了一個使用Demo:

 def "測試併發情況下記錄響應標記符的"() {
        given:
        HttpGet httpGet = FanLibrary.getHttpGet("https://cn.bing.com/");
        MarkRequest mark = new MarkRequest() {

            String m;

            @Override
            public String mark(HttpRequestBase base) {
                base.removeHeaders("requestid");
                m = m == null ? RString.getStringWithoutNum(4) : m
                String value = "fun_" + m + CONNECTOR + Time.getTimeStamp();
                base.addHeader("requestid", value);
                return value;
            }

        };
        FanLibrary.getHttpResponse(httpGet);
        HttpClientConstant.MAX_ACCEPT_TIME = -1
        RequestThreadTimes threadTimes = new RequestThreadTimes(httpGet, 2, mark);
        new Concurrent(threadTimes, 2).start();

        output(RequestThreadTimes.requestMark)

    }

下面是記錄的結果如下,可以看到,一共出現了兩個m的值,後面跟的是時間戳,這樣既保證了requestID唯一性,也可以對執行緒進行歸類。

80_fun_QkhQ_1578367527661
103_fun_QkhQ_1578367527742
101_fun_zwtk_1578367527661
107_fun_zwtk_1578367527763

  • 鄭重宣告:文章首發於公眾號“FunTester”,禁止第三方(騰訊雲除外)轉載、發表。

技術類文章精選

  • java一行程式碼列印心形
  • Linux效能監控軟體netdata中文漢化版
  • 效能測試框架第二版
  • 如何在Linux命令列介面愉快進行效能測試
  • 圖解HTTP腦圖
  • 將swagger文件自動變成測試程式碼
  • 五行程式碼構建靜態部落格
  • 基於java的直線型介面測試框架初探
  • Selenium 4.0 Alpha更新日誌
  • Selenium 4.0 Alpha更新實踐
  • 如何統一介面測試的功能、自動化和效能測試用例

非技術文章精選

  • 為什麼選擇軟體測試作為職業道路?
  • 寫給所有人的程式設計思維
  • 成為自動化測試的7種技能
  • 如何在DevOps引入自動化測試
  • Web端自動化測試失敗原因彙總
  • 如何在DevOps引入自動化測試
  • 測試人員常用藉口
  • 2019年瀏覽器市場份額排行榜
  • API測試基礎
  • API自動化測試指南
  • 未來的QA測試工程師