压测工具优化
This commit is contained in:
parent
ecbcd34e31
commit
7a08d8888a
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
"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,
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user