天天看點

golang web接口壓力測試,性能測試(1)golang 壓力測試前言

文章目錄

  • golang 壓力測試
  • 前言
    • 1.普通應用程式 單元+壓力測試
      • (1). 普通單元測試
      • (2). 請求http單元測試
      • (3). 請求http壓力測試
    • 2.web接口壓力測試
      • 通過壓測函數直接調用内部handler函數

golang 壓力測試

前言

壓力測試之前,先看下單元測試,主要怕自己忘記,僅供參考

1.普通應用程式 單元+壓力測試

(1). 普通單元測試

go test是golang的單元測試指令

package main
import (
   "testing"
   )
func Testfunc (t *testing.T) {
   邏輯
}

           

go test -v test.go 代表運作test.go中所有的以Testf命名開始的函數

go test -v -run=“func” test.go 代表運作test.go中所有的Testffunc的函數

-run=“正則表達”

-v 代表檢視詳情

go test是先編譯然後運作,是以支援go build相關的參數

(2). 請求http單元測試

package main

import (
	"testing"
	"fmt"
	"io/ioutil"
	"net/http"
	"flag"
	"net/url"
	"io"
	"bytes"
	"encoding/json"
	"github.com/parnurzeal/gorequest"//gorequest需要go get
)

var (
//定義指令行參數,可以go test -arg=
	host *string
	ip *string
	reqUrl string
	sign *string
)

func init()  {
	//解析指令行參數
	host = flag.String("host", "127.0.0.1", "請求域名")
	ip = flag.String("ip", "127.0.0.1", "請求ip")
	fromIp = flag.String("from_ip", "10.78.48.10", "請求來源ip")
	sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "請求sign")
	flag.Parse()

}
func TestReq (t *testing.T) {
	send(t, "mock/req.json")
}
//mockFile 代表測試樣本資料
func send(t *testing.T, mockFile string)  {
	if mockFile == "" {
		mockFile = "mock/req.json"
	}
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("err =", err)
		}
	}()

	myRequest := url.Values{
	}
	myRequest.Set("sign", *sign)
	myRequest.Set("t", "1554198282793")
	myRequest.Set("syn" , "169dd6fc154")
	myRequest.Set("n" , "1")

	reqUrl = "http://" + *ip + "/req/" + "?" + myRequest.Encode()

	reqBody, err := ioutil.ReadFile(mockFile)

	if err != nil {
		error(err.Error()})
		return
	}
	
	Request := gorequest.New()
	Request.Header =  map[string]string{
		"X-Forwarded-For": *fromIp,
		"Host": *host,
		"Content-type" : "application/gzip",
	}
	
	resp, data, errs := Request.Post(reqUrl).Type("text").Send(string(reqBody)).EndBytes()
	errStr := make([]string, 0)
	for _, err := range errs {
		if err != nil {
			errStr = append(errStr, err.Error())
			return
		}
	}

	if resp.StatusCode != http.StatusOK {
		error([]string{"return status code error",  resp.StatusCode})
		return
	}
}
           

1:需要go get github.com/parnurzeal/gorequest

2:單元測試go test -v test.go -args -ip=test.com -host=test.com -sign=ccbb608d5c618ff7d55bedc19de4138f

-args後面跟參數清單

(3). 請求http壓力測試

package main

import (
	"testing"
	"fmt"
	"io/ioutil"
	"net/http"
	"flag"
	"net/url"
	"io"
	"bytes"
	"encoding/json"
	"github.com/parnurzeal/gorequest"//gorequest需要go get
)

var (
//定義指令行參數,可以go test -arg=
	host *string
	ip *string
	reqUrl string
	sign *string
)

