压测工具优化

This commit is contained in:
meixiongfeng 2022-12-08 17:03:32 +08:00
parent ecbcd34e31
commit 7a08d8888a
10 changed files with 42 additions and 620 deletions

View File

@ -4,7 +4,6 @@ package model
import (
"errors"
"fmt"
"io"
"net/http"
"strings"
"sync"
@ -23,13 +22,13 @@ const (
// 支持协议
const (
// FormTypeHTTP http 协议
FormTypeHTTP = "http"
// FormTypeWebSocket webSocket 协议
/// FormTypeHTTP http 协议
//FormTypeHTTP = "http"
/// FormTypeWebSocket webSocket 协议
FormTypeWebSocket = "webSocket"
// FormTypeGRPC grpc 协议
FormTypeGRPC = "grpc"
FormTypeRadius = "radius"
/// FormTypeGRPC grpc 协议
//FormTypeGRPC = "grpc"
//FormTypeRadius = "radius"
)
// 校验函数
@ -44,14 +43,6 @@ var (
verifyMapWebSocketMutex sync.RWMutex
)
// RegisterVerifyHTTP 注册 http 校验函数
func RegisterVerifyHTTP(verify string, verifyFunc VerifyHTTP) {
verifyMapHTTPMutex.Lock()
defer verifyMapHTTPMutex.Unlock()
key := fmt.Sprintf("%s.%s", FormTypeHTTP, verify)
verifyMapHTTP[key] = verifyFunc
}
// RegisterVerifyWebSocket 注册 webSocket 校验函数
func RegisterVerifyWebSocket(verify string, verifyFunc VerifyWebSocket) {
verifyMapWebSocketMutex.Lock()
@ -74,23 +65,17 @@ type VerifyWebSocket func(request *Request, seq string, msg []byte) (code int, i
// Request 请求数据
type Request struct {
URL string // URL
Form string // http/webSocket/tcp
Method string // 方法 GET/POST/PUT
Headers map[string]string // Headers
Body string // body
Verify string // 验证的方法
Timeout time.Duration // 请求超时时间
Debug bool // 是否开启Debug模式
MaxCon int // 每个连接的请求数
HTTP2 bool // 是否使用http2.0
Keepalive bool // 是否开启长连接
Code int // 验证的状态码
}
// GetBody 获取请求数据
func (r *Request) GetBody() (body io.Reader) {
return strings.NewReader(r.Body)
URL string // URL
Form string // http/webSocket/tcp
// Method string // 方法 GET/POST/PUT
// Headers map[string]string // Headers
//Body string // body
Verify string // 验证的方法
Timeout time.Duration // 请求超时时间
Debug bool // 是否开启Debug模式
MaxCon int // 每个连接的请求数
Keepalive bool // 是否开启长连接
Code int // 验证的状态码
}
// getVerifyKey 获取校验 key
@ -122,51 +107,12 @@ func (r *Request) GetVerifyWebSocket() VerifyWebSocket {
// timeout 请求超时时间
// debug 是否开启debug
// path curl文件路径 http接口压测自定义参数设置
func NewRequest(url string, verify string, code int, timeout time.Duration, debug bool, path string,
reqHeaders []string,
reqBody string, maxCon int, http2 bool, keepalive bool) (request *Request, err error) {
var (
method = "GET"
headers = make(map[string]string)
body string
)
if path != "" {
var curl *CURL
curl, err = ParseTheFile(path)
if err != nil {
return nil, err
}
if url == "" {
url = curl.GetURL()
}
method = curl.GetMethod()
headers = curl.GetHeaders()
body = curl.GetBody()
} else {
if reqBody != "" {
method = "POST"
body = reqBody
}
for _, v := range reqHeaders {
getHeaderValue(v, headers)
}
if _, ok := headers["Content-Type"]; !ok {
headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8"
}
}
func NewRequest(url string, verify string, code int, timeout time.Duration, debug bool,
maxCon int, keepalive bool) (request *Request, err error) {
form := ""
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
form = FormTypeHTTP
} else if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") {
if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") {
form = FormTypeWebSocket
} else if strings.HasPrefix(url, "grpc://") || strings.HasPrefix(url, "rpc://") {
form = FormTypeGRPC
} else if strings.HasPrefix(url, "radius://") {
form = FormTypeRadius
url = url[9:]
} else {
form = FormTypeHTTP
url = fmt.Sprintf("http://%s", url)
}
if form == "" {
err = fmt.Errorf("url:%s 不合法,必须是完整http、webSocket连接", url)
@ -174,17 +120,6 @@ func NewRequest(url string, verify string, code int, timeout time.Duration, debu
}
var ok bool
switch form {
case FormTypeHTTP:
// verify
if verify == "" {
verify = "statusCode"
}
key := fmt.Sprintf("%s.%s", form, verify)
_, ok = verifyMapHTTP[key]
if !ok {
err = errors.New("验证器不存在:" + key)
return
}
case FormTypeWebSocket:
// verify
if verify == "" {
@ -203,14 +138,10 @@ func NewRequest(url string, verify string, code int, timeout time.Duration, debu
request = &Request{
URL: url,
Form: form,
Method: strings.ToUpper(method),
Headers: headers,
Body: body,
Verify: verify,
Timeout: timeout,
Debug: debug,
MaxCon: maxCon,
HTTP2: http2,
Keepalive: keepalive,
Code: code,
}
@ -239,11 +170,10 @@ func (r *Request) Print() {
if r == nil {
return
}
result := fmt.Sprintf("request:\n form:%s \n url:%s \n method:%s \n headers:%v \n", r.Form, r.URL, r.Method,
r.Headers)
result = fmt.Sprintf("%s data:%v \n", result, r.Body)
result := fmt.Sprintf("request:\n form:%s \n url:%s \n", r.Form, r.URL)
//result = fmt.Sprintf("%s data:%v \n", result, r.Body)
result = fmt.Sprintf("%s verify:%s \n timeout:%s \n debug:%v \n", result, r.Verify, r.Timeout, r.Debug)
result = fmt.Sprintf("%s http2.0%v \n keepalive%v \n maxCon:%v ", result, r.HTTP2, r.Keepalive, r.MaxCon)
//result = fmt.Sprintf("%s http2.0%v \n keepalive%v \n maxCon:%v ", result, r.HTTP2, r.Keepalive, r.MaxCon)
fmt.Println(result)
return
}

View File

@ -1,99 +0,0 @@
// Package client http 客户端
package client
import (
"crypto/tls"
"log"
"net/http"
"os"
"time"
"go_dreamfactory/stress/model"
httplongclinet "go_dreamfactory/stress/server/client/http_longclinet"
"golang.org/x/net/http2"
"go_dreamfactory/stress/helper"
)
// logErr err
var logErr = log.New(os.Stderr, "", 0)
// HTTPRequest HTTP 请求
// method 方法 GET POST
// url 请求的url
// body 请求的body
// headers 请求头信息
// timeout 请求超时时间
func HTTPRequest(chanID uint64, request *model.Request) (resp *http.Response, requestTime uint64, err error) {
method := request.Method
url := request.URL
body := request.GetBody()
timeout := request.Timeout
headers := request.Headers
req, err := http.NewRequest(method, url, body)
if err != nil {
return
}
// 在req中设置Host解决在header中设置Host不生效问题
if _, ok := headers["Host"]; ok {
req.Host = headers["Host"]
}
// 设置默认为utf-8编码
if _, ok := headers["Content-Type"]; !ok {
if headers == nil {
headers = make(map[string]string)
}
headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8"
}
for key, value := range headers {
req.Header.Set(key, value)
}
var client *http.Client
if request.Keepalive {
client = httplongclinet.NewClient(chanID, request)
startTime := time.Now()
resp, err = client.Do(req)
requestTime = uint64(helper.DiffNano(startTime))
if err != nil {
logErr.Println("请求失败:", err)
return
}
return
} else {
req.Close = true
tr := &http.Transport{}
if request.HTTP2 {
// 使用真实证书 验证证书 模拟真实请求
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
}
if err = http2.ConfigureTransport(tr); err != nil {
return
}
} else {
// 跳过证书验证
tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
client = &http.Client{
Transport: tr,
Timeout: timeout,
}
}
startTime := time.Now()
resp, err = client.Do(req)
requestTime = uint64(helper.DiffNano(startTime))
if err != nil {
logErr.Println("请求失败:", err)
return
}
return
}

View File

@ -8,8 +8,6 @@ import (
"time"
"go_dreamfactory/stress/model"
"golang.org/x/net/http2"
)
var (
@ -43,32 +41,18 @@ func setClient(i uint64, request *model.Request) *http.Client {
// createLangHttpClient 初始化长连接客户端参数
func createLangHttpClient(request *model.Request) *http.Client {
tr := &http.Transport{}
if request.HTTP2 {
// 使用真实证书 验证证书 模拟真实请求
tr = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 0, // 最大连接数,默认0无穷大
MaxIdleConnsPerHost: request.MaxCon, // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
IdleConnTimeout: 90 * time.Second, // 多长时间未使用自动关闭连接
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
}
_ = http2.ConfigureTransport(tr)
} else {
// 跳过证书验证
tr = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 0, // 最大连接数,默认0无穷大
MaxIdleConnsPerHost: request.MaxCon, // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
IdleConnTimeout: 90 * time.Second, // 多长时间未使用自动关闭连接
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
tr = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 0, // 最大连接数,默认0无穷大
MaxIdleConnsPerHost: request.MaxCon, // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
IdleConnTimeout: 90 * time.Second, // 多长时间未使用自动关闭连接
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return &http.Client{
Transport: tr,
}

View File

@ -11,7 +11,6 @@ import (
"sync"
"time"
"go_dreamfactory/stress/server/client"
"go_dreamfactory/stress/server/golink"
"go_dreamfactory/stress/server/statistics"
"go_dreamfactory/stress/server/verify"
@ -25,11 +24,11 @@ const (
func init() {
// http
model.RegisterVerifyHTTP("statusCode", verify.HTTPStatusCode)
model.RegisterVerifyHTTP("json", verify.HTTPJson)
//model.RegisterVerifyHTTP("statusCode", verify.HTTPStatusCode)
//model.RegisterVerifyHTTP("json", verify.HTTPJson)
// webSocket
model.RegisterVerifyWebSocket("json", verify.WebSocketJSON)
//model.RegisterVerifyWebSocket("json", verify.WebSocketJSON)
model.RegisterVerifyWebSocket("pb", verify.WebSocketProto)
}
@ -47,14 +46,12 @@ func Dispose(ctx context.Context, concurrency, totalNumber uint64, request *mode
for i := uint64(0); i < concurrency; i++ {
wg.Add(1)
switch request.Form {
case model.FormTypeHTTP:
go golink.HTTP(ctx, i, ch, totalNumber, &wg, request)
case model.FormTypeWebSocket:
switch connectionMode {
case 1:
// 连接以后再启动协程
r := robot.NewRobot(request.URL)
r.SetAccount("21112" + strconv.Itoa(int(i)))
r.SetAccount("31112" + strconv.Itoa(int(i)))
//head := &pb.UserMessage{MainType: "user", SubType: "login"}
// 先登录
@ -66,13 +63,6 @@ func Dispose(ctx context.Context, concurrency, totalNumber uint64, request *mode
// 并发建立长链接
go func(i uint64) {
// 连接以后再启动协程
// ws := client.NewWebSocket(request.URL)
// err := ws.GetConn()
// if err != nil {
// fmt.Println("连接失败:", i, err)
// return
// }
// golink.WebSocket(ctx, i, ch, totalNumber, &wg, request, ws)
}(i)
// 注意:时间间隔太短会出现连接失败的报错 默认连接时长:20毫秒(公网连接)
time.Sleep(5 * time.Millisecond)
@ -80,19 +70,6 @@ func Dispose(ctx context.Context, concurrency, totalNumber uint64, request *mode
data := fmt.Sprintf("不支持的类型:%d", connectionMode)
panic(data)
}
case model.FormTypeGRPC:
// 连接以后再启动协程
ws := client.NewGrpcSocket(request.URL)
err := ws.Link()
if err != nil {
fmt.Println("连接失败:", i, err)
continue
}
go golink.Grpc(ctx, i, ch, totalNumber, &wg, request, ws)
case model.FormTypeRadius:
// Radius use udp, does not a connection
go golink.Radius(ctx, i, ch, totalNumber, &wg, request)
default:
// 类型不支持
wg.Done()

View File

@ -46,7 +46,7 @@ func grpcRequest(chanID uint64, ch chan<- *model.RequestResults, i uint64, reque
var (
ctx = context.Background()
req = &pb.Request{
UserName: request.Body,
//UserName: request.Body,
}
)
rsp, err := c.HelloWorld(ctx, req)

View File

@ -1,105 +0,0 @@
// Package golink 连接
package golink
import (
"compress/gzip"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"sync"
"go_dreamfactory/stress/model"
"go_dreamfactory/stress/server/client"
)
// HTTP 请求
func HTTP(ctx context.Context, chanID uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup,
request *model.Request) {
defer func() {
wg.Done()
}()
// fmt.Printf("启动协程 编号:%05d \n", chanID)
for i := uint64(0); i < totalNumber; i++ {
if ctx.Err() != nil {
fmt.Printf("ctx.Err err: %v \n", ctx.Err())
break
}
list := getRequestList(request)
isSucceed, errCode, requestTime, contentLength := sendList(chanID, list)
requestResults := &model.RequestResults{
Time: requestTime,
IsSucceed: isSucceed,
ErrCode: errCode,
ReceivedBytes: contentLength,
}
requestResults.SetID(chanID, i)
ch <- requestResults
}
return
}
// sendList 多个接口分步压测
func sendList(chanID uint64, requestList []*model.Request) (isSucceed bool, errCode int, requestTime uint64,
contentLength int64) {
errCode = model.HTTPOk
for _, request := range requestList {
succeed, code, u, length := send(chanID, request)
isSucceed = succeed
errCode = code
requestTime = requestTime + u
contentLength = contentLength + length
if succeed == false {
break
}
}
return
}
// send 发送一次请求
func send(chanID uint64, request *model.Request) (bool, int, uint64, int64) {
var (
// startTime = time.Now()
isSucceed = false
errCode = model.HTTPOk
contentLength = int64(0)
err error
resp *http.Response
requestTime uint64
)
newRequest := getRequest(request)
resp, requestTime, err = client.HTTPRequest(chanID, newRequest)
if err != nil {
errCode = model.RequestErr // 请求错误
} else {
// 此处原方式获取的数据长度可能是 -1换成如下方式获取可获取到正确的长度
contentLength, err = getBodyLength(resp)
if err != nil {
contentLength = resp.ContentLength
}
// 验证请求是否成功
errCode, isSucceed = newRequest.GetVerifyHTTP()(newRequest, resp)
}
return isSucceed, errCode, requestTime, contentLength
}
// getBodyLength 获取响应数据长度
func getBodyLength(response *http.Response) (length int64, err error) {
var reader io.ReadCloser
switch response.Header.Get("Content-Encoding") {
case "gzip":
reader, err = gzip.NewReader(response.Body)
defer func() {
_ = reader.Close()
}()
default:
reader = response.Body
}
body, err := ioutil.ReadAll(reader)
return int64(len(body)), err
}

View File

@ -1,71 +0,0 @@
// Package golink 连接
package golink
import (
"time"
"go_dreamfactory/stress/model"
)
// ReqListMany 接口分步压测
type ReqListMany struct {
list []*model.Request
}
// getCount 获取连接
func (r *ReqListMany) getCount() int {
return len(r.list)
}
var (
clientList *ReqListMany
)
// init 接口分步压测示例
func init() {
clientList = &ReqListMany{}
// TODO::接口分步压测示例
// 需要压测的接口参数
clients := make([]*model.Request, 0)
// 压测第一步
clients = append(clients, &model.Request{
URL: "https://page.aliyun.com/delivery/plan/list", // 请求url
Form: "http", // 请求方式 示例参数:http/webSocket/tcp
Method: "POST", // 请求方法 示例参数:GET/POST/PUT
Headers: map[string]string{
"referer": "https://cn.aliyun.com/",
"cookie": "aliyun_choice=CN; JSESSIONID=J8866281-CKCFJ4BUZ7GDO9V89YBW1-KJ3J5V9K-GYUW7; maliyun_temporary_console0=1AbLByOMHeZe3G41KYd5WWZvrM%2BGErkaLcWfBbgveKA9ifboArprPASvFUUfhwHtt44qsDwVqMk8Wkdr1F5LccYk2mPCZJiXb0q%2Bllj5u3SQGQurtyPqnG489y%2FkoA%2FEvOwsXJTvXTFQPK%2BGJD4FJg%3D%3D; cna=L3Q5F8cHDGgCAXL3r8fEZtdU; isg=BFNThsmSCcgX-sUcc5Jo2s2T4tF9COfKYi8g9wVwr3KphHMmjdh3GrHFvPTqJD_C; l=eBaceXLnQGBjstRJBOfwPurza77OSIRAguPzaNbMiT5POw1B5WAlWZbqyNY6C3GVh6lwR37EODnaBeYBc3K-nxvOu9eFfGMmn",
}, // headers 头信息
Body: "adPlanQueryParam=%7B%22adZone%22%3A%7B%22positionList%22%3A%5B%7B%22positionId%22%3A83%7D%5D%7D%2C%22requestId%22%3A%2217958651-f205-44c7-ad5d-f8af92a6217a%22%7D", // 消息体
Verify: "statusCode", // 验证的方法 示例参数:statusCode、json
Timeout: 30 * time.Second, // 是否开启Debug模式
Debug: false, // 是否开启Debug模式
})
// 压测第二步
clients = append(clients, &model.Request{
URL: "https://page.aliyun.com/delivery/plan/list", // 请求url
Form: "http", // 请求方式 示例参数:http/webSocket/tcp
Method: "POST", // 请求方法 示例参数:GET/POST/PUT
Headers: map[string]string{
"referer": "https://cn.aliyun.com/",
"cookie": "aliyun_choice=CN; JSESSIONID=J8866281-CKCFJ4BUZ7GDO9V89YBW1-KJ3J5V9K-GYUW7; maliyun_temporary_console0=1AbLByOMHeZe3G41KYd5WWZvrM%2BGErkaLcWfBbgveKA9ifboArprPASvFUUfhwHtt44qsDwVqMk8Wkdr1F5LccYk2mPCZJiXb0q%2Bllj5u3SQGQurtyPqnG489y%2FkoA%2FEvOwsXJTvXTFQPK%2BGJD4FJg%3D%3D; cna=L3Q5F8cHDGgCAXL3r8fEZtdU; isg=BFNThsmSCcgX-sUcc5Jo2s2T4tF9COfKYi8g9wVwr3KphHMmjdh3GrHFvPTqJD_C; l=eBaceXLnQGBjstRJBOfwPurza77OSIRAguPzaNbMiT5POw1B5WAlWZbqyNY6C3GVh6lwR37EODnaBeYBc3K-nxvOu9eFfGMmn",
}, // headers 头信息
Body: "adPlanQueryParam=%7B%22adZone%22%3A%7B%22positionList%22%3A%5B%7B%22positionId%22%3A83%7D%5D%7D%2C%22requestId%22%3A%2217958651-f205-44c7-ad5d-f8af92a6217a%22%7D", // 消息体
Verify: "statusCode", // 验证的方法 示例参数:statusCode、json
Timeout: 30 * time.Second, // 是否开启Debug模式
Debug: false, // 是否开启Debug模式
})
clientList.list = clients
// TODO::分步压测时,注释下面一行代码
clientList.list = nil
}
// getRequestList 获取请求列表
func getRequestList(request *model.Request) []*model.Request {
if len(clientList.list) <= 0 {
return []*model.Request{request}
}
return clientList.list
}

View File

@ -1,97 +0,0 @@
// Package golink 连接
package golink
import (
"math/rand"
"time"
"go_dreamfactory/stress/model"
)
// ReqListWeigh 接口加权压测
type ReqListWeigh struct {
list []Req
weighCount uint32 // 总权重
}
// Req req
type Req struct {
req *model.Request // 请求信息
weights uint32 // 权重,数字越大访问频率越高
}
// setWeighCount 设置权重
func (r *ReqListWeigh) setWeighCount() {
r.weighCount = 0
for _, value := range r.list {
r.weighCount = r.weighCount + value.weights
}
}
var (
clientWeigh *ReqListWeigh
r *rand.Rand
)
// 多接口压测示例
func init() {
// TODO::压测多个接口示例
// 需要压测的接口参数
clients := make([]Req, 0)
clients = append(clients, Req{req: &model.Request{
URL: "https://page.aliyun.com/delivery/plan/list", // 请求url
Form: "http", // 请求方式 示例参数:http/webSocket/tcp
Method: "POST", // 请求方法 示例参数:GET/POST/PUT
Headers: map[string]string{
"referer": "https://cn.aliyun.com/",
"cookie": "aliyun_choice=CN; JSESSIONID=J8866281-CKCFJ4BUZ7GDO9V89YBW1-KJ3J5V9K-GYUW7; maliyun_temporary_console0=1AbLByOMHeZe3G41KYd5WWZvrM%2BGErkaLcWfBbgveKA9ifboArprPASvFUUfhwHtt44qsDwVqMk8Wkdr1F5LccYk2mPCZJiXb0q%2Bllj5u3SQGQurtyPqnG489y%2FkoA%2FEvOwsXJTvXTFQPK%2BGJD4FJg%3D%3D; cna=L3Q5F8cHDGgCAXL3r8fEZtdU; isg=BFNThsmSCcgX-sUcc5Jo2s2T4tF9COfKYi8g9wVwr3KphHMmjdh3GrHFvPTqJD_C; l=eBaceXLnQGBjstRJBOfwPurza77OSIRAguPzaNbMiT5POw1B5WAlWZbqyNY6C3GVh6lwR37EODnaBeYBc3K-nxvOu9eFfGMmn",
}, // headers 头信息
Body: "adPlanQueryParam=%7B%22adZone%22%3A%7B%22positionList%22%3A%5B%7B%22positionId%22%3A83%7D%5D%7D%2C%22requestId%22%3A%2217958651-f205-44c7-ad5d-f8af92a6217a%22%7D", // 消息体
Verify: "statusCode", // 验证的方法 示例参数:statusCode、json
Timeout: 30 * time.Second, // 是否开启Debug模式
Debug: false, // 是否开启Debug模式
}, weights: 2})
clients = append(clients, Req{req: &model.Request{
URL: "https://page.aliyun.com/delivery/plan/list", // 请求url
Form: "http", // 请求方式 示例参数:http/webSocket/tcp
Method: "POST", // 请求方法 示例参数:GET/POST/PUT
Headers: map[string]string{
"referer": "https://cn.aliyun.com/",
"cookie": "aliyun_choice=CN; JSESSIONID=J8866281-CKCFJ4BUZ7GDO9V89YBW1-KJ3J5V9K-GYUW7; maliyun_temporary_console0=1AbLByOMHeZe3G41KYd5WWZvrM%2BGErkaLcWfBbgveKA9ifboArprPASvFUUfhwHtt44qsDwVqMk8Wkdr1F5LccYk2mPCZJiXb0q%2Bllj5u3SQGQurtyPqnG489y%2FkoA%2FEvOwsXJTvXTFQPK%2BGJD4FJg%3D%3D; cna=L3Q5F8cHDGgCAXL3r8fEZtdU; isg=BFNThsmSCcgX-sUcc5Jo2s2T4tF9COfKYi8g9wVwr3KphHMmjdh3GrHFvPTqJD_C; l=eBaceXLnQGBjstRJBOfwPurza77OSIRAguPzaNbMiT5POw1B5WAlWZbqyNY6C3GVh6lwR37EODnaBeYBc3K-nxvOu9eFfGMmn",
}, // headers 头信息
Body: "adPlanQueryParam=%7B%22adZone%22%3A%7B%22positionList%22%3A%5B%7B%22positionId%22%3A83%7D%5D%7D%2C%22requestId%22%3A%2217958651-f205-44c7-ad5d-f8af92a6217a%22%7D", // 消息体
Verify: "statusCode", // 验证的方法 示例参数:statusCode、json
Timeout: 30 * time.Second, // 是否开启Debug模式
Debug: false, // 是否开启Debug模式
}, weights: 1})
r = rand.New(rand.NewSource(time.Now().Unix()))
clientWeigh = &ReqListWeigh{
list: clients,
}
// TODO::注释下面一行代码
clientWeigh.list = nil
clientWeigh.setWeighCount()
}
// getRequest 获取请求
func getRequest(request *model.Request) *model.Request {
if clientWeigh == nil || clientWeigh.weighCount <= 0 {
return request
}
n := uint32(r.Int31n(int32(clientWeigh.weighCount)))
var (
count uint32
)
for _, value := range clientWeigh.list {
if count >= n {
// value.req.Print()
return value.req
}
count = count + value.weights
}
panic("getRequest err")
}

View File

@ -1,94 +0,0 @@
// Package verify 校验
package verify
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"go_dreamfactory/stress/model"
)
// getZipData 处理gzip压缩
func getZipData(response *http.Response) (body []byte, err error) {
var reader io.ReadCloser
switch response.Header.Get("Content-Encoding") {
case "gzip":
reader, err = gzip.NewReader(response.Body)
defer func() {
_ = reader.Close()
}()
default:
reader = response.Body
}
body, err = ioutil.ReadAll(reader)
response.Body = ioutil.NopCloser(bytes.NewReader(body))
return
}
// HTTPStatusCode 通过 HTTP 状态码判断是否请求成功
func HTTPStatusCode(request *model.Request, response *http.Response) (code int, isSucceed bool) {
defer func() {
_ = response.Body.Close()
}()
code = response.StatusCode
if code == request.Code {
isSucceed = true
}
// 开启调试模式
if request.GetDebug() {
body, err := getZipData(response)
fmt.Printf("请求结果 httpCode:%d body:%s err:%v \n", response.StatusCode, string(body), err)
}
io.Copy(ioutil.Discard, response.Body)
return
}
/*************************** 返回值为json ********************************/
// ResponseJSON 返回数据结构体
type ResponseJSON struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
// HTTPJson 通过返回的Body 判断
// 返回示例: {"code":200,"msg":"Success","data":{}}
// code 默认将http code作为返回码http code 为200时 取body中的返回code
func HTTPJson(request *model.Request, response *http.Response) (code int, isSucceed bool) {
defer func() {
_ = response.Body.Close()
}()
code = response.StatusCode
if code == http.StatusOK {
body, err := getZipData(response)
if err != nil {
code = model.ParseError
fmt.Printf("请求结果 ioutil.ReadAll err:%v", err)
} else {
responseJSON := &ResponseJSON{}
err = json.Unmarshal(body, responseJSON)
if err != nil {
code = model.ParseError
fmt.Printf("请求结果 json.Unmarshal err:%v", err)
} else {
code = responseJSON.Code
// body 中code返回200为返回数据成功
if responseJSON.Code == request.Code {
isSucceed = true
}
}
}
// 开启调试模式
if request.GetDebug() {
fmt.Printf("请求结果 httpCode:%d body:%s err:%v \n", response.StatusCode, string(body), err)
}
}
io.Copy(ioutil.Discard, response.Body)
return
}

View File

@ -35,12 +35,11 @@ var (
debugStr = "false" // 是否是debug
requestURL = "" // 压测的url 目前支持http/https ws/wss
path = "" // curl文件路径 http接口压测自定义参数设置
verify = "" // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json
verify = "pb" // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json
headers array // 自定义头信息传递给服务器
body = "" // HTTP POST方式传送数据
maxCon = 1 // 单个连接最大请求数
code = 200 // 成功状态码
http2 = false // 是否开http2.0
keepalive = false // 是否开启长连接
cpuNumber = 1 // CUP 核数,默认为一核,一般场景下单核已经够用了
timeout int64 = 0 // 超时时间,默认不设置
@ -57,7 +56,6 @@ func init() {
flag.StringVar(&body, "data", body, "HTTP POST方式传送数据")
flag.IntVar(&maxCon, "m", maxCon, "单个host最大连接数")
flag.IntVar(&code, "code", code, "请求成功的状态码")
flag.BoolVar(&http2, "http2", http2, "是否开http2.0")
flag.BoolVar(&keepalive, "k", keepalive, "是否开启长连接")
flag.IntVar(&cpuNumber, "cpuNumber", cpuNumber, "CUP 核数,默认为一核")
flag.Int64Var(&timeout, "timeout", timeout, "超时时间 单位 秒,默认不设置")
@ -84,7 +82,6 @@ func main() {
totalNumber = 1
debugStr = "false"
requestURL = "ws://106.54.189.74:7891/gateway"
verify = "pb"
if concurrency == 0 || totalNumber == 0 || (requestURL == "" && path == "") {
fmt.Printf("示例: go run main.go -c 1 -n 1 -u https://www.baidu.com/ \n")
fmt.Printf("压测地址或curl路径必填 \n")
@ -93,7 +90,7 @@ func main() {
return
}
debug := strings.ToLower(debugStr) == "false"
request, err := model.NewRequest(requestURL, verify, code, 0, debug, path, headers, body, maxCon, http2, keepalive)
request, err := model.NewRequest(requestURL, verify, code, 0, debug, maxCon, keepalive)
if err != nil {
fmt.Printf("参数不合法 %v \n", err)
return