压测工具优化
This commit is contained in:
parent
ecbcd34e31
commit
7a08d8888a
@ -4,7 +4,6 @@ package model
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -23,13 +22,13 @@ const (
|
|||||||
|
|
||||||
// 支持协议
|
// 支持协议
|
||||||
const (
|
const (
|
||||||
// FormTypeHTTP http 协议
|
/// FormTypeHTTP http 协议
|
||||||
FormTypeHTTP = "http"
|
//FormTypeHTTP = "http"
|
||||||
// FormTypeWebSocket webSocket 协议
|
/// FormTypeWebSocket webSocket 协议
|
||||||
FormTypeWebSocket = "webSocket"
|
FormTypeWebSocket = "webSocket"
|
||||||
// FormTypeGRPC grpc 协议
|
/// FormTypeGRPC grpc 协议
|
||||||
FormTypeGRPC = "grpc"
|
//FormTypeGRPC = "grpc"
|
||||||
FormTypeRadius = "radius"
|
//FormTypeRadius = "radius"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 校验函数
|
// 校验函数
|
||||||
@ -44,14 +43,6 @@ var (
|
|||||||
verifyMapWebSocketMutex sync.RWMutex
|
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 校验函数
|
// RegisterVerifyWebSocket 注册 webSocket 校验函数
|
||||||
func RegisterVerifyWebSocket(verify string, verifyFunc VerifyWebSocket) {
|
func RegisterVerifyWebSocket(verify string, verifyFunc VerifyWebSocket) {
|
||||||
verifyMapWebSocketMutex.Lock()
|
verifyMapWebSocketMutex.Lock()
|
||||||
@ -74,23 +65,17 @@ type VerifyWebSocket func(request *Request, seq string, msg []byte) (code int, i
|
|||||||
|
|
||||||
// Request 请求数据
|
// Request 请求数据
|
||||||
type Request struct {
|
type Request struct {
|
||||||
URL string // URL
|
URL string // URL
|
||||||
Form string // http/webSocket/tcp
|
Form string // http/webSocket/tcp
|
||||||
Method string // 方法 GET/POST/PUT
|
// Method string // 方法 GET/POST/PUT
|
||||||
Headers map[string]string // Headers
|
// Headers map[string]string // Headers
|
||||||
Body string // body
|
//Body string // body
|
||||||
Verify string // 验证的方法
|
Verify string // 验证的方法
|
||||||
Timeout time.Duration // 请求超时时间
|
Timeout time.Duration // 请求超时时间
|
||||||
Debug bool // 是否开启Debug模式
|
Debug bool // 是否开启Debug模式
|
||||||
MaxCon int // 每个连接的请求数
|
MaxCon int // 每个连接的请求数
|
||||||
HTTP2 bool // 是否使用http2.0
|
Keepalive bool // 是否开启长连接
|
||||||
Keepalive bool // 是否开启长连接
|
Code int // 验证的状态码
|
||||||
Code int // 验证的状态码
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBody 获取请求数据
|
|
||||||
func (r *Request) GetBody() (body io.Reader) {
|
|
||||||
return strings.NewReader(r.Body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVerifyKey 获取校验 key
|
// getVerifyKey 获取校验 key
|
||||||
@ -122,51 +107,12 @@ func (r *Request) GetVerifyWebSocket() VerifyWebSocket {
|
|||||||
// timeout 请求超时时间
|
// timeout 请求超时时间
|
||||||
// debug 是否开启debug
|
// debug 是否开启debug
|
||||||
// path curl文件路径 http接口压测,自定义参数设置
|
// path curl文件路径 http接口压测,自定义参数设置
|
||||||
func NewRequest(url string, verify string, code int, timeout time.Duration, debug bool, path string,
|
func NewRequest(url string, verify string, code int, timeout time.Duration, debug bool,
|
||||||
reqHeaders []string,
|
maxCon int, keepalive bool) (request *Request, err error) {
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
form := ""
|
form := ""
|
||||||
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
|
if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") {
|
||||||
form = FormTypeHTTP
|
|
||||||
} else if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") {
|
|
||||||
form = FormTypeWebSocket
|
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 == "" {
|
if form == "" {
|
||||||
err = fmt.Errorf("url:%s 不合法,必须是完整http、webSocket连接", url)
|
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
|
var ok bool
|
||||||
switch form {
|
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:
|
case FormTypeWebSocket:
|
||||||
// verify
|
// verify
|
||||||
if verify == "" {
|
if verify == "" {
|
||||||
@ -203,14 +138,10 @@ func NewRequest(url string, verify string, code int, timeout time.Duration, debu
|
|||||||
request = &Request{
|
request = &Request{
|
||||||
URL: url,
|
URL: url,
|
||||||
Form: form,
|
Form: form,
|
||||||
Method: strings.ToUpper(method),
|
|
||||||
Headers: headers,
|
|
||||||
Body: body,
|
|
||||||
Verify: verify,
|
Verify: verify,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Debug: debug,
|
Debug: debug,
|
||||||
MaxCon: maxCon,
|
MaxCon: maxCon,
|
||||||
HTTP2: http2,
|
|
||||||
Keepalive: keepalive,
|
Keepalive: keepalive,
|
||||||
Code: code,
|
Code: code,
|
||||||
}
|
}
|
||||||
@ -239,11 +170,10 @@ func (r *Request) Print() {
|
|||||||
if r == nil {
|
if r == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result := fmt.Sprintf("request:\n form:%s \n url:%s \n method:%s \n headers:%v \n", r.Form, r.URL, r.Method,
|
result := fmt.Sprintf("request:\n form:%s \n url:%s \n", r.Form, r.URL)
|
||||||
r.Headers)
|
//result = fmt.Sprintf("%s data:%v \n", result, r.Body)
|
||||||
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 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)
|
fmt.Println(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -8,8 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go_dreamfactory/stress/model"
|
"go_dreamfactory/stress/model"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -43,32 +41,18 @@ func setClient(i uint64, request *model.Request) *http.Client {
|
|||||||
// createLangHttpClient 初始化长连接客户端参数
|
// createLangHttpClient 初始化长连接客户端参数
|
||||||
func createLangHttpClient(request *model.Request) *http.Client {
|
func createLangHttpClient(request *model.Request) *http.Client {
|
||||||
tr := &http.Transport{}
|
tr := &http.Transport{}
|
||||||
if request.HTTP2 {
|
|
||||||
// 使用真实证书 验证证书 模拟真实请求
|
tr = &http.Transport{
|
||||||
tr = &http.Transport{
|
DialContext: (&net.Dialer{
|
||||||
DialContext: (&net.Dialer{
|
Timeout: 30 * time.Second,
|
||||||
Timeout: 30 * time.Second,
|
KeepAlive: 30 * time.Second,
|
||||||
KeepAlive: 30 * time.Second,
|
}).DialContext,
|
||||||
}).DialContext,
|
MaxIdleConns: 0, // 最大连接数,默认0无穷大
|
||||||
MaxIdleConns: 0, // 最大连接数,默认0无穷大
|
MaxIdleConnsPerHost: request.MaxCon, // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
|
||||||
MaxIdleConnsPerHost: request.MaxCon, // 对每个host的最大连接数量(MaxIdleConnsPerHost<=MaxIdleConns)
|
IdleConnTimeout: 90 * time.Second, // 多长时间未使用自动关闭连接
|
||||||
IdleConnTimeout: 90 * time.Second, // 多长时间未使用自动关闭连接
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
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},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go_dreamfactory/stress/server/client"
|
|
||||||
"go_dreamfactory/stress/server/golink"
|
"go_dreamfactory/stress/server/golink"
|
||||||
"go_dreamfactory/stress/server/statistics"
|
"go_dreamfactory/stress/server/statistics"
|
||||||
"go_dreamfactory/stress/server/verify"
|
"go_dreamfactory/stress/server/verify"
|
||||||
@ -25,11 +24,11 @@ const (
|
|||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
// http
|
// http
|
||||||
model.RegisterVerifyHTTP("statusCode", verify.HTTPStatusCode)
|
//model.RegisterVerifyHTTP("statusCode", verify.HTTPStatusCode)
|
||||||
model.RegisterVerifyHTTP("json", verify.HTTPJson)
|
//model.RegisterVerifyHTTP("json", verify.HTTPJson)
|
||||||
|
|
||||||
// webSocket
|
// webSocket
|
||||||
model.RegisterVerifyWebSocket("json", verify.WebSocketJSON)
|
//model.RegisterVerifyWebSocket("json", verify.WebSocketJSON)
|
||||||
model.RegisterVerifyWebSocket("pb", verify.WebSocketProto)
|
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++ {
|
for i := uint64(0); i < concurrency; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
switch request.Form {
|
switch request.Form {
|
||||||
case model.FormTypeHTTP:
|
|
||||||
go golink.HTTP(ctx, i, ch, totalNumber, &wg, request)
|
|
||||||
case model.FormTypeWebSocket:
|
case model.FormTypeWebSocket:
|
||||||
switch connectionMode {
|
switch connectionMode {
|
||||||
case 1:
|
case 1:
|
||||||
// 连接以后再启动协程
|
// 连接以后再启动协程
|
||||||
r := robot.NewRobot(request.URL)
|
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"}
|
//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) {
|
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)
|
}(i)
|
||||||
// 注意:时间间隔太短会出现连接失败的报错 默认连接时长:20毫秒(公网连接)
|
// 注意:时间间隔太短会出现连接失败的报错 默认连接时长:20毫秒(公网连接)
|
||||||
time.Sleep(5 * time.Millisecond)
|
time.Sleep(5 * time.Millisecond)
|
||||||
@ -80,19 +70,6 @@ func Dispose(ctx context.Context, concurrency, totalNumber uint64, request *mode
|
|||||||
data := fmt.Sprintf("不支持的类型:%d", connectionMode)
|
data := fmt.Sprintf("不支持的类型:%d", connectionMode)
|
||||||
panic(data)
|
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:
|
default:
|
||||||
// 类型不支持
|
// 类型不支持
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
@ -46,7 +46,7 @@ func grpcRequest(chanID uint64, ch chan<- *model.RequestResults, i uint64, reque
|
|||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
req = &pb.Request{
|
req = &pb.Request{
|
||||||
UserName: request.Body,
|
//UserName: request.Body,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
rsp, err := c.HelloWorld(ctx, req)
|
rsp, err := c.HelloWorld(ctx, req)
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -35,12 +35,11 @@ var (
|
|||||||
debugStr = "false" // 是否是debug
|
debugStr = "false" // 是否是debug
|
||||||
requestURL = "" // 压测的url 目前支持,http/https ws/wss
|
requestURL = "" // 压测的url 目前支持,http/https ws/wss
|
||||||
path = "" // curl文件路径 http接口压测,自定义参数设置
|
path = "" // curl文件路径 http接口压测,自定义参数设置
|
||||||
verify = "" // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json
|
verify = "pb" // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json
|
||||||
headers array // 自定义头信息传递给服务器
|
headers array // 自定义头信息传递给服务器
|
||||||
body = "" // HTTP POST方式传送数据
|
body = "" // HTTP POST方式传送数据
|
||||||
maxCon = 1 // 单个连接最大请求数
|
maxCon = 1 // 单个连接最大请求数
|
||||||
code = 200 // 成功状态码
|
code = 200 // 成功状态码
|
||||||
http2 = false // 是否开http2.0
|
|
||||||
keepalive = false // 是否开启长连接
|
keepalive = false // 是否开启长连接
|
||||||
cpuNumber = 1 // CUP 核数,默认为一核,一般场景下单核已经够用了
|
cpuNumber = 1 // CUP 核数,默认为一核,一般场景下单核已经够用了
|
||||||
timeout int64 = 0 // 超时时间,默认不设置
|
timeout int64 = 0 // 超时时间,默认不设置
|
||||||
@ -57,7 +56,6 @@ func init() {
|
|||||||
flag.StringVar(&body, "data", body, "HTTP POST方式传送数据")
|
flag.StringVar(&body, "data", body, "HTTP POST方式传送数据")
|
||||||
flag.IntVar(&maxCon, "m", maxCon, "单个host最大连接数")
|
flag.IntVar(&maxCon, "m", maxCon, "单个host最大连接数")
|
||||||
flag.IntVar(&code, "code", code, "请求成功的状态码")
|
flag.IntVar(&code, "code", code, "请求成功的状态码")
|
||||||
flag.BoolVar(&http2, "http2", http2, "是否开http2.0")
|
|
||||||
flag.BoolVar(&keepalive, "k", keepalive, "是否开启长连接")
|
flag.BoolVar(&keepalive, "k", keepalive, "是否开启长连接")
|
||||||
flag.IntVar(&cpuNumber, "cpuNumber", cpuNumber, "CUP 核数,默认为一核")
|
flag.IntVar(&cpuNumber, "cpuNumber", cpuNumber, "CUP 核数,默认为一核")
|
||||||
flag.Int64Var(&timeout, "timeout", timeout, "超时时间 单位 秒,默认不设置")
|
flag.Int64Var(&timeout, "timeout", timeout, "超时时间 单位 秒,默认不设置")
|
||||||
@ -84,7 +82,6 @@ func main() {
|
|||||||
totalNumber = 1
|
totalNumber = 1
|
||||||
debugStr = "false"
|
debugStr = "false"
|
||||||
requestURL = "ws://106.54.189.74:7891/gateway"
|
requestURL = "ws://106.54.189.74:7891/gateway"
|
||||||
verify = "pb"
|
|
||||||
if concurrency == 0 || totalNumber == 0 || (requestURL == "" && path == "") {
|
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("示例: go run main.go -c 1 -n 1 -u https://www.baidu.com/ \n")
|
||||||
fmt.Printf("压测地址或curl路径必填 \n")
|
fmt.Printf("压测地址或curl路径必填 \n")
|
||||||
@ -93,7 +90,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
debug := strings.ToLower(debugStr) == "false"
|
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 {
|
if err != nil {
|
||||||
fmt.Printf("参数不合法 %v \n", err)
|
fmt.Printf("参数不合法 %v \n", err)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user