func init()  {
	//解析指令行參數
	host = flag.String("host", "127.0.0.1", "請求域名")
	ip = flag.String("ip", "127.0.0.1", "請求ip")
	fromIp = flag.String("from_ip", "10.78.48.10", "請求來源ip")
	sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "請求sign")
	flag.Parse()

}
//壓力測試程式
func BenchmarkSend (b *testing.B) {
	t := new(testing.T)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		send(t, "")
	}
}
//mockFile 代表測試樣本資料
func send(t *testing.T, mockFile string)  {
	if mockFile == "" {
		mockFile = "mock/req.json"
	}
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("err =", err)
		}
	}()

	myRequest := url.Values{
	}
	myRequest.Set("sign", *sign)
	myRequest.Set("t", "1554198282793")
	myRequest.Set("syn" , "169dd6fc154")
	myRequest.Set("n" , "1")

	reqUrl = "http://" + *ip + "/req/" + "?" + myRequest.Encode()

	reqBody, err := ioutil.ReadFile(mockFile)

	if err != nil {
		error(err.Error()})
		return
	}
	
	Request := gorequest.New()
	Request.Header =  map[string]string{
		"X-Forwarded-For": *fromIp,
		"Host": *host,
		"Content-type" : "application/gzip",
	}
	
	resp, data, errs := Request.Post(reqUrl).Type("text").Send(string(reqBody)).EndBytes()
	errStr := make([]string, 0)
	for _, err := range errs {
		if err != nil {
			errStr = append(errStr, err.Error())
			return
		}
	}

	if resp.StatusCode != http.StatusOK {
		error([]string{"return status code error",  resp.StatusCode})
		return
	}
}
           

壓力測試:

go test -v -run=“none” -bench=“Send*” -benchtime=10s test.go

如果是帶 cpu和記憶體分析的

go test -v -run=“none” -bench=“Send*” -benchtime=10s -cpuprofile cpu.out -memprofile mem.out test.go

簡單的看cpu效果

輸出 上面cpu和記憶體資料 到圖檔

go tool pprof -png cpu.out > cpu.png

也可以直接進入指令行,help查詢相關指令:

go tool pprof cput.out

不過這種http請求壓測看記憶體和trace資料沒有什麼意義

下面介紹不同過http協定,不運作網絡http請求,直接壓測函數内部調用http handler函數,我使用的架構是echo

2.web接口壓力測試

通過壓測函數直接調用内部handler函數

我使用的架構是echo,其他架構應該也差不多,下面代碼僅供參考

package main

import (
	"testing"
	"fmt"
	"io/ioutil"
	"net/http"
	"flag"
	"net/url"
	"io"
	"bytes"
	"encoding/json"
	"github.com/parnurzeal/gorequest"//gorequest需要go get
)

var (
//定義指令行參數,可以go test -arg=
	host *string
	ip *string
	reqUrl string
	sign *string
)

func init()  {
	//解析指令行參數
	host = flag.String("host", "127.0.0.1", "請求域名")
	ip = flag.String("ip", "127.0.0.1", "請求ip")
	fromIp = flag.String("from_ip", "10.78.48.10", "請求來源ip")
	sign = flag.String("sign", "c8abc2d3efa0081478beb66e0542eb62", "請求sign")
	flag.Parse()

}
//壓力測試程式
func BenchmarkSend (b *testing.B) {
	//建構請求對象
	testData, _ := ioutil.ReadFile("mock/req.json")
	req, _ := http.NewRequest("POST", "/req_url", io.Reader(bytes.NewReader(testData)))
	req.Header.Set("X-Forwarded-For" , "10.78.48.10")

   //new一個response對象
	res := new(http.ResponseWriter)
	//new 一個context的函數
	cont := echo.New().NewContext(req, *res)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		req.Body = ioutil.NopCloser(io.Reader(bytes.NewReader(testData)))
		//handles包代表http handler的包
		//Handlers.Send代表handles包内部的真正邏輯函數
		handlers.Send.Post(cont)
	}
}
           

因為是直接調用的内部handler函數,是以CPU消耗和記憶體都比較真實

如果是帶 cpu和記憶體分析的

go test -v -run=“none” -bench=“Send*” -benchtime=10s -cpuprofile cpu.out -memprofile mem.out test.go

如果要分析攜程調用和網絡阻塞:

go test -v -run=“none” -bench=“Send*” -benchtime=10s -trace=trace.out test.go

go tool trace -http=“host:port” trace.out

通路http:host:port即可看到

go