diff --git a/README.md b/README.md index fc302b3..f79b3bf 100644 --- a/README.md +++ b/README.md @@ -569,8 +569,7 @@ go version: go1.12.9 linux/amd64 - go server -```golang -package main +```golangpackage main import ( "log" diff --git a/helper/helper.go b/helper/helper.go index 489e0e7..ea7c306 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:40 - */ - +// Package helper 帮助函数,时间、数组的通用处理 package helper import ( @@ -27,4 +21,3 @@ func InArrayStr(str string, arr []string) (inArray bool) { } return } - diff --git a/main.go b/main.go index b7e4861..6341ce3 100644 --- a/main.go +++ b/main.go @@ -1,27 +1,25 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 13:44 - */ - +// Package main go 实现的压测工具 package main import ( "flag" "fmt" - "go-stress-testing/model" - "go-stress-testing/server" "runtime" "strings" + + "go-stress-testing/model" + "go-stress-testing/server" ) +// array 自定义数组参数 type array []string +// String string func (a *array) String() string { return fmt.Sprint(*a) } +// Set set func (a *array) Set(s string) error { *a = append(*a, s) @@ -32,53 +30,47 @@ var ( concurrency uint64 = 1 // 并发数 totalNumber uint64 = 1 // 请求数(单个并发/协程) debugStr = "false" // 是否是debug - requestUrl string // 压测的url 目前支持,http/https ws/wss - path string // curl文件路径 http接口压测,自定义参数设置 - verify string // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json + requestURL = "" // 压测的url 目前支持,http/https ws/wss + path = "" // curl文件路径 http接口压测,自定义参数设置 + verify = "" // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json headers array // 自定义头信息传递给服务器 - body string // HTTP POST方式传送数据 + body = "" // HTTP POST方式传送数据 ) func init() { flag.Uint64Var(&concurrency, "c", concurrency, "并发数") flag.Uint64Var(&totalNumber, "n", totalNumber, "请求数(单个并发/协程)") flag.StringVar(&debugStr, "d", debugStr, "调试模式") - flag.StringVar(&requestUrl, "u", "", "压测地址") - flag.StringVar(&path, "p", "", "curl文件路径") - flag.StringVar(&verify, "v", "", "验证方法 http 支持:statusCode、json webSocket支持:json") + flag.StringVar(&requestURL, "u", requestURL, "压测地址") + flag.StringVar(&path, "p", path, "curl文件路径") + flag.StringVar(&verify, "v", verify, "验证方法 http 支持:statusCode、json webSocket支持:json") flag.Var(&headers, "H", "自定义头信息传递给服务器 示例:-H 'Content-Type: application/json'") - flag.StringVar(&body, "data", "", "HTTP POST方式传送数据") + flag.StringVar(&body, "data", body, "HTTP POST方式传送数据") // 解析参数 flag.Parse() } -// go 实现的压测工具 +// main go 实现的压测工具 // 编译可执行文件 //go:generate go build main.go func main() { - runtime.GOMAXPROCS(1) - 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("压测地址或curl路径必填 \n") - fmt.Printf("当前请求参数: -c %d -n %d -d %v -u %s \n", concurrency, totalNumber, debugStr, requestUrl) + fmt.Printf("当前请求参数: -c %d -n %d -d %v -u %s \n", concurrency, totalNumber, debugStr, requestURL) flag.Usage() - return } debug := strings.ToLower(debugStr) == "true" - request, err := model.NewRequest(requestUrl, verify, 0, debug, path, headers, body) + request, err := model.NewRequest(requestURL, verify, 0, debug, path, headers, body) if err != nil { fmt.Printf("参数不合法 %v \n", err) - return } - fmt.Printf("\n 开始启动 并发数:%d 请求数:%d 请求参数: \n", concurrency, totalNumber) request.Print() - // 开始处理 server.Dispose(concurrency, totalNumber, request) - return } diff --git a/model/curl_model.go b/model/curl_model.go index db4a5cd..9e51d84 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-19 -* Time: 09:51 - */ - +// Package model 数据模型 package model import ( @@ -17,73 +11,60 @@ import ( "go-stress-testing/helper" ) -// curl参数解析 +// CURL curl参数解析 type CURL struct { Data map[string][]string } +// getDataValue 获取数据 func (c *CURL) getDataValue(keys []string) []string { var ( value = make([]string, 0) ) - for _, key := range keys { var ( ok bool ) - value, ok = c.Data[key] if ok { break } } - return value } -// 从文件中解析curl +// ParseTheFile 从文件中解析curl func ParseTheFile(path string) (curl *CURL, err error) { - if path == "" { err = errors.New("路径不能为空") - return } - curl = &CURL{ Data: make(map[string][]string), } - file, err := os.Open(path) if err != nil { err = errors.New("打开文件失败:" + err.Error()) - return } - defer func() { - file.Close() + _ = file.Close() }() - dataBytes, err := ioutil.ReadAll(file) if err != nil { err = errors.New("读取文件失败:" + err.Error()) - return } data := string(dataBytes) - for len(data) > 0 { if strings.HasPrefix(data, "curl") { data = data[5:] } - data = strings.TrimSpace(data) var ( key string value string ) - index := strings.Index(data, " ") if index <= 0 { break @@ -91,36 +72,29 @@ func ParseTheFile(path string) (curl *CURL, err error) { key = strings.TrimSpace(data[:index]) data = data[index+1:] data = strings.TrimSpace(data) - // url if !strings.HasPrefix(key, "-") { key = strings.Trim(key, "'") curl.Data["curl"] = []string{key} - // 去除首尾空格 data = strings.TrimFunc(data, func(r rune) bool { if r == ' ' || r == '\\' || r == '\n' { return true } - return false }) continue } - if strings.HasPrefix(data, "-") { continue } - var ( endSymbol = " " ) - if strings.HasPrefix(data, "'") { endSymbol = "'" data = data[1:] } - index = strings.Index(data, endSymbol) if index <= -1 { index = len(data) @@ -132,56 +106,39 @@ func ParseTheFile(path string) (curl *CURL, err error) { } else { data = "" } - // 去除首尾空格 data = strings.TrimFunc(data, func(r rune) bool { if r == ' ' || r == '\\' || r == '\n' { return true } - return false }) - if key == "" { continue } - curl.Data[key] = append(curl.Data[key], value) - - // break - } - - // debug - // for key, value := range curl.Data { - // fmt.Println("key:", key, "value:", value) - // } - return } +// String string func (c *CURL) String() (url string) { curlByte, _ := json.Marshal(c) - return string(curlByte) } -// GetUrl -func (c *CURL) GetUrl() (url string) { - +// GetURL 获取url +func (c *CURL) GetURL() (url string) { keys := []string{"curl", "--url"} value := c.getDataValue(keys) if len(value) <= 0 { - return } - url = value[0] - return } -// GetMethod +// GetMethod 获取 请求方式 func (c *CURL) GetMethod() (method string) { keys := []string{"-X", "--request"} value := c.getDataValue(keys) @@ -205,58 +162,43 @@ func (c *CURL) defaultMethod() (method string) { return } -// GetHeaders +// GetHeaders 获取请求头 func (c *CURL) GetHeaders() (headers map[string]string) { headers = make(map[string]string, 0) - keys := []string{"-H", "--header"} value := c.getDataValue(keys) - for _, v := range value { getHeaderValue(v, headers) } - return } -// GetHeaders +// GetHeadersStr 获取请求头string func (c *CURL) GetHeadersStr() string { headers := c.GetHeaders() bytes, _ := json.Marshal(&headers) - return string(bytes) } -// 获取body +// GetBody 获取body func (c *CURL) GetBody() (body string) { - keys := []string{"--data", "-d", "--data-urlencode", "--data-raw", "--data-binary"} value := c.getDataValue(keys) - if len(value) <= 0 { body = c.getPostForm() - return } - - // body = strings.NewReader(value[0]) body = value[0] - return } +// getPostForm get post form func (c *CURL) getPostForm() (body string) { keys := []string{"--form", "-F", "--form-string"} value := c.getDataValue(keys) - if len(value) <= 0 { - return } - body = strings.Join(value, "&") - return } - - diff --git a/model/curl_model_test.go b/model/curl_model_test.go index ef6a7aa..68c0b21 100644 --- a/model/curl_model_test.go +++ b/model/curl_model_test.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-19 -* Time: 10:16 - */ - +// Package model 数据模型 package model import ( @@ -12,6 +6,7 @@ import ( "testing" ) +// TestCurl 测试函数 func TestCurl(t *testing.T) { // ../curl.txt c, err := ParseTheFile("../curl/post.curl.txt") @@ -20,13 +15,10 @@ func TestCurl(t *testing.T) { if err != nil { return } - fmt.Printf("curl:%s \n", c.String()) - fmt.Printf("url:%s \n", c.GetUrl()) + fmt.Printf("url:%s \n", c.GetURL()) fmt.Printf("method:%s \n", c.GetMethod()) fmt.Printf("body:%v \n", c.GetBody()) fmt.Printf("body string:%v \n", c.GetBody()) - fmt.Printf("headers:%s \n", c.GetHeadersStr()) - } diff --git a/model/request_model.go b/model/request_model.go index 02aa87a..cf780fe 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 18:19 - */ - +// Package model 请求数据模型package model package model import ( @@ -17,57 +11,69 @@ import ( "time" ) +// 返回 code 码 const ( - HttpOk = 200 // 请求成功 - RequestTimeout = 506 // 请求超时 - RequestErr = 509 // 请求错误 - ParseError = 510 // 解析错误 + // HTTPOk 请求成功 + HTTPOk = 200 + // RequestErr 请求错误 + RequestErr = 509 + // ParseError 解析错误 + ParseError = 510 // 解析错误 +) - FormTypeHttp = "http" +// 支持协议 +const ( + // FormTypeHTTP http 协议 + FormTypeHTTP = "http" + // FormTypeWebSocket webSocket 协议 FormTypeWebSocket = "webSocket" - FormTypeGRPC = "grpc" + // FormTypeGRPC grpc 协议 + FormTypeGRPC = "grpc" ) +// 校验函数 var ( - // 校验函数 - verifyMapHttp = make(map[string]VerifyHttp) - verifyMapHttpMutex sync.RWMutex - - verifyMapWebSocket = make(map[string]VerifyWebSocket) + // verifyMapHTTP http 校验函数 + verifyMapHTTP = make(map[string]VerifyHTTP) + // verifyMapHTTPMutex http 并发锁 + verifyMapHTTPMutex sync.RWMutex + // verifyMapWebSocket webSocket 校验函数 + verifyMapWebSocket = make(map[string]VerifyWebSocket) + // verifyMapWebSocketMutex webSocket 并发锁 verifyMapWebSocketMutex sync.RWMutex ) -// 注册http校验函数 -func RegisterVerifyHttp(verify string, verifyFunc VerifyHttp) { - verifyMapHttpMutex.Lock() - defer verifyMapHttpMutex.Unlock() - - key := fmt.Sprintf("%s.%s", FormTypeHttp, verify) - verifyMapHttp[key] = verifyFunc +// RegisterVerifyHTTP 注册 http 校验函数 +func RegisterVerifyHTTP(verify string, verifyFunc VerifyHTTP) { + verifyMapHTTPMutex.Lock() + defer verifyMapHTTPMutex.Unlock() + key := fmt.Sprintf("%s.%s", FormTypeHTTP, verify) + verifyMapHTTP[key] = verifyFunc } -// 注册webSocket校验函数 +// RegisterVerifyWebSocket 注册 webSocket 校验函数 func RegisterVerifyWebSocket(verify string, verifyFunc VerifyWebSocket) { verifyMapWebSocketMutex.Lock() defer verifyMapWebSocketMutex.Unlock() - key := fmt.Sprintf("%s.%s", FormTypeWebSocket, verify) verifyMapWebSocket[key] = verifyFunc } -// 验证器 +// Verify 验证器 type Verify interface { GetCode() int // 有一个方法,返回code为200为成功 GetResult() bool // 返回是否成功 } -// 验证方法 -type VerifyHttp func(request *Request, response *http.Response) (code int, isSucceed bool) +// VerifyHTTP http 验证 +type VerifyHTTP func(request *Request, response *http.Response) (code int, isSucceed bool) + +// VerifyWebSocket webSocket 验证 type VerifyWebSocket func(request *Request, seq string, msg []byte) (code int, isSucceed bool) -// 请求结果 +// Request 请求数据 type Request struct { - Url string // Url + URL string // URL Form string // http/webSocket/tcp Method string // 方法 GET/POST/PUT Headers map[string]string // Headers @@ -75,118 +81,97 @@ type Request struct { Verify string // 验证的方法 Timeout time.Duration // 请求超时时间 Debug bool // 是否开启Debug模式 - - // 连接以后初始化事件 - // 循环事件 切片 时间 动作 } +// GetBody 获取请求数据 func (r *Request) GetBody() (body io.Reader) { - body = strings.NewReader(r.Body) - - return + return strings.NewReader(r.Body) } +// getVerifyKey 获取校验 key func (r *Request) getVerifyKey() (key string) { - key = fmt.Sprintf("%s.%s", r.Form, r.Verify) - - return + return fmt.Sprintf("%s.%s", r.Form, r.Verify) } -// 获取数据校验方法 -func (r *Request) GetVerifyHttp() VerifyHttp { - verify, ok := verifyMapHttp[r.getVerifyKey()] +// GetVerifyHTTP 获取数据校验方法 +func (r *Request) GetVerifyHTTP() VerifyHTTP { + verify, ok := verifyMapHTTP[r.getVerifyKey()] if !ok { - panic("GetVerifyHttp 验证方法不存在:" + r.Verify) + panic("GetVerifyHTTP 验证方法不存在:" + r.Verify) } - return verify } +// GetVerifyWebSocket 获取数据校验方法 func (r *Request) GetVerifyWebSocket() VerifyWebSocket { verify, ok := verifyMapWebSocket[r.getVerifyKey()] if !ok { panic("GetVerifyWebSocket 验证方法不存在:" + r.Verify) } - return verify } -// NewRequest +// NewRequest 生成请求结构体 // url 压测的url // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json // timeout 请求超时时间 // debug 是否开启debug // path curl文件路径 http接口压测,自定义参数设置 -func NewRequest(url string, verify string, timeout time.Duration, debug bool, path string, reqHeaders []string, reqBody string) (request *Request, err error) { - +func NewRequest(url string, verify string, timeout time.Duration, debug bool, path string, reqHeaders []string, + reqBody string) (request *Request, err error) { var ( method = "GET" headers = make(map[string]string) body string ) - if path != "" { - curl, err := ParseTheFile(path) + var curl *CURL + curl, err = ParseTheFile(path) if err != nil { - return nil, err } - if url == "" { - url = curl.GetUrl() + url = curl.GetURL() } - method = curl.GetMethod() headers = curl.GetHeaders() body = curl.GetBody() } else { - if reqBody != "" { method = "POST" body = reqBody - headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8" } - for _, v := range reqHeaders { getHeaderValue(v, headers) } } - form := "" if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") { - form = FormTypeHttp + form = FormTypeHTTP } else if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") { form = FormTypeWebSocket } else if strings.HasPrefix(url, "grpc://") || strings.HasPrefix(url, "rpc://") { form = FormTypeGRPC } else { - form = FormTypeHttp + form = FormTypeHTTP url = fmt.Sprintf("http://%s", url) } - if form == "" { - err = errors.New(fmt.Sprintf("url:%s 不合法,必须是完整http、webSocket连接", url)) - + err = fmt.Errorf("url:%s 不合法,必须是完整http、webSocket连接", url) return } - - var ( - ok bool - ) - + var ok bool switch form { - case FormTypeHttp: + case FormTypeHTTP: // verify if verify == "" { verify = "statusCode" } - key := fmt.Sprintf("%s.%s", form, verify) - _, ok = verifyMapHttp[key] + _, ok = verifyMapHTTP[key] if !ok { err = errors.New("验证器不存在:" + key) - return } case FormTypeWebSocket: @@ -194,23 +179,18 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa if verify == "" { verify = "json" } - key := fmt.Sprintf("%s.%s", form, verify) _, ok = verifyMapWebSocket[key] if !ok { err = errors.New("验证器不存在:" + key) - return } - } - if timeout == 0 { timeout = 30 * time.Second } - request = &Request{ - Url: url, + URL: url, Form: form, Method: strings.ToUpper(method), Headers: headers, @@ -219,21 +199,18 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa Timeout: timeout, Debug: debug, } - return - } +// getHeaderValue 获取 header func getHeaderValue(v string, headers map[string]string) { index := strings.Index(v, ":") if index < 0 { return } - vIndex := index + 1 if len(v) >= vIndex { value := strings.TrimPrefix(v[vIndex:], " ") - if _, ok := headers[v[:index]]; ok { headers[v[:index]] = fmt.Sprintf("%s; %s", headers[v[:index]], value) } else { @@ -242,55 +219,50 @@ func getHeaderValue(v string, headers map[string]string) { } } -// 打印 +// Print 格式化打印 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("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("%s verify:%s \n timeout:%s \n debug:%v \n", result, r.Verify, r.Timeout, r.Debug) fmt.Println(result) - return } +// GetDebug 获取 debug 参数 func (r *Request) GetDebug() bool { - return r.Debug } +// IsParameterLegal 参数是否合法 func (r *Request) IsParameterLegal() (err error) { - r.Form = "http" // statusCode json r.Verify = "json" - key := fmt.Sprintf("%s.%s", r.Form, r.Verify) - _, ok := verifyMapHttp[key] + _, ok := verifyMapHTTP[key] if !ok { - return errors.New("验证器不存在:" + key) } - return } -// 请求结果 +// RequestResults 请求结果 type RequestResults struct { - Id string // 消息Id - ChanId uint64 // 消息Id + ID string // 消息ID + ChanID uint64 // 消息ID Time uint64 // 请求时间 纳秒 IsSucceed bool // 是否请求成功 ErrCode int // 错误码 ReceivedBytes int64 } -func (r *RequestResults) SetId(chanId uint64, number uint64) { - id := fmt.Sprintf("%d_%d", chanId, number) - - r.Id = id - r.ChanId = chanId +// SetID 设置请求唯一ID +func (r *RequestResults) SetID(chanID uint64, number uint64) { + id := fmt.Sprintf("%d_%d", chanID, number) + r.ID = id + r.ChanID = chanID } diff --git a/proto/pb.pb.go b/proto/pb.pb.go index 9d25eae..bc54285 100644 --- a/proto/pb.pb.go +++ b/proto/pb.pb.go @@ -1,6 +1,5 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: pb.proto - package protobuf import ( diff --git a/proto/pb.proto b/proto/pb.proto index 75198d6..0789792 100644 --- a/proto/pb.proto +++ b/proto/pb.proto @@ -1,5 +1,4 @@ syntax = "proto3"; - package protobuf; // ApiServer api 接口 diff --git a/server/client/clienter.go b/server/client/clienter.go new file mode 100644 index 0000000..533fd00 --- /dev/null +++ b/server/client/clienter.go @@ -0,0 +1,9 @@ +// Package client clientpackage client +package client + +// Clienter 接口 注册、连接、发送 等 +type Clienter interface { + GetConn() (err error) + Close() (err error) + Send() +} diff --git a/server/client/grpc_client.go b/server/client/grpc_client.go index 475d337..0bf7e0d 100644 --- a/server/client/grpc_client.go +++ b/server/client/grpc_client.go @@ -1,29 +1,24 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 21:03 - */ - +// Package client grpc 客户端 package client import ( "context" "fmt" - "google.golang.org/grpc" "strings" "time" + + "google.golang.org/grpc" ) +// GrpcSocket grpc type GrpcSocket struct { conn *grpc.ClientConn address string } +// NewGrpcSocket new func NewGrpcSocket(address string) (s *GrpcSocket) { - var ( - newAddr string - ) + var newAddr string arr := strings.Split(address, "//") if len(arr) >= 2 { newAddr = arr[1] @@ -34,11 +29,12 @@ func NewGrpcSocket(address string) (s *GrpcSocket) { return } +// getAddress 获取地址 func (g *GrpcSocket) getAddress() (address string) { return g.address } -// 关闭 +// Close 关闭 func (g *GrpcSocket) Close() (err error) { if g == nil { return @@ -46,21 +42,22 @@ func (g *GrpcSocket) Close() (err error) { if g.conn == nil { return } - g.conn.Close() - return + return g.conn.Close() } // Link 建立连接 func (g *GrpcSocket) Link() (err error) { - ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() conn, err := grpc.DialContext(ctx, g.address, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { - return fmt.Errorf("GetConn: 连接失败 address:%s %w", g.address, err) + return fmt.Errorf("getConn: 连接失败 address:%s %w", g.address, err) } g.conn = conn return } +// GetConn 获取连接 func (g *GrpcSocket) GetConn() (conn *grpc.ClientConn) { return g.conn } diff --git a/server/client/http_client.go b/server/client/http_client.go index b0fe7bc..9f0edf5 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 21:03 - */ - +// Package client http 客户端 package client import ( @@ -18,29 +12,27 @@ import ( "go-stress-testing/helper" ) +// logErr err var logErr = log.New(os.Stderr, "", 0) -// HTTP 请求 +// HTTPRequest HTTP 请求 // method 方法 GET POST // url 请求的url // body 请求的body // headers 请求头信息 // timeout 请求超时时间 -func HttpRequest(method, url string, body io.Reader, headers map[string]string, timeout time.Duration) (resp *http.Response, requestTime uint64, err error) { - +func HTTPRequest(method, url string, body io.Reader, headers map[string]string, + timeout time.Duration) (resp *http.Response, requestTime uint64, err error) { // 跳过证书验证 tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } - client := &http.Client{ Transport: tr, Timeout: timeout, } - req, err := http.NewRequest(method, url, body) if err != nil { - return } req.Close = true @@ -55,11 +47,9 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, } headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8" } - for key, value := range headers { req.Header.Set(key, value) } - startTime := time.Now() resp, err = client.Do(req) requestTime = uint64(helper.DiffNano(startTime)) @@ -68,10 +58,5 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, return } - - // bytes, err := json.Marshal(req) - // fmt.Printf("%#v \n", req) - return } - diff --git a/server/client/websocket_client.go b/server/client/websocket_client.go index fc74b42..7e6454b 100644 --- a/server/client/websocket_client.go +++ b/server/client/websocket_client.go @@ -1,147 +1,118 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 21:03 - */ - +// Package client webSocket 客户端 package client import ( "errors" "fmt" - "golang.org/x/net/websocket" "net/url" "strings" + + "golang.org/x/net/websocket" ) const ( connRetry = 3 // 建立连接重试次数 ) +// WebSocket webSocket type WebSocket struct { conn *websocket.Conn - UrlLink string - Url *url.URL + URLLink string + URL *url.URL IsSsl bool } +// NewWebSocket new func NewWebSocket(urlLink string) (ws *WebSocket) { - var ( - isSsl bool - ) - + var isSsl bool if strings.HasPrefix(urlLink, "wss://") { isSsl = true } - u, err := url.Parse(urlLink) // 解析失败 if err != nil { panic(err) } - ws = &WebSocket{ - UrlLink: urlLink, - Url: u, + URLLink: urlLink, + URL: u, IsSsl: isSsl, } return } +// getLink 获取连接 func (w *WebSocket) getLink() (link string) { - link = w.UrlLink - - return + return w.URLLink } +// getOrigin 获取源连接 func (w *WebSocket) getOrigin() (origin string) { origin = "http://" if w.IsSsl { origin = "https://" } - - origin = fmt.Sprintf("%s%s/", origin, w.Url.Host) - + origin = fmt.Sprintf("%s%s/", origin, w.URL.Host) return } -// 关闭 +// Close 关闭 func (w *WebSocket) Close() (err error) { if w == nil { - return } - if w.conn == nil { return } - - w.conn.Close() - - return + return w.conn.Close() } +// GetConn 获取连接 func (w *WebSocket) GetConn() (err error) { - var ( conn *websocket.Conn i int ) - for i = 0; i < connRetry; i++ { conn, err = websocket.Dial(w.getLink(), "", w.getOrigin()) if err != nil { fmt.Println("GetConn 建立连接失败 in...", i, err) - continue } w.conn = conn - return } - if err != nil { fmt.Println("GetConn 建立连接失败", i, err) } - return } -// 发送数据 +// Write 发送数据 func (w *WebSocket) Write(body []byte) (err error) { if w.conn == nil { err = errors.New("未建立连接") - return } - _, err = w.conn.Write(body) if err != nil { fmt.Println("发送数据失败:", err) - return } - return } -// 接收数据 +// Read 接收数据 func (w *WebSocket) Read() (msg []byte, err error) { if w.conn == nil { err = errors.New("未建立连接") - return } - msg = make([]byte, 512) - n, err := w.conn.Read(msg) if err != nil { fmt.Println("接收数据失败:", err) - return nil, err } - return msg[:n], nil } diff --git a/server/dispose.go b/server/dispose.go index 3d078a3..408720a 100644 --- a/server/dispose.go +++ b/server/dispose.go @@ -1,98 +1,80 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:42 - */ - +// Package server 压测启动 package server import ( "fmt" + "sync" + "time" + "go-stress-testing/model" "go-stress-testing/server/client" "go-stress-testing/server/golink" "go-stress-testing/server/statistics" "go-stress-testing/server/verify" - "sync" - "time" ) const ( connectionMode = 1 // 1:顺序建立长链接 2:并发建立长链接 ) -// 注册验证器 +// init 注册验证器 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) } -// 处理函数 +// Dispose 处理函数 func Dispose(concurrency, totalNumber uint64, request *model.Request) { - // 设置接收数据缓存 ch := make(chan *model.RequestResults, 1000) var ( wg sync.WaitGroup // 发送数据完成 wgReceiving sync.WaitGroup // 数据处理完成 ) - wgReceiving.Add(1) go statistics.ReceivingResults(concurrency, ch, &wgReceiving) - for i := uint64(0); i < concurrency; i++ { wg.Add(1) switch request.Form { - case model.FormTypeHttp: - - go golink.Http(i, ch, totalNumber, &wg, request) - + case model.FormTypeHTTP: + go golink.HTTP(i, ch, totalNumber, &wg, request) case model.FormTypeWebSocket: - switch connectionMode { case 1: // 连接以后再启动协程 - ws := client.NewWebSocket(request.Url) + ws := client.NewWebSocket(request.URL) err := ws.GetConn() if err != nil { fmt.Println("连接失败:", i, err) - continue } - go golink.WebSocket(i, ch, totalNumber, &wg, request, ws) case 2: // 并发建立长链接 go func(i uint64) { // 连接以后再启动协程 - ws := client.NewWebSocket(request.Url) + ws := client.NewWebSocket(request.URL) err := ws.GetConn() if err != nil { fmt.Println("连接失败:", i, err) - return } - golink.WebSocket(i, ch, totalNumber, &wg, request, ws) }(i) - // 注意:时间间隔太短会出现连接失败的报错 默认连接时长:20毫秒(公网连接) time.Sleep(5 * time.Millisecond) default: - data := fmt.Sprintf("不支持的类型:%d", connectionMode) panic(data) } - case model.FormTypeGRPC: // 连接以后再启动协程 - ws := client.NewGrpcSocket(request.Url) + ws := client.NewGrpcSocket(request.URL) err := ws.Link() if err != nil { fmt.Println("连接失败:", i, err) @@ -104,16 +86,12 @@ func Dispose(concurrency, totalNumber uint64, request *model.Request) { wg.Done() } } - // 等待所有的数据都发送完成 wg.Wait() - // 延时1毫秒 确保数据都处理完成了 time.Sleep(1 * time.Millisecond) close(ch) - // 数据全部处理完成了 wgReceiving.Wait() - return } diff --git a/server/golink/grpc_link.go b/server/golink/grpc_link.go index cd43c02..ac07d5e 100644 --- a/server/golink/grpc_link.go +++ b/server/golink/grpc_link.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:43 - */ - +// Package golink 连接 package golink import ( @@ -20,30 +14,28 @@ import ( ) // Grpc grpc 接口请求 -func Grpc(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, +func Grpc(chanID uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request, ws *client.GrpcSocket) { - defer func() { wg.Done() }() defer func() { - ws.Close() + _ = ws.Close() }() for i := uint64(0); i < totalNumber; i++ { - grpcRequest(chanId, ch, i, request, ws) + grpcRequest(chanID, ch, i, request, ws) } return } -// 请求 -func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, +// grpcRequest 请求 +func grpcRequest(chanID uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, ws *client.GrpcSocket) { var ( startTime = time.Now() isSucceed = false - errCode = model.HttpOk + errCode = model.HTTPOk ) - // 需要发送的数据 conn := ws.GetConn() if conn == nil { @@ -76,8 +68,6 @@ func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, reque IsSucceed: isSucceed, ErrCode: errCode, } - requestResults.SetId(chanId, i) - + requestResults.SetID(chanID, i) ch <- requestResults } - diff --git a/server/golink/http_link.go b/server/golink/http_link.go index b4a4407..b2014f6 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:43 - */ - +// Package golink 连接 package golink import ( @@ -14,39 +8,32 @@ import ( "go-stress-testing/server/client" ) -// http go link -func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request) { - +// HTTP 请求 +func HTTP(chanID uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, + request *model.Request) { defer func() { wg.Done() }() - - // fmt.Printf("启动协程 编号:%05d \n", chanId) + // fmt.Printf("启动协程 编号:%05d \n", chanID) for i := uint64(0); i < totalNumber; i++ { - list := getRequestList(request) - isSucceed, errCode, requestTime, contentLength := sendList(list) - requestResults := &model.RequestResults{ Time: requestTime, IsSucceed: isSucceed, ErrCode: errCode, ReceivedBytes: contentLength, } - - requestResults.SetId(chanId, i) - + requestResults.SetID(chanID, i) ch <- requestResults } return } -// 多个接口分步压测 +// sendList 多个接口分步压测 func sendList(requestList []*model.Request) (isSucceed bool, errCode int, requestTime uint64, contentLength int64) { - - errCode = model.HttpOk + errCode = model.HTTPOk for _, request := range requestList { succeed, code, u, length := send(request) isSucceed = succeed @@ -54,11 +41,9 @@ func sendList(requestList []*model.Request) (isSucceed bool, errCode int, reques requestTime = requestTime + u contentLength = contentLength + length if succeed == false { - break } } - return } @@ -67,22 +52,18 @@ func send(request *model.Request) (bool, int, uint64, int64) { var ( // startTime = time.Now() isSucceed = false - errCode = model.HttpOk + errCode = model.HTTPOk contentLength = int64(0) ) - newRequest := getRequest(request) - // newRequest := request - - resp, requestTime, err := client.HttpRequest(newRequest.Method, newRequest.Url, newRequest.GetBody(), newRequest.Headers, newRequest.Timeout) - // requestTime := uint64(heper.DiffNano(startTime)) + resp, requestTime, err := client.HTTPRequest(newRequest.Method, newRequest.URL, newRequest.GetBody(), + newRequest.Headers, newRequest.Timeout) if err != nil { errCode = model.RequestErr // 请求错误 } else { contentLength = resp.ContentLength - // 验证请求是否成功 - errCode, isSucceed = newRequest.GetVerifyHttp()(newRequest, resp) + errCode, isSucceed = newRequest.GetVerifyHTTP()(newRequest, resp) } return isSucceed, errCode, requestTime, contentLength } diff --git a/server/golink/http_link_many.go b/server/golink/http_link_many.go index 427985d..efaced5 100644 --- a/server/golink/http_link_many.go +++ b/server/golink/http_link_many.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2020/7/31 -* Time: 8:36 下午 - */ - +// Package golink 连接 package golink import ( @@ -13,11 +7,12 @@ import ( "go-stress-testing/model" ) -// 接口分步压测 +// ReqListMany 接口分步压测 type ReqListMany struct { list []*model.Request } +// getCount 获取连接 func (r *ReqListMany) getCount() int { return len(r.list) } @@ -26,24 +21,22 @@ 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 + 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 头信息 + }, // 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模式 @@ -52,31 +45,27 @@ func init() { // 压测第二步 clients = append(clients, &model.Request{ - Url: "https://page.aliyun.com/delivery/plan/list", // 请求url + 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 头信息 + }, // 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::注释下面一行代码 + // TODO::分步压测时,注释下面一行代码 clientList.list = nil } +// getRequestList 获取请求列表 func getRequestList(request *model.Request) []*model.Request { - if len(clientList.list) <= 0 { - return []*model.Request{request} } - return clientList.list } diff --git a/server/golink/http_link_weigh.go b/server/golink/http_link_weigh.go index 8c59fcf..d8663e3 100644 --- a/server/golink/http_link_weigh.go +++ b/server/golink/http_link_weigh.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2020/7/31 -* Time: 8:36 下午 - */ - +// Package golink 连接 package golink import ( @@ -14,17 +8,19 @@ import ( "go-stress-testing/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 { @@ -39,18 +35,17 @@ var ( // 多接口压测示例 func init() { - // TODO::压测多个接口示例 // 需要压测的接口参数 clients := make([]Req, 0) clients = append(clients, Req{req: &model.Request{ - Url: "https://page.aliyun.com/delivery/plan/list", // 请求url + 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 头信息 + }, // 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模式 @@ -58,13 +53,13 @@ func init() { }, weights: 2}) clients = append(clients, Req{req: &model.Request{ - Url: "https://page.aliyun.com/delivery/plan/list", // 请求url + 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 头信息 + }, // 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模式 @@ -72,7 +67,6 @@ func init() { }, weights: 1}) r = rand.New(rand.NewSource(time.Now().Unix())) - clientWeigh = &ReqListWeigh{ list: clients, } @@ -83,19 +77,15 @@ func init() { 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() @@ -103,8 +93,5 @@ func getRequest(request *model.Request) *model.Request { } count = count + value.weights } - panic("getRequest err") - - return nil } diff --git a/server/golink/websocket_link.go b/server/golink/websocket_link.go index caffdad..07ada85 100644 --- a/server/golink/websocket_link.go +++ b/server/golink/websocket_link.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:43 - */ - +// Package golink 连接 package golink import ( @@ -31,33 +25,27 @@ func init() { keepAlive = true } -// web socket go link -func WebSocket(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request, ws *client.WebSocket) { - +// WebSocket webSocket go link +func WebSocket(chanID uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, + request *model.Request, ws *client.WebSocket) { defer func() { wg.Done() }() - - // fmt.Printf("启动协程 编号:%05d \n", chanId) - defer func() { - ws.Close() + _ = ws.Close() }() var ( i uint64 ) - // 暂停60秒 t := time.NewTimer(firstTime) for { select { case <-t.C: t.Reset(intervalTime) - // 请求 - webSocketRequest(chanId, ch, i, request, ws) - + webSocketRequest(chanID, ch, i, request, ws) // 结束条件 i = i + 1 if i >= totalNumber { @@ -65,7 +53,6 @@ func WebSocket(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint6 } } } - end: t.Stop() @@ -74,48 +61,38 @@ end: chWaitFor := make(chan int, 0) <-chWaitFor } - return } -// 请求 -func webSocketRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, ws *client.WebSocket) { - +// webSocketRequest 请求 +func webSocketRequest(chanID uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, + ws *client.WebSocket) { var ( startTime = time.Now() isSucceed = false - errCode = model.HttpOk + errCode = model.HTTPOk + msg []byte ) - // 需要发送的数据 - seq := fmt.Sprintf("%d_%d", chanId, i) + seq := fmt.Sprintf("%d_%d", chanID, i) err := ws.Write([]byte(`{"seq":"` + seq + `","cmd":"ping","data":{}}`)) if err != nil { errCode = model.RequestErr // 请求错误 } else { - - // time.Sleep(1 * time.Second) - msg, err := ws.Read() + msg, err = ws.Read() if err != nil { errCode = model.ParseError fmt.Println("读取数据 失败~") } else { - // fmt.Println(msg) errCode, isSucceed = request.GetVerifyWebSocket()(request, seq, msg) } } - requestTime := uint64(helper.DiffNano(startTime)) - requestResults := &model.RequestResults{ Time: requestTime, IsSucceed: isSucceed, ErrCode: errCode, } - - requestResults.SetId(chanId, i) - + requestResults.SetID(chanID, i) ch <- requestResults - } - diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 390cca0..65b9f0d 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-15 -* Time: 18:14 - */ - +// Package statistics 统计数据 package statistics import ( @@ -14,10 +8,10 @@ import ( "sync" "time" - "go-stress-testing/model" - "golang.org/x/text/language" "golang.org/x/text/message" + + "go-stress-testing/model" ) var ( @@ -26,19 +20,14 @@ var ( p = message.NewPrinter(language.English) ) -// 接收结果并处理 +// ReceivingResults 接收结果并处理 // 统计的时间都是纳秒,显示的时间 都是毫秒 // concurrent 并发数 func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sync.WaitGroup) { - defer func() { wg.Done() }() - - var ( - stopChan = make(chan bool) - ) - + var stopChan = make(chan bool) // 时间 var ( processingTime uint64 // 处理总时间 @@ -47,16 +36,13 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy minTime uint64 // 最小时长 successNum uint64 // 成功处理数,code为0 failureNum uint64 // 处理失败数,code不为0 - chanIdLen int // 并发数 - chanIds = make(map[uint64]bool) + chanIDLen int // 并发数 + chanIDs = make(map[uint64]bool) receivedBytes int64 ) - statTime := uint64(time.Now().UnixNano()) - // 错误码/错误个数 var errCode = make(map[int]int) - // 定时输出一次计算结果 ticker := time.NewTicker(exportStatisticsTime) go func() { @@ -65,80 +51,67 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy case <-ticker.C: endTime := uint64(time.Now().UnixNano()) requestTime = endTime - statTime - go calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIdLen, errCode, receivedBytes) + go calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, + chanIDLen, errCode, receivedBytes) case <-stopChan: // 处理完成 - return } } }() - header() - for data := range ch { - // fmt.Println("处理一条数据", data.Id, data.Time, data.IsSucceed, data.ErrCode) + // fmt.Println("处理一条数据", data.ID, data.Time, data.IsSucceed, data.ErrCode) processingTime = processingTime + data.Time - if maxTime <= data.Time { maxTime = data.Time } - if minTime == 0 { minTime = data.Time } else if minTime > data.Time { minTime = data.Time } - // 是否请求成功 if data.IsSucceed == true { successNum = successNum + 1 } else { failureNum = failureNum + 1 } - // 统计错误码 if value, ok := errCode[data.ErrCode]; ok { errCode[data.ErrCode] = value + 1 } else { errCode[data.ErrCode] = 1 } - receivedBytes += data.ReceivedBytes - - if _, ok := chanIds[data.ChanId]; !ok { - chanIds[data.ChanId] = true - chanIdLen = len(chanIds) + if _, ok := chanIDs[data.ChanID]; !ok { + chanIDs[data.ChanID] = true + chanIDLen = len(chanIDs) } } - // 数据全部接受完成,停止定时输出统计数据 stopChan <- true - endTime := uint64(time.Now().UnixNano()) requestTime = endTime - statTime - - calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIdLen, errCode, receivedBytes) - + calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIDLen, errCode, + receivedBytes) fmt.Printf("\n\n") - fmt.Println("************************* 结果 stat ****************************") fmt.Println("处理协程数量:", concurrent) // fmt.Println("处理协程数量:", concurrent, "程序处理总时长:", fmt.Sprintf("%.3f", float64(processingTime/concurrent)/1e9), "秒") - fmt.Println("请求总数(并发数*请求数 -c * -n):", successNum+failureNum, "总请求时间:", fmt.Sprintf("%.3f", float64(requestTime)/1e9), + fmt.Println("请求总数(并发数*请求数 -c * -n):", successNum+failureNum, "总请求时间:", + fmt.Sprintf("%.3f", float64(requestTime)/1e9), "秒", "successNum:", successNum, "failureNum:", failureNum) - fmt.Println("************************* 结果 end ****************************") - fmt.Printf("\n\n") } -// 计算数据 -func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64, chanIdLen int, errCode map[int]int, receivedBytes int64) { +// calculateData 计算数据 +func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64, + chanIDLen int, errCode map[int]int, receivedBytes int64) { if processingTime == 0 { processingTime = 1 } - var ( qps float64 averageTime float64 @@ -146,48 +119,39 @@ func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, su minTimeFloat float64 requestTimeFloat float64 ) - // 平均 每个协程成功数*总协程数据/总耗时 (每秒) if processingTime != 0 { qps = float64(successNum*1e9*concurrent) / float64(processingTime) } - // 平均时长 总耗时/总请求数/并发数 纳秒=>毫秒 if successNum != 0 && concurrent != 0 { averageTime = float64(processingTime) / float64(successNum*1e6) } - // 纳秒=>毫秒 maxTimeFloat = float64(maxTime) / 1e6 minTimeFloat = float64(minTime) / 1e6 requestTimeFloat = float64(requestTime) / 1e9 - // 打印的时长都为毫秒 - // result := fmt.Sprintf("请求总数:%8d|successNum:%8d|failureNum:%8d|qps:%9.3f|maxTime:%9.3f|minTime:%9.3f|平均时长:%9.3f|errCode:%v", successNum+failureNum, successNum, failureNum, qps, maxTimeFloat, minTimeFloat, averageTime, errCode) - // fmt.Println(result) - table(successNum, failureNum, errCode, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat, chanIdLen, receivedBytes) + table(successNum, failureNum, errCode, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat, chanIDLen, + receivedBytes) } -// 打印表头信息 +// header 打印表头信息 func header() { fmt.Printf("\n\n") // 打印的时长都为毫秒 总请数 fmt.Println("─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────") - result := fmt.Sprintf(" 耗时│ 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 错误码") - fmt.Println(result) - // result = fmt.Sprintf("耗时(s) │总请求数│成功数│失败数│QPS│最长耗时│最短耗时│平均耗时│错误码") - // fmt.Println(result) + fmt.Println(" 耗时│ 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 错误码") fmt.Println("─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────") - return } -// 打印表格 -func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIdLen int, receivedBytes int64) { +// table 打印表格 +func table(successNum, failureNum uint64, errCode map[int]int, + qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIDLen int, receivedBytes int64) { var ( speed int64 ) - if requestTimeFloat > 0 { speed = int64(float64(receivedBytes) / requestTimeFloat) } else { @@ -205,30 +169,24 @@ func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, receivedBytesStr = p.Sprintf("%d", receivedBytes) speedStr = p.Sprintf("%d", speed) } - // 打印的时长都为毫秒 result := fmt.Sprintf("%4.0fs│%7d│%7d│%7d│%8.2f│%8.2f│%8.2f│%8.2f│%8s│%8s│%v", - requestTimeFloat, chanIdLen, successNum, failureNum, qps, maxTimeFloat, minTimeFloat, averageTime, + requestTimeFloat, chanIDLen, successNum, failureNum, qps, maxTimeFloat, minTimeFloat, averageTime, receivedBytesStr, speedStr, printMap(errCode)) fmt.Println(result) - return } -// 输出错误码、次数 节约字符(终端一行字符大小有限) +// printMap 输出错误码、次数 节约字符(终端一行字符大小有限) func printMap(errCode map[int]int) (mapStr string) { - var ( mapArr []string ) for key, value := range errCode { mapArr = append(mapArr, fmt.Sprintf("%d:%d", key, value)) } - sort.Strings(mapArr) - mapStr = strings.Join(mapArr, ";") - return } diff --git a/server/statistics/statistics_test.go b/server/statistics/statistics_test.go index 6a60833..4949fff 100644 --- a/server/statistics/statistics_test.go +++ b/server/statistics/statistics_test.go @@ -5,7 +5,6 @@ * Date: 2020/9/28 * Time: 14:02 */ - package statistics import ( diff --git a/server/verify/http_verify.go b/server/verify/http_verify.go index d3ba561..4cd9080 100644 --- a/server/verify/http_verify.go +++ b/server/verify/http_verify.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-16 -* Time: 16:03 - */ - +// Package verify 校验 package verify import ( @@ -12,20 +6,21 @@ import ( "compress/gzip" "encoding/json" "fmt" - "go-stress-testing/model" "io" "io/ioutil" "net/http" + + "go-stress-testing/model" ) -// 处理gzip压缩 +// 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() + _ = reader.Close() }() default: reader = response.Body @@ -35,69 +30,63 @@ func getZipData(response *http.Response) (body []byte, err error) { return } -// 通过Http状态码判断是否请求成功 -func HttpStatusCode(request *model.Request, response *http.Response) (code int, isSucceed bool) { - - defer response.Body.Close() +// HTTPStatusCode 通过 HTTP 状态码判断是否请求成功 +func HTTPStatusCode(request *model.Request, response *http.Response) (code int, isSucceed bool) { + defer func() { + _ = response.Body.Close() + }() code = response.StatusCode if code == http.StatusOK { isSucceed = true } - // 开启调试模式 if request.GetDebug() { body, err := getZipData(response) fmt.Printf("请求结果 httpCode:%d body:%s err:%v \n", response.StatusCode, string(body), err) - } - return } /*************************** 返回值为json ********************************/ -// 返回数据结构体 -type ResponseJson struct { +// ResponseJSON 返回数据结构体 +type ResponseJSON struct { Code int `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data"` } -// 通过返回的Body 判断 +// 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 response.Body.Close() +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) + responseJSON := &ResponseJSON{} + err = json.Unmarshal(body, responseJSON) if err != nil { code = model.ParseError fmt.Printf("请求结果 json.Unmarshal err:%v", err) } else { - - code = responseJson.Code - + code = responseJSON.Code // body 中code返回200为返回数据成功 - if responseJson.Code == 200 { + if responseJSON.Code == 200 { isSucceed = true } } } - // 开启调试模式 if request.GetDebug() { fmt.Printf("请求结果 httpCode:%d body:%s err:%v \n", response.StatusCode, string(body), err) } } - return } diff --git a/server/verify/websokcet_verify.go b/server/verify/websokcet_verify.go index b9037ef..8cfeb99 100644 --- a/server/verify/websokcet_verify.go +++ b/server/verify/websokcet_verify.go @@ -1,22 +1,15 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-16 -* Time: 16:03 - */ - +// Package verify 校验 package verify import ( "encoding/json" "fmt" + "go-stress-testing/model" ) -/*************************** 返回值为json ********************************/ - -// 返回数据结构体 -type WebSocketResponseJson struct { +// WebSocketResponseJSON 返回数据结构体,返回值为json +type WebSocketResponseJSON struct { Seq string `json:"seq"` Cmd string `json:"cmd"` Response struct { @@ -26,34 +19,31 @@ type WebSocketResponseJson struct { } `json:"response"` } -// 通过返回的Body 判断 +// WebSocketJSON 通过返回的Body 判断 // 返回示例: {"seq":"1566276523281-585638","cmd":"heartbeat","response":{"code":200,"codeMsg":"Success","data":null}} // code 取body中的返回code -func WebSocketJson(request *model.Request, seq string, msg []byte) (code int, isSucceed bool) { - - responseJson := &WebSocketResponseJson{} - err := json.Unmarshal(msg, responseJson) +func WebSocketJSON(request *model.Request, seq string, msg []byte) (code int, isSucceed bool) { + responseJSON := &WebSocketResponseJSON{} + err := json.Unmarshal(msg, responseJSON) if err != nil { code = model.ParseError fmt.Printf("请求结果 json.Unmarshal msg:%s err:%v", string(msg), err) } else { - if seq != responseJson.Seq { + if seq != responseJSON.Seq { code = model.ParseError - fmt.Println("请求和返回seq不一致 ~请求:", seq, responseJson.Seq, string(msg)) + fmt.Println("请求和返回seq不一致 ~请求:", seq, responseJSON.Seq, string(msg)) } else { - code = responseJson.Response.Code + code = responseJSON.Response.Code // body 中code返回200为返回数据成功 if code == 200 { isSucceed = true } } } - // 开启调试模式 if request.GetDebug() { fmt.Printf("请求结果 seq:%s body:%s \n", seq, string(msg)) } - return } diff --git a/tests/grpc/main.go b/tests/grpc/main.go index c144fb9..3944124 100644 --- a/tests/grpc/main.go +++ b/tests/grpc/main.go @@ -1,19 +1,15 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2021/2/3 -* Time: 23:44 - */ - +// Package main grpc server package main import ( "context" "fmt" - pb "go-stress-testing/proto" - "google.golang.org/grpc" "log" "net" + + "google.golang.org/grpc" + + pb "go-stress-testing/proto" ) const ( diff --git a/tests/servers.go b/tests/servers.go index 61578c9..15b2f02 100644 --- a/tests/servers.go +++ b/tests/servers.go @@ -1,10 +1,4 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2020/8/1 -* Time: 09:27 - */ - +// Package main 测试用例package main package main import ( @@ -18,21 +12,16 @@ const ( ) func main() { - runtime.GOMAXPROCS(runtime.NumCPU() - 1) - hello := func(w http.ResponseWriter, req *http.Request) { data := "Hello, go-stress-testing! \n" - w.Header().Add("Server", "golang") - w.Write([]byte(data)) - + _, _ = w.Write([]byte(data)) return } http.HandleFunc("/", hello) err := http.ListenAndServe(":"+httpPort, nil) - if err != nil { log.Fatal("ListenAndServe: ", err) }