From bbd054511f3b0bc9aba0f3eca5707dac581ad1e6 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 29 Aug 2019 16:42:18 +0800 Subject: [PATCH 01/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f9b248..496d6a7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 压测工具如何选择? ab、locust、Jmeter、go压测工具【单台机器100w连接压测实战】 +# go实现的压测工具【单台机器100w连接压测实战】 本文介绍压测是什么,解释压测的专属名词,教大家如何压测。介绍市面上的常见压测工具(ab、locust、Jmeter、go实现的压测工具、云压测),对比这些压测工具,教大家如何选择一款适合自己的压测工具,本文还有两个压测实战项目: @@ -21,7 +21,7 @@ - [3、常见的压测工具](#3常见的压测工具) - [3.1 ab](#31-ab) - [3.2 locust](#32-locust) - - [3.3 Jmeter](#33-Jmeter) + - [3.3 JMeter](#33-JMeter) - [3.4 云压测](#34-云压测) - [3.4.1 云压测介绍](#341-云压测介绍) - [3.4.2 阿里云 性能测试 PTS](#342-阿里云-性能测试-PTS) @@ -387,7 +387,7 @@ Hatch rate (users spawned/second) 每秒钟增加用户数 ![locust 压测页面](https://img.mukewang.com/5d5e4fad000177e125501368.png) -### 3.3 Jmeter +### 3.3 JMeter - 简介 From 9e05c774d16b7ef21e659c07a8a4792072c60816 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Fri, 30 Aug 2019 14:09:18 +0800 Subject: [PATCH 02/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 496d6a7..f570cf4 100644 --- a/README.md +++ b/README.md @@ -701,7 +701,7 @@ centOS 7.6 上述设置不生效,需要手动修改配置文件 core 是限制内核文件的大小,这里设置为 unlimited ``` -# 添加一下参数 +# 添加以下参数 root soft nofile 1040000 root hard nofile 1040000 From 191394f019fd9c9b211474050256a07cceaa0187 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Fri, 30 Aug 2019 14:14:14 +0800 Subject: [PATCH 03/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f570cf4..6f10711 100644 --- a/README.md +++ b/README.md @@ -755,6 +755,8 @@ net.ipv4.tcp_rmem = 4096 4096 16777216 net.ipv4.tcp_wmem = 4096 4096 16777216 ``` +`sysctl -p` 修改配置以后使得配置生效命令 + 配置解释: - `ip_local_port_range` 表示TCP/UDP协议允许使用的本地端口号 范围:1024~65000 From 4221dbe1d43b0e4a48551e3a61adb07e8123a012 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 31 Aug 2019 14:20:25 +0800 Subject: [PATCH 04/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6f10711..10636e5 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 `-u` 需要压测的地址 -```shell script +```shell # clone 项目 git clone https://github.com/link1st/go-stress-testing.git @@ -196,10 +196,10 @@ go run main.go -c 1 -n 100 -u https://www.baidu.com/ - 机器: 峰值时间每秒钟请求数(QPS) / 单台机器的QPS = 需要的机器的数量 - 假设:网站每天的用户数(100W),每天的用户的访问量约为3000W PV,这台机器的需要多少QPS? -> `( 30000000*0.8 ) / (86400 * 0.2) ≈ 1389 (QPS)` +> ( 30000000\*0.8 ) / (86400 * 0.2) ≈ 1389 (QPS) - 假设:单台机器的的QPS是69,需要需要多少台机器来支撑? -> `1389 / 69 ≈ 20` +> 1389 / 69 ≈ 20 ## 3、常见的压测工具 ### 3.1 ab @@ -211,7 +211,7 @@ ab属于一个轻量级的压测工具,结果不会特别准确,可以用作 - 安装 -```shell script +```shell # 在linux环境安装 sudo yum -y install httpd ``` @@ -232,7 +232,7 @@ Options are: - 压测命令 -```shell script +```shell # 使用ab压测工具,对百度的链接 请求100次,并发数1 ab -n 100 -c 1 https://www.baidu.com/ ``` @@ -311,7 +311,7 @@ Percentage of the requests served within a certain time (ms) - 安装 -```shell script +```shell # pip3 安装locust pip3 install locust # 查看是否安装成功 @@ -345,7 +345,7 @@ class WebsiteUser(HttpLocust): - 启动压测 -```shell script +```shell locust -f test.py --host=https://www.baidu.com ``` From 1b60b3815c0fc76f889713363f36463c2a28fcbd Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sun, 1 Sep 2019 14:03:37 +0800 Subject: [PATCH 05/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 10636e5..969742b 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,6 @@ - [8、参考文献](#8参考文献) - - ## 1、项目说明 ### 1.1 go-stress-testing From bad241fd45d56bd5cefa90b7378100a1458d8c78 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Tue, 10 Sep 2019 19:29:31 +0800 Subject: [PATCH 06/75] add GitHub actions --- .github/workflows/release.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bf654d5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Release +on: + create: + tags: + - v* +jobs: + + release: + name: Release on GitHub + runs-on: ubuntu-latest + steps: + - name: Check out chode + uses: actions/checkout@v1 + + - name: Validates GO releaser config + uses: docker://goreleaser/goreleaser:latest + with: + args: check + + - name: Create release on GitHub + uses: docker://goreleaser/goreleaser:latest + with: + args: release + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file From f2cbb1a3ca93386f3b585d42bc5b19447ba3885f Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Tue, 10 Sep 2019 19:43:18 +0800 Subject: [PATCH 07/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf654d5..c3d18f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,9 @@ jobs: - name: Check out chode uses: actions/checkout@v1 + - name: Go PATH + run: export PATH=$PATH:$(go env GOPATH)/bin + - name: Validates GO releaser config uses: docker://goreleaser/goreleaser:latest with: From cd6bc19a6f71dec6ea4f47cd6816dbef20308911 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Tue, 10 Sep 2019 19:47:12 +0800 Subject: [PATCH 08/75] gitHub actions --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c3d18f1..ed99a06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,14 +12,14 @@ jobs: - name: Check out chode uses: actions/checkout@v1 - - name: Go PATH - run: export PATH=$PATH:$(go env GOPATH)/bin - - name: Validates GO releaser config uses: docker://goreleaser/goreleaser:latest with: args: check + - name: Go PATH + run: export PATH=$PATH:$(go env GOPATH)/bin + - name: Create release on GitHub uses: docker://goreleaser/goreleaser:latest with: From fd598df371cce1912237144286eaa831de77eda1 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:03:25 +0800 Subject: [PATCH 09/75] gitHub actions --- .github/workflows/release.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed99a06..cb73e23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,18 @@ jobs: with: args: check +#/home/runner/work/actions/actions + - name: go path + run: | + ls + cd .. + pwd + mkdir -vp go/{src/,bin/,pkg/} + mv go-stress-testing go/src/ + export GOPATH=${pwd}/go + export GO111MODULE=off + cd go/src/go-stress-testing + - name: Go PATH run: export PATH=$PATH:$(go env GOPATH)/bin From c8276dce84b74a208fde3028fc3f216821a0d40e Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:04:54 +0800 Subject: [PATCH 10/75] gitHub actions --- .github/workflows/push.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..f753ab5 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,36 @@ +name: Go actions +on: + push: + branches: + - master +jobs: + + build: + name: Build + runs-on: ubuntu-latest + steps: + + - name: Set up Go 1.12 + uses: actions/setup-go@v1 + with: + go-version: 1.12 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: Get dependencies + run: | + go get -v -t -d ./... + if [ -f Gopkg.toml ]; then + curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + dep ensure + fi + + - name: ls + run: | + ls + pwd + + - name: run + run: go run main.go -c 1 -n 100 -u https://www.baidu.com/ \ No newline at end of file From ac5562df1adb94923b7229c948bc65da930518ec Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:09:31 +0800 Subject: [PATCH 11/75] actions --- .github/workflows/push.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f753ab5..53af1fd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -19,6 +19,21 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v1 + #/home/runner/work/actions + - name: go path + run: | + ls + cd .. + pwd + mkdir -vp go/{src/,bin/,pkg/} + mv go-stress-testing go/src/ + export GOPATH=${pwd}/go + export GO111MODULE=off + cd go/src/go-stress-testing + + - name: Go PATH + run: export PATH=$PATH:$(go env GOPATH)/bin + - name: Get dependencies run: | go get -v -t -d ./... From bd026e7fd2389ff7760e280f0a65c09c00a21b80 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:16:15 +0800 Subject: [PATCH 12/75] actions --- .github/workflows/push.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 53af1fd..102541c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -27,9 +27,13 @@ jobs: pwd mkdir -vp go/{src/,bin/,pkg/} mv go-stress-testing go/src/ - export GOPATH=${pwd}/go + export GOPATH=${pwd}/go/ export GO111MODULE=off cd go/src/go-stress-testing + ls + go env + echo $(go env GOPATH) + - name: Go PATH run: export PATH=$PATH:$(go env GOPATH)/bin From ddba9f8833ba18746bdb09473d147293b4e415ae Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:20:06 +0800 Subject: [PATCH 13/75] actions --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 102541c..3e229b7 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -27,7 +27,7 @@ jobs: pwd mkdir -vp go/{src/,bin/,pkg/} mv go-stress-testing go/src/ - export GOPATH=${pwd}/go/ + export GOPATH=$(pwd)/go/ export GO111MODULE=off cd go/src/go-stress-testing ls From d500e97e35a0f10e76f6483b2172b127a75eee30 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:24:49 +0800 Subject: [PATCH 14/75] actions --- .github/workflows/push.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 3e229b7..011a30b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -25,11 +25,13 @@ jobs: ls cd .. pwd + ls mkdir -vp go/{src/,bin/,pkg/} mv go-stress-testing go/src/ export GOPATH=$(pwd)/go/ export GO111MODULE=off cd go/src/go-stress-testing + pwd ls go env echo $(go env GOPATH) From d9b561fb2b65bacd28020ec626d2b977e628d956 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:29:27 +0800 Subject: [PATCH 15/75] actions --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 011a30b..5c0f500 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -35,6 +35,7 @@ jobs: ls go env echo $(go env GOPATH) + ls $(go env GOPATH) - name: Go PATH From 79f1cfb66a5b39479069bfd9b56160b3c5b864a3 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:32:46 +0800 Subject: [PATCH 16/75] actions --- .github/workflows/push.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 5c0f500..b92befc 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -36,10 +36,12 @@ jobs: go env echo $(go env GOPATH) ls $(go env GOPATH) + echo $(go env GOPATH)/bin/ + ls $(go env GOPATH)/bin/ - - name: Go PATH - run: export PATH=$PATH:$(go env GOPATH)/bin +# - name: Go PATH +# run: export PATH=$PATH:$(go env GOPATH)/bin/ - name: Get dependencies run: | From 046fcaee4fac034ead657402d5e9be49aca4a53f Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:36:35 +0800 Subject: [PATCH 17/75] actions --- .github/workflows/push.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b92befc..388ef5b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -28,28 +28,15 @@ jobs: ls mkdir -vp go/{src/,bin/,pkg/} mv go-stress-testing go/src/ - export GOPATH=$(pwd)/go/ + export GOPATH=$(pwd)/go export GO111MODULE=off cd go/src/go-stress-testing pwd - ls - go env - echo $(go env GOPATH) - ls $(go env GOPATH) echo $(go env GOPATH)/bin/ - ls $(go env GOPATH)/bin/ - -# - name: Go PATH -# run: export PATH=$PATH:$(go env GOPATH)/bin/ - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi + - name: Go PATH + run: export PATH=$PATH:$(go env GOPATH)/bin/ - name: ls run: | From 01cfbedb842eeebc17d0afd3f768b1a65cec34a0 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:38:49 +0800 Subject: [PATCH 18/75] actions --- .github/workflows/push.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 388ef5b..42d9a22 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -34,9 +34,8 @@ jobs: pwd echo $(go env GOPATH)/bin/ - - - name: Go PATH - run: export PATH=$PATH:$(go env GOPATH)/bin/ +# - name: Go PATH +# run: export PATH=$PATH:$(go env GOPATH)/bin/ - name: ls run: | From 886468eb0ef684ac126a72afe3d96b4335f11ee6 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 14:44:33 +0800 Subject: [PATCH 19/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/push.yml | 46 ----------------------------------- .github/workflows/release.yml | 40 ------------------------------ .gitignore | 3 ++- README.md | 3 +++ 4 files changed, 5 insertions(+), 87 deletions(-) delete mode 100644 .github/workflows/push.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml deleted file mode 100644 index 42d9a22..0000000 --- a/.github/workflows/push.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Go actions -on: - push: - branches: - - master -jobs: - - build: - name: Build - runs-on: ubuntu-latest - steps: - - - name: Set up Go 1.12 - uses: actions/setup-go@v1 - with: - go-version: 1.12 - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v1 - - #/home/runner/work/actions - - name: go path - run: | - ls - cd .. - pwd - ls - mkdir -vp go/{src/,bin/,pkg/} - mv go-stress-testing go/src/ - export GOPATH=$(pwd)/go - export GO111MODULE=off - cd go/src/go-stress-testing - pwd - echo $(go env GOPATH)/bin/ - -# - name: Go PATH -# run: export PATH=$PATH:$(go env GOPATH)/bin/ - - - name: ls - run: | - ls - pwd - - - name: run - run: go run main.go -c 1 -n 100 -u https://www.baidu.com/ \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index cb73e23..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Release -on: - create: - tags: - - v* -jobs: - - release: - name: Release on GitHub - runs-on: ubuntu-latest - steps: - - name: Check out chode - uses: actions/checkout@v1 - - - name: Validates GO releaser config - uses: docker://goreleaser/goreleaser:latest - with: - args: check - -#/home/runner/work/actions/actions - - name: go path - run: | - ls - cd .. - pwd - mkdir -vp go/{src/,bin/,pkg/} - mv go-stress-testing go/src/ - export GOPATH=${pwd}/go - export GO111MODULE=off - cd go/src/go-stress-testing - - - name: Go PATH - run: export PATH=$PATH:$(go env GOPATH)/bin - - - name: Create release on GitHub - uses: docker://goreleaser/goreleaser:latest - with: - args: release - env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 66b3677..d3148ed 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ go_stress_testing_mac go_stress_testing_linux go_stress_testing_win.exe curl.txt -*curl.txt \ No newline at end of file +*curl.txt +go-stress-testing-mac \ No newline at end of file diff --git a/README.md b/README.md index 969742b..7403f57 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 - 可以在 mac/linux/windows 不同平台下执行的命令 +- [go-stress-testing][https://github.com/link1st/go-stress-testing/releases] 下载地址 + 参数说明: `-c` 表示并发数 @@ -439,6 +441,7 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 ### 4.2 用法 +- [go-stress-testing][https://github.com/link1st/go-stress-testing/releases] 下载地址 - 支持参数: ``` From 4a05ecf3aa650a9dad4ece6d9178acb3f9316377 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 15:01:49 +0800 Subject: [PATCH 20/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 ++++----- README.md | 27 +++++++++++---------------- build.sh | 6 +++--- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index d3148ed..946e4c5 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,8 @@ main main_linux main_win.exe -go_stress_testing_mac -go_stress_testing_linux -go_stress_testing_win.exe +go-stress-testing-mac +go-stress-testing-linux +go-stress-testing-win.exe curl.txt -*curl.txt -go-stress-testing-mac \ No newline at end of file +*curl.txt \ No newline at end of file diff --git a/README.md b/README.md index 7403f57..4c4e204 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 - 可以在 mac/linux/windows 不同平台下执行的命令 -- [go-stress-testing][https://github.com/link1st/go-stress-testing/releases] 下载地址 +- [go-stress-testing](https://github.com/link1st/go-stress-testing/releases) 压测工具下载地址 参数说明: @@ -65,15 +65,8 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 ```shell -# clone 项目 -git clone https://github.com/link1st/go-stress-testing.git - -# 进入项目目录 -cd go-stress-testing - -# 运行 -go run main.go -c 1 -n 100 -u https://www.baidu.com/ - +# 运行 以mac为示例 +./go-stress-testing-mac -c 1 -n 100 -u https://www.baidu.com/ ``` @@ -462,27 +455,29 @@ Usage of ./go_stress_testing_mac: - `-n` 是单个用户请求的次数,请求总次数 = `-c`* `-n`, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数 +- [下载地址](https://github.com/link1st/go-stress-testing/releases) + +- 下载以后执行下面命令即可压测 - 使用示例: ``` # 查看用法 -go run main.go +./go-stress-testing-mac # 使用请求百度页面 -go run main.go -c 1 -n 100 -u https://www.baidu.com/ +./go-stress-testing-mac -c 1 -n 100 -u https://www.baidu.com/ # 使用debug模式请求百度页面 -go run main.go -c 1 -n 1 -d true -u https://www.baidu.com/ +./go-stress-testing-mac -c 1 -n 1 -d true -u https://www.baidu.com/ # 使用 curl文件(文件在curl目录下) 的方式请求 -go run main.go -c 1 -n 1 -p curl/baidu.curl.txt +./go-stress-testing-mac -c 1 -n 1 -p curl/baidu.curl.txt # 压测webSocket连接 -go run main.go -c 10 -n 10 -u ws://127.0.0.1:8089/acc +./go-stress-testing-mac -c 10 -n 10 -u ws://127.0.0.1:8089/acc ``` - - 使用 curl文件进行压测 curl是Linux在命令行下的工作的文件传输工具,是一款很强大的http命令行工具。 diff --git a/build.sh b/build.sh index 822276c..818dd21 100755 --- a/build.sh +++ b/build.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # 编译mac下可以执行文件 -go build -o go_stress_testing_mac main.go +go build -o go-stress-testing-mac main.go # 使用交叉编译 linux和windows版本可以执行的文件 -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go_stress_testing_linux main.go -CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o go_stress_testing_win.exe main.go +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go-stress-testing-linux main.go +CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o go-stress-testing-win.exe main.go From a2d0db479a7f9f7d003e0c5b9c312cf56ed7afe6 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 11 Sep 2019 15:05:48 +0800 Subject: [PATCH 21/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4c4e204..6e2cc79 100644 --- a/README.md +++ b/README.md @@ -434,11 +434,11 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 ### 4.2 用法 -- [go-stress-testing][https://github.com/link1st/go-stress-testing/releases] 下载地址 +- [go-stress-testing](https://github.com/link1st/go-stress-testing/releases) 下载地址 - 支持参数: ``` -Usage of ./go_stress_testing_mac: +Usage of ./go-stress-testing-mac: -c uint 并发数 (default 1) -d string @@ -455,8 +455,6 @@ Usage of ./go_stress_testing_mac: - `-n` 是单个用户请求的次数,请求总次数 = `-c`* `-n`, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数 -- [下载地址](https://github.com/link1st/go-stress-testing/releases) - - 下载以后执行下面命令即可压测 - 使用示例: From 9ecc1f19f67d623cee70f25f4ca96e9d78dd2d31 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Fri, 21 Feb 2020 11:58:22 +0800 Subject: [PATCH 22/75] fix io.Reader bug --- model/curl_model.go | 19 +++---------------- model/curl_model_test.go | 2 +- model/request_model.go | 10 ++++++++-- server/golink/http_link.go | 2 +- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/model/curl_model.go b/model/curl_model.go index 191c4bd..368c2e8 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -10,7 +10,6 @@ package model import ( "encoding/json" "errors" - "io" "io/ioutil" "os" "strings" @@ -172,7 +171,7 @@ func (c *CURL) GetHeadersStr() string { } // 获取body -func (c *CURL) GetBody() (body io.Reader) { +func (c *CURL) GetBody() (body string) { value, ok := c.Data["--data"] if !ok { @@ -189,20 +188,8 @@ func (c *CURL) GetBody() (body io.Reader) { return } - body = strings.NewReader(value[0]) + // body = strings.NewReader(value[0]) + body = value[0] return } - -// 获取body -func (c *CURL) GetBodyStr() (str string) { - - body := c.GetBody() - if body == nil { - - return - } - bytes, _ := ioutil.ReadAll(body) - str = string(bytes) - return -} diff --git a/model/curl_model_test.go b/model/curl_model_test.go index dbf7ec4..ef6a7aa 100644 --- a/model/curl_model_test.go +++ b/model/curl_model_test.go @@ -25,7 +25,7 @@ func TestCurl(t *testing.T) { 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.GetBodyStr()) + 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 35e479d..f2809f1 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -70,7 +70,7 @@ type Request struct { Form string // http/webSocket/tcp Method string // 方法 get/post/put Headers map[string]string // Headers - Body io.Reader // body + Body string // body Verify string // 验证的方法 VerifyHttp VerifyHttp // 验证的方法 VerifyWebSocket VerifyWebSocket // 验证的方法 @@ -81,6 +81,12 @@ type Request struct { // 循环事件 切片 时间 动作 } +func (r *Request) GetBody() (body io.Reader) { + body = strings.NewReader(r.Body) + + return +} + // NewRequest // url 压测的url // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json @@ -92,7 +98,7 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa var ( method = "GET" headers = make(map[string]string) - body io.Reader + body string ) if path != "" { diff --git a/server/golink/http_link.go b/server/golink/http_link.go index 1d70292..4b7f223 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -31,7 +31,7 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg errCode = model.HttpOk ) - resp, err := client.HttpRequest(request.Method, request.Url, request.Body, request.Headers, request.Timeout) + resp, err := client.HttpRequest(request.Method, request.Url, request.GetBody(), request.Headers, request.Timeout) requestTime := uint64(heper.DiffNano(startTime)) // resp, err := server.HttpGetResp(request.Url) if err != nil { From dc01d61ec325201a8b38ba8f273da971c8b7a7f4 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 6 May 2020 15:42:03 +0800 Subject: [PATCH 23/75] =?UTF-8?q?fix=20postman=20curl=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- curl/test.chrome.curl.copy.txt | 11 +++ curl/test.curl.txt | 11 +++ curl/test.postman.curl.copy.txt | 4 + model/curl_model.go | 138 +++++++++++++++++++++----------- model/request_model.go | 6 +- 6 files changed, 126 insertions(+), 48 deletions(-) create mode 100644 curl/test.chrome.curl.copy.txt create mode 100644 curl/test.curl.txt create mode 100644 curl/test.postman.curl.copy.txt diff --git a/.gitignore b/.gitignore index 946e4c5..ca38fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,5 @@ main_win.exe go-stress-testing-mac go-stress-testing-linux go-stress-testing-win.exe -curl.txt -*curl.txt \ No newline at end of file +model/modeltest/* +model/modeltest* \ No newline at end of file diff --git a/curl/test.chrome.curl.copy.txt b/curl/test.chrome.curl.copy.txt new file mode 100644 index 0000000..9f53972 --- /dev/null +++ b/curl/test.chrome.curl.copy.txt @@ -0,0 +1,11 @@ +curl 'https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=1464_21098_31424_31341_31464_31229_30823_31163_31475&hisdata=&req=2&csor=0' \ + -H 'Connection: keep-alive' \ + -H 'Accept: application/json, text/javascript, */*; q=0.01' \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36' \ + -H 'Sec-Fetch-Site: same-origin' \ + -H 'Sec-Fetch-Mode: cors' \ + -H 'Sec-Fetch-Dest: empty' \ + -H 'Referer: https://www.baidu.com/' \ + -H 'Accept-Language: zh-CN,zh;q=0.9' \ + -H 'Cookie: BIDUPSID=A2CDAA36D74F85E5007CAA415E35B9DF; PSTM=1588732560; BAIDUID=A2CDAA36D74F85E59E4B8060EC4A0230:FG=1; BD_HOME=1; BD_UPN=123253; H_PS_PSSID=1464_21098_31424_31341_31464_31229_30823_31163_31475' \ + --compressed \ No newline at end of file diff --git a/curl/test.curl.txt b/curl/test.curl.txt new file mode 100644 index 0000000..9f53972 --- /dev/null +++ b/curl/test.curl.txt @@ -0,0 +1,11 @@ +curl 'https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=1464_21098_31424_31341_31464_31229_30823_31163_31475&hisdata=&req=2&csor=0' \ + -H 'Connection: keep-alive' \ + -H 'Accept: application/json, text/javascript, */*; q=0.01' \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36' \ + -H 'Sec-Fetch-Site: same-origin' \ + -H 'Sec-Fetch-Mode: cors' \ + -H 'Sec-Fetch-Dest: empty' \ + -H 'Referer: https://www.baidu.com/' \ + -H 'Accept-Language: zh-CN,zh;q=0.9' \ + -H 'Cookie: BIDUPSID=A2CDAA36D74F85E5007CAA415E35B9DF; PSTM=1588732560; BAIDUID=A2CDAA36D74F85E59E4B8060EC4A0230:FG=1; BD_HOME=1; BD_UPN=123253; H_PS_PSSID=1464_21098_31424_31341_31464_31229_30823_31163_31475' \ + --compressed \ No newline at end of file diff --git a/curl/test.postman.curl.copy.txt b/curl/test.postman.curl.copy.txt new file mode 100644 index 0000000..ad81434 --- /dev/null +++ b/curl/test.postman.curl.copy.txt @@ -0,0 +1,4 @@ +curl -X GET \ + 'https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=1464_21098_31424_31341_31464_31229_30823_31163_31475&hisdata=&req=2&csor=0' \ + -H 'Postman-Token: c9b71950-61fd-43be-a38a-6596de238f0f' \ + -H 'cache-control: no-cache' \ No newline at end of file diff --git a/model/curl_model.go b/model/curl_model.go index 368c2e8..3bec701 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -20,6 +20,25 @@ type CURL struct { Data map[string][]string } +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 func ParseTheFile(path string) (curl *CURL, err error) { @@ -44,39 +63,88 @@ func ParseTheFile(path string) (curl *CURL, err error) { file.Close() }() - data, err := ioutil.ReadAll(file) + 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 + ) - dataStr := string(data) - for true { - index := strings.Index(dataStr, "'") + index := strings.Index(data, " ") if index <= 0 { break } + 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 + } - key := strings.TrimSpace(dataStr[:index]) - key = strings.ReplaceAll(key, "\n", "") + if strings.HasPrefix(data, "-") { + continue + } - dataStr = dataStr[index+1:] + var ( + endSymbol = " " + ) - index = strings.Index(dataStr, "'") + if strings.HasPrefix(data, "'") { + endSymbol = "'" + data = data[1:] + } - if index <= 0 { + index = strings.Index(data, endSymbol) + if index <= -1 { break } + value = data[:index] + data = data[index+1:] - value := dataStr[:index] + // 去除首尾空格 + data = strings.TrimFunc(data, func(r rune) bool { + if r == ' ' || r == '\\' || r == '\n' { + return true + } - dataStr = dataStr[index+1:] + return false + }) curl.Data[key] = append(curl.Data[key], value) + // break + } + // for key, value := range curl.Data { + // fmt.Println("key:", key, "value:", value) + // } + return } @@ -88,12 +156,9 @@ func (c *CURL) String() (url string) { // GetUrl func (c *CURL) GetUrl() (url string) { - value, ok := c.Data["curl"] - if !ok { - - return - } + keys := []string{"curl"} + value := c.getDataValue(keys) if len(value) <= 0 { return @@ -108,24 +173,17 @@ func (c *CURL) GetUrl() (url string) { func (c *CURL) GetMethod() (method string) { method = "GET" - if _, ok := c.Data["--data"]; ok { - method = "POST" + var ( + postKeys = []string{"--d", "--data", "--data-binary $", "--data-binary"} + ) + value := c.getDataValue(postKeys) - return + if len(value) >= 1 { + return "POST" } - // TODO::目前发送不了 - if _, ok := c.Data["--data-binary $"]; ok { - method = "POST" - - return - } - - value, ok := c.Data["-X"] - if !ok { - - return - } + keys := []string{"-X", "--request"} + value = c.getDataValue(keys) if len(value) <= 0 { @@ -141,11 +199,8 @@ func (c *CURL) GetMethod() (method string) { func (c *CURL) GetHeaders() (headers map[string]string) { headers = make(map[string]string, 0) - value, ok := c.Data["-H"] - if !ok { - - return - } + keys := []string{"-H", "--header"} + value := c.getDataValue(keys) for _, v := range value { index := strings.Index(v, ":") @@ -173,15 +228,8 @@ func (c *CURL) GetHeadersStr() string { // 获取body func (c *CURL) GetBody() (body string) { - value, ok := c.Data["--data"] - if !ok { - // data-binary - value, ok = c.Data["--data-binary $"] - if !ok { - - return - } - } + keys := []string{"--data", "-d", "--data-raw", "--data-binary"} + value := c.getDataValue(keys) if len(value) <= 0 { diff --git a/model/request_model.go b/model/request_model.go index f2809f1..2469454 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -122,6 +122,9 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa form = FormTypeHttp } else if strings.HasPrefix(url, "ws://") || strings.HasPrefix(url, "wss://") { form = FormTypeWebSocket + } else { + form = FormTypeHttp + url = fmt.Sprintf("http://%s", url) } if form == "" { @@ -194,7 +197,8 @@ func (r *Request) Print() { return } - result := fmt.Sprintf("request:\n url:%s \n form:%s \n method:%s \n headers:%v \n", r.Url, r.Form, 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 debu:%v \n", result, r.Verify, r.Timeout, r.Debug) fmt.Println(result) From 38e554e9c6ddeb014a2daaf026f2613d28b6dea7 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 6 May 2020 17:44:23 +0800 Subject: [PATCH 24/75] add test --- ...t.chrome.curl.copy.txt => test.chrome.curl.txt} | 0 curl/test.post.cur.txt | 14 ++++++++++++++ ...postman.curl.copy.txt => test.postman.curl.txt} | 0 3 files changed, 14 insertions(+) rename curl/{test.chrome.curl.copy.txt => test.chrome.curl.txt} (100%) create mode 100644 curl/test.post.cur.txt rename curl/{test.postman.curl.copy.txt => test.postman.curl.txt} (100%) diff --git a/curl/test.chrome.curl.copy.txt b/curl/test.chrome.curl.txt similarity index 100% rename from curl/test.chrome.curl.copy.txt rename to curl/test.chrome.curl.txt diff --git a/curl/test.post.cur.txt b/curl/test.post.cur.txt new file mode 100644 index 0000000..63f8c3b --- /dev/null +++ b/curl/test.post.cur.txt @@ -0,0 +1,14 @@ +curl 'https://page.aliyun.com/delivery/plan/list' \ + -H 'authority: page.aliyun.com' \ + -H 'accept: application/json, text/plain, */*' \ + -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36' \ + -H 'content-type: application/x-www-form-urlencoded' \ + -H 'origin: https://cn.aliyun.com' \ + -H 'sec-fetch-site: same-site' \ + -H 'sec-fetch-mode: cors' \ + -H 'sec-fetch-dest: empty' \ + -H 'referer: https://cn.aliyun.com/' \ + -H 'accept-language: zh-CN,zh;q=0.9' \ + -H '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' \ + --data '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' \ + --compressed \ No newline at end of file diff --git a/curl/test.postman.curl.copy.txt b/curl/test.postman.curl.txt similarity index 100% rename from curl/test.postman.curl.copy.txt rename to curl/test.postman.curl.txt From b211ce675611713b21565cdc1f25cad0277192f1 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 6 May 2020 17:55:08 +0800 Subject: [PATCH 25/75] update timeout --- model/request_model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/request_model.go b/model/request_model.go index 2469454..9f6091d 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -170,7 +170,7 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } if timeout == 0 { - timeout = 3 * time.Second + timeout = 30 * time.Second } request = &Request{ From 7a18fa9a0df6f5ffa59cc61604e6035c8660cb5f Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Mon, 18 May 2020 11:15:21 +0800 Subject: [PATCH 26/75] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=A1=8C=20header=E3=80=81body=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++++++++++++++ main.go | 18 +++++++++++++++++- model/curl_model.go | 5 +++-- model/request_model.go | 21 ++++++++++++++++++++- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e2cc79..f03f473 100644 --- a/README.md +++ b/README.md @@ -476,6 +476,23 @@ Usage of ./go-stress-testing-mac: ./go-stress-testing-mac -c 10 -n 10 -u ws://127.0.0.1:8089/acc ``` +- 完整压测命令示例 +```shell script +# 更多参数 支持 header、post body +go run main.go -c 1 -n 1 -d true -u 'https://page.aliyun.com/delivery/plan/list' \ + -H 'authority: page.aliyun.com' \ + -H 'accept: application/json, text/plain, */*' \ + -H 'content-type: application/x-www-form-urlencoded' \ + -H 'origin: https://cn.aliyun.com' \ + -H 'sec-fetch-site: same-site' \ + -H 'sec-fetch-mode: cors' \ + -H 'sec-fetch-dest: empty' \ + -H 'referer: https://cn.aliyun.com/' \ + -H 'accept-language: zh-CN,zh;q=0.9' \ + -H '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' \ + -data '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' +``` + - 使用 curl文件进行压测 curl是Linux在命令行下的工作的文件传输工具,是一款很强大的http命令行工具。 diff --git a/main.go b/main.go index 3fc9e4a..6c30b99 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,18 @@ import ( "strings" ) +type array []string + +func (a *array) String() string { + return fmt.Sprint(*a) +} + +func (a *array) Set(s string) error { + *a = append(*a, s) + + return nil +} + // go 实现的压测工具 // // 编译可执行文件 @@ -31,6 +43,8 @@ func main() { requestUrl string // 压测的url 目前支持,http/https ws/wss path string // curl文件路径 http接口压测,自定义参数设置 verify string // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json + headers array // 自定义头信息传递给服务器 + body string // HTTP POST方式传送数据 ) flag.Uint64Var(&concurrency, "c", 1, "并发数") @@ -39,6 +53,8 @@ func main() { flag.StringVar(&requestUrl, "u", "", "压测地址") flag.StringVar(&path, "p", "", "curl文件路径") flag.StringVar(&verify, "v", "", "验证方法 http 支持:statusCode、json webSocket支持:json") + flag.Var(&headers, "H", "自定义头信息传递给服务器 示例:-header 'Content-Type: application/json'") + flag.StringVar(&body, "data", "", "HTTP POST方式传送数据") // 解析参数 flag.Parse() @@ -53,7 +69,7 @@ func main() { } debug := strings.ToLower(debugStr) == "true" - request, err := model.NewRequest(requestUrl, verify, 0, debug, path) + request, err := model.NewRequest(requestUrl, verify, 0, debug, path, headers, body) if err != nil { fmt.Printf("参数不合法 %v \n", err) diff --git a/model/curl_model.go b/model/curl_model.go index 3bec701..56fbf7b 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -208,9 +208,10 @@ func (c *CURL) GetHeaders() (headers map[string]string) { continue } - vIndex := index + 2 + vIndex := index + 1 if len(v) >= vIndex { - headers[v[:index]] = v[vIndex:] + value := v[vIndex:] + headers[v[:index]] = strings.TrimPrefix(value, " ") } } diff --git a/model/request_model.go b/model/request_model.go index 9f6091d..b29068d 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -93,7 +93,7 @@ func (r *Request) GetBody() (body io.Reader) { // timeout 请求超时时间 // debug 是否开启debug // path curl文件路径 http接口压测,自定义参数设置 -func NewRequest(url string, verify string, timeout time.Duration, debug bool, path 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" @@ -115,6 +115,25 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa method = curl.GetMethod() headers = curl.GetHeaders() body = curl.GetBody() + } else { + + if reqBody != "" { + method = "POST" + body = reqBody + } + + for _, v := range reqHeaders { + index := strings.Index(v, ":") + if index < 0 { + continue + } + + vIndex := index + 1 + if len(v) >= vIndex { + value := v[vIndex:] + headers[v[:index]] = strings.TrimPrefix(value, " ") + } + } } form := "" From 166953ca4f1db6b3565d80c5bd17e1d4f889a097 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Mon, 18 May 2020 14:00:39 +0800 Subject: [PATCH 27/75] fix --- model/curl_model.go | 11 +---------- model/request_model.go | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/model/curl_model.go b/model/curl_model.go index 56fbf7b..0f1a041 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -203,16 +203,7 @@ func (c *CURL) GetHeaders() (headers map[string]string) { value := c.getDataValue(keys) for _, v := range value { - index := strings.Index(v, ":") - if index < 0 { - continue - } - - vIndex := index + 1 - if len(v) >= vIndex { - value := v[vIndex:] - headers[v[:index]] = strings.TrimPrefix(value, " ") - } + getHeaderValue(v, headers) } return diff --git a/model/request_model.go b/model/request_model.go index b29068d..7670c0a 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -120,19 +120,12 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa if reqBody != "" { method = "POST" body = reqBody + + headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8" } for _, v := range reqHeaders { - index := strings.Index(v, ":") - if index < 0 { - continue - } - - vIndex := index + 1 - if len(v) >= vIndex { - value := v[vIndex:] - headers[v[:index]] = strings.TrimPrefix(value, " ") - } + getHeaderValue(v, headers) } } @@ -209,6 +202,24 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } +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 { + headers[v[:index]] = value + } + } +} + // 打印 func (r *Request) Print() { if r == nil { From 3a96307b99a88f69ef955af18849430c3439d72c Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 20 May 2020 18:31:56 +0800 Subject: [PATCH 28/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f03f473..15e595a 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,9 @@ - [6.3 客户端配置](#63-客户端配置) - [6.4 准备](#64-准备) - [6.5 压测数据](#65-压测数据) -- [7、总结](#7总结) -- [8、参考文献](#8参考文献) +- [7、常见问题](#7常见问题) +- [8、总结](#8总结) +- [9、参考文献](#9参考文献) ## 1、项目说明 @@ -849,7 +850,17 @@ VmSize: 27133804 kB 至此,压测已经全部完成,单台机器支持100W连接已经满足~ -## 7、总结 +## 7.常见问题 +- **Q:** 压测过程中服务器出现大量 **TIME_WAIT** + + A: 参考TCP四次挥手原理,主动关闭连接的一方会出现 **TIME_WAIT** 状态,等待的时长为 2MSL(约1分钟左右) + + 原因是:主动断开的一方回复 ACK 消息可能丢失,TCP 是可靠的传输协议,在没有收到 ACK 消息的另一端会重试,重新发送FIN消息,所以主动关闭的一方会等待 2MSL 时间,防止对方重试,这就出现了大量 **TIME_WAIT** 状态(参考: 四次挥手的最后两次) + +TCP 握手: +![TCP握手](https://img.mukewang.com/5ec504300001aa7b08301233.png) + +## 8、总结 到这里压测总算完成,本次压测花费16元巨款。 @@ -858,7 +869,7 @@ VmSize: 27133804 kB 本文通过介绍什么是压测,在什么情况下需要压测,通过单台机器100W长连接的压测实战了解Linux内核的参数的调优。如果觉得现有的压测工具不适用,可以自己实现或者是改造成属于自己的自己的工具。 -## 8、参考文献 +## 9、参考文献 [性能测试工具](https://testerhome.com/topics/17068) From af5077b999b8eeadf0ab357a155b9d722b177e01 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 20 May 2020 18:32:37 +0800 Subject: [PATCH 29/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15e595a..a004f02 100644 --- a/README.md +++ b/README.md @@ -851,7 +851,7 @@ VmSize: 27133804 kB 至此,压测已经全部完成,单台机器支持100W连接已经满足~ ## 7.常见问题 -- **Q:** 压测过程中服务器出现大量 **TIME_WAIT** +- **Q:** 压测过程中会出现大量 **TIME_WAIT** A: 参考TCP四次挥手原理,主动关闭连接的一方会出现 **TIME_WAIT** 状态,等待的时长为 2MSL(约1分钟左右) From a8ab10d978d8eeca23ed533f31c94eeac2ff130c Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 20 May 2020 18:36:17 +0800 Subject: [PATCH 30/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a004f02..09e5c70 100644 --- a/README.md +++ b/README.md @@ -858,7 +858,7 @@ VmSize: 27133804 kB 原因是:主动断开的一方回复 ACK 消息可能丢失,TCP 是可靠的传输协议,在没有收到 ACK 消息的另一端会重试,重新发送FIN消息,所以主动关闭的一方会等待 2MSL 时间,防止对方重试,这就出现了大量 **TIME_WAIT** 状态(参考: 四次挥手的最后两次) TCP 握手: -![TCP握手](https://img.mukewang.com/5ec504300001aa7b08301233.png) + ## 8、总结 From f56182c6c18a71242585c83b8d2f7488a4dbf6fa Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 3 Jun 2020 20:34:12 +0800 Subject: [PATCH 31/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 09e5c70..bf664bb 100644 --- a/README.md +++ b/README.md @@ -429,7 +429,7 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 ### 4.1 介绍 -- go-stress-testing 是go语言实现的简单压测工具,源码开源、支持二次开发,可以压测http、webSocket请求,使用协程模拟单个用户,可以更高效的利用CPU资源。 +- go-stress-testing 是go语言实现的简单压测工具,源码开源、支持二次开发,可以压测http、webSocket请求、私有rpc调用,使用协程模拟单个用户,可以更高效的利用CPU资源。 - 项目地址 [https://github.com/link1st/go-stress-testing](https://github.com/link1st/go-stress-testing) @@ -441,17 +441,21 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 ``` Usage of ./go-stress-testing-mac: -c uint - 并发数 (default 1) + 并发数 (default 1) -d string - 调试模式 (default "false") + 调试模式 (default "false") + -data string + HTTP POST方式传送数据 -n uint - 请求总数 (default 1) + 请求总数 (default 1) -p string - curl文件路径 + curl文件路径 -u string - 请求地址 + 压测地址 -v string - 验证方法 http 支持:statusCode、json webSocket支持:json (default "statusCode") + 验证方法 http 支持:statusCode、json webSocket支持:json + -H value + 自定义头信息传递给服务器 示例:-header 'Content-Type: application/json' ``` - `-n` 是单个用户请求的次数,请求总次数 = `-c`* `-n`, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数 From 8699eb4556c5b62e593ced49bb505fa5299792bb Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 3 Jun 2020 20:47:04 +0800 Subject: [PATCH 32/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf664bb..acc26d4 100644 --- a/README.md +++ b/README.md @@ -505,8 +505,12 @@ curl是Linux在命令行下的工作的文件传输工具,是一款很强大 使用curl文件可以压测使用非GET的请求,支持设置http请求的 method、cookies、header、body等参数 -chrome 浏览器生成 curl文件,打开开发者模式(快捷键F12),如图所示,生成 curl 在终端执行命令 -![copy cURL](https://img.mukewang.com/5d60eddd0001f4b016661114.png) +**I**: chrome 浏览器生成 curl文件,打开开发者模式(快捷键F12),如图所示,生成 curl 在终端执行命令 +![chrome cURL](https://img.mukewang.com/5d60eddd0001f4b016661114.png) + +**II** postman 生成 curl 命令 +![postman cURL](https://img.mukewang.com/5ed79b590001837120581530.png) + 生成内容粘贴到项目目录下的**curl/baidu.curl.txt**文件中,执行下面命令就可以从curl.txt文件中读取需要压测的内容进行压测了 From d97e33e118af3b77c551416a024453d8274783f3 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Wed, 3 Jun 2020 21:38:44 +0800 Subject: [PATCH 33/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index acc26d4..fa8e58a 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ 本文介绍压测是什么,解释压测的专属名词,教大家如何压测。介绍市面上的常见压测工具(ab、locust、Jmeter、go实现的压测工具、云压测),对比这些压测工具,教大家如何选择一款适合自己的压测工具,本文还有两个压测实战项目: -- 单台机器对HTTP短连接 QPS 1W+ 的压测实战 -- 单台机器100W长连接的压测实战 +- 单台机器对 HTTP 短连接 QPS 1W+ 的压测实战 +- 单台机器 100W 长连接的压测实战 ## 目录 - [1、项目说明](#1项目说明) @@ -48,7 +48,7 @@ ## 1、项目说明 ### 1.1 go-stress-testing -go 实现的压测工具,每个用户用一个协程的方式模拟,最大限度的利用CPU资源 +go 实现的压测工具,每个用户用一个协程的方式模拟,最大限度的利用 CPU 资源 ### 1.2 项目体验 @@ -60,7 +60,7 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 `-c` 表示并发数 -`-n` 每个并发执行请求的次数,总请求的次数 = 并发数 * 每个并发执行请求的次数 +`-n` 每个并发执行请求的次数,总请求的次数 = 并发数 `*` 每个并发执行请求的次数 `-u` 需要压测的地址 @@ -125,7 +125,6 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 **错误码**: 压测中,接口返回的 code码:返回次数的集合 - ## 2、压测 ### 2.1 压测是什么 @@ -138,7 +137,6 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 - 压测的目的就是通过压测(模拟真实用户的行为),测算出机器的性能(单台机器的QPS),从而推算出系统在承受指定用户数(100W)时,需要多少机器能支撑得住 - 压测是在上线前为了应对未来可能达到的用户数量的一次预估(提前演练),压测以后通过优化程序的性能或准备充足的机器,来保证用户的体验。 - ### 2.3 压测名词解释 #### 2.3.1 压测类型解释 @@ -183,10 +181,10 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 ### 2.4 如何计算压测指标 -- 压测我们需要有目的性的压测,这次压测我们需要达到什么目标(如:单台机器的性能为100QPS?网站能同时满足100W人同时在线) +- 压测我们需要有目的性的压测,这次压测我们需要达到什么目标(如:单台机器的性能为 100QPS?网站能同时满足100W人同时在线) - 可以通过以下计算方法来进行计算: - 压测原则:每天80%的访问量集中在20%的时间里,这20%的时间就叫做峰值 -- 公式: ( 总PV数*80% ) / ( 每天的秒数*20% ) = 峰值时间每秒钟请求数(QPS) +- 公式: ( 总PV数`*`80% ) / ( 每天的秒数`*`20% ) = 峰值时间每秒钟请求数(QPS) - 机器: 峰值时间每秒钟请求数(QPS) / 单台机器的QPS = 需要的机器的数量 - 假设:网站每天的用户数(100W),每天的用户的访问量约为3000W PV,这台机器的需要多少QPS? @@ -199,9 +197,9 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 ### 3.1 ab - 简介 -ApacheBench 是 Apache服务器自带的一个web压力测试工具,简称ab。ab又是一个命令行工具,对发起负载的本机要求很低,根据ab命令可以创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访问,因此可以用来测试目标服务器的负载压力。总的来说ab工具小巧简单,上手学习较快,可以提供需要的基本性能指标,但是没有图形化结果,不能监控。 +ApacheBench 是 Apache 服务器自带的一个web压力测试工具,简称 ab。ab 又是一个命令行工具,对发起负载的本机要求很低,根据 ab 命令可以创建很多的并发访问线程,模拟多个访问者同时对某一 URL 地址进行访问,因此可以用来测试目标服务器的负载压力。总的来说 ab 工具小巧简单,上手学习较快,可以提供需要的基本性能指标,但是没有图形化结果,不能监控。 -ab属于一个轻量级的压测工具,结果不会特别准确,可以用作参考。 +ab 属于一个轻量级的压测工具,结果不会特别准确,可以用作参考。 - 安装 @@ -291,7 +289,7 @@ Percentage of the requests served within a certain time (ms) - `Failed requests` 失败个数 -- `Requests per second` 吞吐量,指的是某个并发用户下单位时间内处理的请求数。等效于QPS,其实可以看作同一个统计方式,只是叫法不同而已。 +- `Requests per second` 吞吐量,指的是某个并发用户下单位时间内处理的请求数。等效于 QPS,其实可以看作同一个统计方式,只是叫法不同而已。 - `Time per request` 用户平均请求等待时间 @@ -301,7 +299,7 @@ Percentage of the requests served within a certain time (ms) - 简介 -是非常简单易用、分布式、python开发的压力测试工具。有图形化界面,支持将压测数据导出。 +是非常简单易用、分布式、python 开发的压力测试工具。有图形化界面,支持将压测数据导出。 - 安装 @@ -505,13 +503,12 @@ curl是Linux在命令行下的工作的文件传输工具,是一款很强大 使用curl文件可以压测使用非GET的请求,支持设置http请求的 method、cookies、header、body等参数 -**I**: chrome 浏览器生成 curl文件,打开开发者模式(快捷键F12),如图所示,生成 curl 在终端执行命令 +**I:** chrome 浏览器生成 curl文件,打开开发者模式(快捷键F12),如图所示,生成 curl 在终端执行命令 ![chrome cURL](https://img.mukewang.com/5d60eddd0001f4b016661114.png) -**II** postman 生成 curl 命令 +**II:** postman 生成 curl 命令 ![postman cURL](https://img.mukewang.com/5ed79b590001837120581530.png) - 生成内容粘贴到项目目录下的**curl/baidu.curl.txt**文件中,执行下面命令就可以从curl.txt文件中读取需要压测的内容进行压测了 ``` From a973f4ad4a24d7363b937c99fd99fbd15fb6854d Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Tue, 9 Jun 2020 14:22:16 +0800 Subject: [PATCH 34/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa8e58a..4f26437 100644 --- a/README.md +++ b/README.md @@ -434,6 +434,7 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 ### 4.2 用法 - [go-stress-testing](https://github.com/link1st/go-stress-testing/releases) 下载地址 +- clone 项目源码运行的时候,需要将项目 clone 到 **$GOPATH** 目录下 - 支持参数: ``` From 3f325bf9040c35320445d08e04bfbf3472212540 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 18 Jun 2020 12:56:35 +0800 Subject: [PATCH 35/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4f26437..d291141 100644 --- a/README.md +++ b/README.md @@ -573,6 +573,7 @@ package main import ( "log" "net/http" + "runtime" ) const ( From 11dcf09991083ecae7144167069d1f40855e48e0 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 18 Jun 2020 13:00:14 +0800 Subject: [PATCH 36/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d291141..639e3b4 100644 --- a/README.md +++ b/README.md @@ -585,7 +585,7 @@ func main() { runtime.GOMAXPROCS(runtime.NumCPU() - 1) hello := func(w http.ResponseWriter, req *http.Request) { - data := "Hello, World!" + data := "Hello, go-stress-testing! \n" w.Header().Add("Server", "golang") w.Write([]byte(data)) From 83916d9881d20cc3eb543a82879b4874af2dd83f Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 18 Jun 2020 13:15:20 +0800 Subject: [PATCH 37/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 639e3b4..a15225e 100644 --- a/README.md +++ b/README.md @@ -897,3 +897,12 @@ TCP 握手: [https://github.com/link1st/go-stress-testing](https://github.com/link1st/go-stress-testing) github 搜:link1st 查看项目 go-stress-testing + +### 意见反馈 + +- 在项目中遇到问题可以直接在这里找找答案或者提问 [issues](https://github.com/link1st/go-stress-testing/issues) +- 也可以添加我的微信(申请信息填写:公司、姓名,我好备注下),直接反馈给我 +
+

+ 添加link1st的微信 +

From 339e1477b388fc244a4ac8a505ae97407e3fb1c2 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 18 Jun 2020 16:38:33 +0800 Subject: [PATCH 38/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- model/request_model.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a15225e..44d067e 100644 --- a/README.md +++ b/README.md @@ -159,9 +159,9 @@ go 实现的压测工具,每个用户用一个协程的方式模拟,最大 | 请求成功数(Request Success Number) | 在一次压测中,请求成功的数量 | | 请求失败数(Request Failures Number) | 在一次压测中,请求失败的数量 | | 错误率(Error Rate) | 在压测中,请求成功的数量与请求失败数量的比率 | -| 最大响应时间(Max Response Time) | 在一次事务中,从发出请求或指令系统做出的反映(响应)的最大时间 | -| 最少响应时间(Mininum Response Time) | 在一次事务中,从发出请求或指令系统做出的反映(响应)的最少时间 | -| 平均响应时间(Average Response Time) | 在一次事务中,从发出请求或指令系统做出的反映(响应)的平均时间 | +| 最大响应时间(Max Response Time) | 在一次压测中,从发出请求或指令系统做出的反映(响应)的最大时间 | +| 最少响应时间(Mininum Response Time) | 在一次压测中,从发出请求或指令系统做出的反映(响应)的最少时间 | +| 平均响应时间(Average Response Time) | 在一次压测中,从发出请求或指令系统做出的反映(响应)的平均时间 | #### 2.3.3 机器性能指标解释 @@ -606,7 +606,7 @@ func main() { - go_stress_testing 压测命令 ``` -./go_stress_testing_linux -c 100 -n 10000 -u http://127.0.0.1:8088/ +./go-stress-testing-linux -c 100 -n 10000 -u http://127.0.0.1:8088/ ``` diff --git a/model/request_model.go b/model/request_model.go index 7670c0a..e9b6fa6 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -229,7 +229,7 @@ func (r *Request) Print() { 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 debu:%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) fmt.Println(result) return From 8dc8f2707a9b581be78d6152bc19dfa23f68614d Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 18 Jun 2020 17:53:41 +0800 Subject: [PATCH 39/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 44d067e..bdbafab 100644 --- a/README.md +++ b/README.md @@ -454,7 +454,7 @@ Usage of ./go-stress-testing-mac: -v string 验证方法 http 支持:statusCode、json webSocket支持:json -H value - 自定义头信息传递给服务器 示例:-header 'Content-Type: application/json' + 自定义头信息传递给服务器 示例:-H 'Content-Type: application/json' ``` - `-n` 是单个用户请求的次数,请求总次数 = `-c`* `-n`, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数 diff --git a/main.go b/main.go index 6c30b99..9854d3f 100644 --- a/main.go +++ b/main.go @@ -53,7 +53,7 @@ func main() { flag.StringVar(&requestUrl, "u", "", "压测地址") flag.StringVar(&path, "p", "", "curl文件路径") flag.StringVar(&verify, "v", "", "验证方法 http 支持:statusCode、json webSocket支持:json") - flag.Var(&headers, "H", "自定义头信息传递给服务器 示例:-header 'Content-Type: application/json'") + flag.Var(&headers, "H", "自定义头信息传递给服务器 示例:-H 'Content-Type: application/json'") flag.StringVar(&body, "data", "", "HTTP POST方式传送数据") // 解析参数 From 93d18ec071dad730206c2971db1d30c326f553a4 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Fri, 19 Jun 2020 09:52:49 +0800 Subject: [PATCH 40/75] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++-------- main.go | 4 ++-- server/statistics/statistics.go | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bdbafab..de4c22e 100644 --- a/README.md +++ b/README.md @@ -441,20 +441,20 @@ PTS(Performance Testing Service)是面向所有技术背景人员的云化 Usage of ./go-stress-testing-mac: -c uint 并发数 (default 1) + -n uint + 请求数(单个并发/协程) (default 1) + -u string + 压测地址 -d string 调试模式 (default "false") + -H value + 自定义头信息传递给服务器 示例:-H 'Content-Type: application/json' -data string HTTP POST方式传送数据 - -n uint - 请求总数 (default 1) - -p string - curl文件路径 - -u string - 压测地址 -v string 验证方法 http 支持:statusCode、json webSocket支持:json - -H value - 自定义头信息传递给服务器 示例:-H 'Content-Type: application/json' + -p string + curl文件路径 ``` - `-n` 是单个用户请求的次数,请求总次数 = `-c`* `-n`, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数 diff --git a/main.go b/main.go index 9854d3f..3e14d83 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func main() { var ( concurrency uint64 // 并发数 - totalNumber uint64 // 请求总数(单个并发) + totalNumber uint64 // 请求数(单个并发/协程) debugStr string // 是否是debug requestUrl string // 压测的url 目前支持,http/https ws/wss path string // curl文件路径 http接口压测,自定义参数设置 @@ -48,7 +48,7 @@ func main() { ) flag.Uint64Var(&concurrency, "c", 1, "并发数") - flag.Uint64Var(&totalNumber, "n", 1, "请求总数") + flag.Uint64Var(&totalNumber, "n", 1, "请求数(单个并发/协程)") flag.StringVar(&debugStr, "d", "false", "调试模式") flag.StringVar(&requestUrl, "u", "", "压测地址") flag.StringVar(&path, "p", "", "curl文件路径") diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 00b5ed7..efaaf3a 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -116,7 +116,7 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy fmt.Println("************************* 结果 stat ****************************") fmt.Println("处理协程数量:", concurrent) // fmt.Println("处理协程数量:", concurrent, "程序处理总时长:", fmt.Sprintf("%.3f", float64(processingTime/concurrent)/1e9), "秒") - fmt.Println("请求总数:", 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 ****************************") From 5bdc17f95f2046bf91464ff5db8c9739269aeb5a Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Mon, 22 Jun 2020 16:54:46 +0800 Subject: [PATCH 41/75] =?UTF-8?q?=E6=94=AF=E6=8C=81=20curl=20--url=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0=20=E8=AE=BE=E7=BD=AEurl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/curl_model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/curl_model.go b/model/curl_model.go index 0f1a041..8a8bc12 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -157,7 +157,7 @@ func (c *CURL) String() (url string) { // GetUrl func (c *CURL) GetUrl() (url string) { - keys := []string{"curl"} + keys := []string{"curl","--url"} value := c.getDataValue(keys) if len(value) <= 0 { From 2eaf3861c9e01bce9c5b3ce4071bcc712709e3cc Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sun, 28 Jun 2020 10:56:22 +0800 Subject: [PATCH 42/75] =?UTF-8?q?=E8=BF=81=E7=A7=BB=20go=20mod?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 5 + go.sum | 5 + vendor/golang.org/x/net/LICENSE | 27 - vendor/golang.org/x/net/PATENTS | 22 - vendor/golang.org/x/net/websocket/client.go | 106 ---- vendor/golang.org/x/net/websocket/dial.go | 24 - vendor/golang.org/x/net/websocket/hybi.go | 583 ------------------ vendor/golang.org/x/net/websocket/server.go | 113 ---- .../golang.org/x/net/websocket/websocket.go | 451 -------------- vendor/vendor.json | 14 - 10 files changed, 10 insertions(+), 1340 deletions(-) create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 vendor/golang.org/x/net/LICENSE delete mode 100644 vendor/golang.org/x/net/PATENTS delete mode 100644 vendor/golang.org/x/net/websocket/client.go delete mode 100644 vendor/golang.org/x/net/websocket/dial.go delete mode 100644 vendor/golang.org/x/net/websocket/hybi.go delete mode 100644 vendor/golang.org/x/net/websocket/server.go delete mode 100644 vendor/golang.org/x/net/websocket/websocket.go delete mode 100644 vendor/vendor.json diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d9021ef --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module go-stress-testing + +go 1.14 + +require golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c037375 --- /dev/null +++ b/go.sum @@ -0,0 +1,5 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE deleted file mode 100644 index 6a66aea..0000000 --- a/vendor/golang.org/x/net/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS deleted file mode 100644 index 7330990..0000000 --- a/vendor/golang.org/x/net/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/net/websocket/client.go b/vendor/golang.org/x/net/websocket/client.go deleted file mode 100644 index 69a4ac7..0000000 --- a/vendor/golang.org/x/net/websocket/client.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "io" - "net" - "net/http" - "net/url" -) - -// DialError is an error that occurs while dialling a websocket server. -type DialError struct { - *Config - Err error -} - -func (e *DialError) Error() string { - return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() -} - -// NewConfig creates a new WebSocket config for client connection. -func NewConfig(server, origin string) (config *Config, err error) { - config = new(Config) - config.Version = ProtocolVersionHybi13 - config.Location, err = url.ParseRequestURI(server) - if err != nil { - return - } - config.Origin, err = url.ParseRequestURI(origin) - if err != nil { - return - } - config.Header = http.Header(make(map[string][]string)) - return -} - -// NewClient creates a new WebSocket client connection over rwc. -func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - err = hybiClientHandshake(config, br, bw) - if err != nil { - return - } - buf := bufio.NewReadWriter(br, bw) - ws = newHybiClientConn(config, buf, rwc) - return -} - -// Dial opens a new client connection to a WebSocket. -func Dial(url_, protocol, origin string) (ws *Conn, err error) { - config, err := NewConfig(url_, origin) - if err != nil { - return nil, err - } - if protocol != "" { - config.Protocol = []string{protocol} - } - return DialConfig(config) -} - -var portMap = map[string]string{ - "ws": "80", - "wss": "443", -} - -func parseAuthority(location *url.URL) string { - if _, ok := portMap[location.Scheme]; ok { - if _, _, err := net.SplitHostPort(location.Host); err != nil { - return net.JoinHostPort(location.Host, portMap[location.Scheme]) - } - } - return location.Host -} - -// DialConfig opens a new client connection to a WebSocket with a config. -func DialConfig(config *Config) (ws *Conn, err error) { - var client net.Conn - if config.Location == nil { - return nil, &DialError{config, ErrBadWebSocketLocation} - } - if config.Origin == nil { - return nil, &DialError{config, ErrBadWebSocketOrigin} - } - dialer := config.Dialer - if dialer == nil { - dialer = &net.Dialer{} - } - client, err = dialWithDialer(dialer, config) - if err != nil { - goto Error - } - ws, err = NewClient(config, client) - if err != nil { - client.Close() - goto Error - } - return - -Error: - return nil, &DialError{config, err} -} diff --git a/vendor/golang.org/x/net/websocket/dial.go b/vendor/golang.org/x/net/websocket/dial.go deleted file mode 100644 index 2dab943..0000000 --- a/vendor/golang.org/x/net/websocket/dial.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "crypto/tls" - "net" -) - -func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) { - switch config.Location.Scheme { - case "ws": - conn, err = dialer.Dial("tcp", parseAuthority(config.Location)) - - case "wss": - conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig) - - default: - err = ErrBadScheme - } - return -} diff --git a/vendor/golang.org/x/net/websocket/hybi.go b/vendor/golang.org/x/net/websocket/hybi.go deleted file mode 100644 index 8cffdd1..0000000 --- a/vendor/golang.org/x/net/websocket/hybi.go +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -// This file implements a protocol of hybi draft. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 - -import ( - "bufio" - "bytes" - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "strings" -) - -const ( - websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - closeStatusNormal = 1000 - closeStatusGoingAway = 1001 - closeStatusProtocolError = 1002 - closeStatusUnsupportedData = 1003 - closeStatusFrameTooLarge = 1004 - closeStatusNoStatusRcvd = 1005 - closeStatusAbnormalClosure = 1006 - closeStatusBadMessageData = 1007 - closeStatusPolicyViolation = 1008 - closeStatusTooBigData = 1009 - closeStatusExtensionMismatch = 1010 - - maxControlFramePayloadLength = 125 -) - -var ( - ErrBadMaskingKey = &ProtocolError{"bad masking key"} - ErrBadPongMessage = &ProtocolError{"bad pong message"} - ErrBadClosingStatus = &ProtocolError{"bad closing status"} - ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} - ErrNotImplemented = &ProtocolError{"not implemented"} - - handshakeHeader = map[string]bool{ - "Host": true, - "Upgrade": true, - "Connection": true, - "Sec-Websocket-Key": true, - "Sec-Websocket-Origin": true, - "Sec-Websocket-Version": true, - "Sec-Websocket-Protocol": true, - "Sec-Websocket-Accept": true, - } -) - -// A hybiFrameHeader is a frame header as defined in hybi draft. -type hybiFrameHeader struct { - Fin bool - Rsv [3]bool - OpCode byte - Length int64 - MaskingKey []byte - - data *bytes.Buffer -} - -// A hybiFrameReader is a reader for hybi frame. -type hybiFrameReader struct { - reader io.Reader - - header hybiFrameHeader - pos int64 - length int -} - -func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { - n, err = frame.reader.Read(msg) - if frame.header.MaskingKey != nil { - for i := 0; i < n; i++ { - msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] - frame.pos++ - } - } - return n, err -} - -func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } - -func (frame *hybiFrameReader) HeaderReader() io.Reader { - if frame.header.data == nil { - return nil - } - if frame.header.data.Len() == 0 { - return nil - } - return frame.header.data -} - -func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } - -func (frame *hybiFrameReader) Len() (n int) { return frame.length } - -// A hybiFrameReaderFactory creates new frame reader based on its frame type. -type hybiFrameReaderFactory struct { - *bufio.Reader -} - -// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. -// See Section 5.2 Base Framing protocol for detail. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 -func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { - hybiFrame := new(hybiFrameReader) - frame = hybiFrame - var header []byte - var b byte - // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 - for i := 0; i < 3; i++ { - j := uint(6 - i) - hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 - } - hybiFrame.header.OpCode = header[0] & 0x0f - - // Second byte. Mask/Payload len(7bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - mask := (b & 0x80) != 0 - b &= 0x7f - lengthFields := 0 - switch { - case b <= 125: // Payload length 7bits. - hybiFrame.header.Length = int64(b) - case b == 126: // Payload length 7+16bits - lengthFields = 2 - case b == 127: // Payload length 7+64bits - lengthFields = 8 - } - for i := 0; i < lengthFields; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits - b &= 0x7f - } - header = append(header, b) - hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) - } - if mask { - // Masking key. 4 bytes. - for i := 0; i < 4; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) - } - } - hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) - hybiFrame.header.data = bytes.NewBuffer(header) - hybiFrame.length = len(header) + int(hybiFrame.header.Length) - return -} - -// A HybiFrameWriter is a writer for hybi frame. -type hybiFrameWriter struct { - writer *bufio.Writer - - header *hybiFrameHeader -} - -func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { - var header []byte - var b byte - if frame.header.Fin { - b |= 0x80 - } - for i := 0; i < 3; i++ { - if frame.header.Rsv[i] { - j := uint(6 - i) - b |= 1 << j - } - } - b |= frame.header.OpCode - header = append(header, b) - if frame.header.MaskingKey != nil { - b = 0x80 - } else { - b = 0 - } - lengthFields := 0 - length := len(msg) - switch { - case length <= 125: - b |= byte(length) - case length < 65536: - b |= 126 - lengthFields = 2 - default: - b |= 127 - lengthFields = 8 - } - header = append(header, b) - for i := 0; i < lengthFields; i++ { - j := uint((lengthFields - i - 1) * 8) - b = byte((length >> j) & 0xff) - header = append(header, b) - } - if frame.header.MaskingKey != nil { - if len(frame.header.MaskingKey) != 4 { - return 0, ErrBadMaskingKey - } - header = append(header, frame.header.MaskingKey...) - frame.writer.Write(header) - data := make([]byte, length) - for i := range data { - data[i] = msg[i] ^ frame.header.MaskingKey[i%4] - } - frame.writer.Write(data) - err = frame.writer.Flush() - return length, err - } - frame.writer.Write(header) - frame.writer.Write(msg) - err = frame.writer.Flush() - return length, err -} - -func (frame *hybiFrameWriter) Close() error { return nil } - -type hybiFrameWriterFactory struct { - *bufio.Writer - needMaskingKey bool -} - -func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} - if buf.needMaskingKey { - frameHeader.MaskingKey, err = generateMaskingKey() - if err != nil { - return nil, err - } - } - return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil -} - -type hybiFrameHandler struct { - conn *Conn - payloadType byte -} - -func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) { - if handler.conn.IsServerConn() { - // The client MUST mask all frames sent to the server. - if frame.(*hybiFrameReader).header.MaskingKey == nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } else { - // The server MUST NOT mask all frames. - if frame.(*hybiFrameReader).header.MaskingKey != nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } - if header := frame.HeaderReader(); header != nil { - io.Copy(ioutil.Discard, header) - } - switch frame.PayloadType() { - case ContinuationFrame: - frame.(*hybiFrameReader).header.OpCode = handler.payloadType - case TextFrame, BinaryFrame: - handler.payloadType = frame.PayloadType() - case CloseFrame: - return nil, io.EOF - case PingFrame, PongFrame: - b := make([]byte, maxControlFramePayloadLength) - n, err := io.ReadFull(frame, b) - if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { - return nil, err - } - io.Copy(ioutil.Discard, frame) - if frame.PayloadType() == PingFrame { - if _, err := handler.WritePong(b[:n]); err != nil { - return nil, err - } - } - return nil, nil - } - return frame, nil -} - -func (handler *hybiFrameHandler) WriteClose(status int) (err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) - if err != nil { - return err - } - msg := make([]byte, 2) - binary.BigEndian.PutUint16(msg, uint16(status)) - _, err = w.Write(msg) - w.Close() - return err -} - -func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - return n, err -} - -// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. -func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - if buf == nil { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - buf = bufio.NewReadWriter(br, bw) - } - ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, - frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, - frameWriterFactory: hybiFrameWriterFactory{ - buf.Writer, request == nil}, - PayloadType: TextFrame, - defaultCloseStatus: closeStatusNormal} - ws.frameHandler = &hybiFrameHandler{conn: ws} - return ws -} - -// generateMaskingKey generates a masking key for a frame. -func generateMaskingKey() (maskingKey []byte, err error) { - maskingKey = make([]byte, 4) - if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { - return - } - return -} - -// generateNonce generates a nonce consisting of a randomly selected 16-byte -// value that has been base64-encoded. -func generateNonce() (nonce []byte) { - key := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, key); err != nil { - panic(err) - } - nonce = make([]byte, 24) - base64.StdEncoding.Encode(nonce, key) - return -} - -// removeZone removes IPv6 zone identifer from host. -// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" -func removeZone(host string) string { - if !strings.HasPrefix(host, "[") { - return host - } - i := strings.LastIndex(host, "]") - if i < 0 { - return host - } - j := strings.LastIndex(host[:i], "%") - if j < 0 { - return host - } - return host[:j] + host[i:] -} - -// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of -// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. -func getNonceAccept(nonce []byte) (expected []byte, err error) { - h := sha1.New() - if _, err = h.Write(nonce); err != nil { - return - } - if _, err = h.Write([]byte(websocketGUID)); err != nil { - return - } - expected = make([]byte, 28) - base64.StdEncoding.Encode(expected, h.Sum(nil)) - return -} - -// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 -func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - - // According to RFC 6874, an HTTP client, proxy, or other - // intermediary must remove any IPv6 zone identifier attached - // to an outgoing URI. - bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n") - bw.WriteString("Upgrade: websocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - nonce := generateNonce() - if config.handshakeData != nil { - nonce = []byte(config.handshakeData["key"]) - } - bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") - bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - - if config.Version != ProtocolVersionHybi13 { - return ErrBadProtocolVersion - } - - bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") - if len(config.Protocol) > 0 { - bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") - } - // TODO(ukai): send Sec-WebSocket-Extensions. - err = config.Header.WriteSubset(bw, handshakeHeader) - if err != nil { - return err - } - - bw.WriteString("\r\n") - if err = bw.Flush(); err != nil { - return err - } - - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - if resp.StatusCode != 101 { - return ErrBadStatus - } - if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - expectedAccept, err := getNonceAccept(nonce) - if err != nil { - return err - } - if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { - return ErrChallengeResponse - } - if resp.Header.Get("Sec-WebSocket-Extensions") != "" { - return ErrUnsupportedExtensions - } - offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") - if offeredProtocol != "" { - protocolMatched := false - for i := 0; i < len(config.Protocol); i++ { - if config.Protocol[i] == offeredProtocol { - protocolMatched = true - break - } - } - if !protocolMatched { - return ErrBadWebSocketProtocol - } - config.Protocol = []string{offeredProtocol} - } - - return nil -} - -// newHybiClientConn creates a client WebSocket connection after handshake. -func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHybiConn(config, buf, rwc, nil) -} - -// A HybiServerHandshaker performs a server handshake using hybi draft protocol. -type hybiServerHandshaker struct { - *Config - accept []byte -} - -func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi13 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { - return http.StatusBadRequest, ErrNotWebSocket - } - - key := req.Header.Get("Sec-Websocket-Key") - if key == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - version := req.Header.Get("Sec-Websocket-Version") - switch version { - case "13": - c.Version = ProtocolVersionHybi13 - default: - return http.StatusBadRequest, ErrBadWebSocketVersion - } - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - if protocol != "" { - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - } - c.accept, err = getNonceAccept([]byte(key)) - if err != nil { - return http.StatusInternalServerError, err - } - return http.StatusSwitchingProtocols, nil -} - -// Origin parses the Origin header in req. -// If the Origin header is not set, it returns nil and nil. -func Origin(config *Config, req *http.Request) (*url.URL, error) { - var origin string - switch config.Version { - case ProtocolVersionHybi13: - origin = req.Header.Get("Origin") - } - if origin == "" { - return nil, nil - } - return url.ParseRequestURI(origin) -} - -func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - // You need choose a Protocol in Handshake func in Server. - return ErrBadWebSocketProtocol - } - } - buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") - buf.WriteString("Upgrade: websocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // TODO(ukai): send Sec-WebSocket-Extensions. - if c.Header != nil { - err := c.Header.WriteSubset(buf, handshakeHeader) - if err != nil { - return err - } - } - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiServerConn(c.Config, buf, rwc, request) -} - -// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. -func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiConn(config, buf, rwc, request) -} diff --git a/vendor/golang.org/x/net/websocket/server.go b/vendor/golang.org/x/net/websocket/server.go deleted file mode 100644 index 0895dea..0000000 --- a/vendor/golang.org/x/net/websocket/server.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "fmt" - "io" - "net/http" -) - -func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { - var hs serverHandshaker = &hybiServerHandshaker{Config: config} - code, err := hs.ReadHandshake(buf.Reader, req) - if err == ErrBadWebSocketVersion { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - if err != nil { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - if handshake != nil { - err = handshake(config, req) - if err != nil { - code = http.StatusForbidden - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.Flush() - return - } - } - err = hs.AcceptHandshake(buf.Writer) - if err != nil { - code = http.StatusBadRequest - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.Flush() - return - } - conn = hs.NewServerConn(buf, rwc, req) - return -} - -// Server represents a server of a WebSocket. -type Server struct { - // Config is a WebSocket configuration for new WebSocket connection. - Config - - // Handshake is an optional function in WebSocket handshake. - // For example, you can check, or don't check Origin header. - // Another example, you can select config.Protocol. - Handshake func(*Config, *http.Request) error - - // Handler handles a WebSocket connection. - Handler -} - -// ServeHTTP implements the http.Handler interface for a WebSocket -func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { - s.serveWebSocket(w, req) -} - -func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { - rwc, buf, err := w.(http.Hijacker).Hijack() - if err != nil { - panic("Hijack failed: " + err.Error()) - } - // The server should abort the WebSocket connection if it finds - // the client did not send a handshake that matches with protocol - // specification. - defer rwc.Close() - conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) - if err != nil { - return - } - if conn == nil { - panic("unexpected nil conn") - } - s.Handler(conn) -} - -// Handler is a simple interface to a WebSocket browser client. -// It checks if Origin header is valid URL by default. -// You might want to verify websocket.Conn.Config().Origin in the func. -// If you use Server instead of Handler, you could call websocket.Origin and -// check the origin in your Handshake func. So, if you want to accept -// non-browser clients, which do not send an Origin header, set a -// Server.Handshake that does not check the origin. -type Handler func(*Conn) - -func checkOrigin(config *Config, req *http.Request) (err error) { - config.Origin, err = Origin(config, req) - if err == nil && config.Origin == nil { - return fmt.Errorf("null origin") - } - return err -} - -// ServeHTTP implements the http.Handler interface for a WebSocket -func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - s := Server{Handler: h, Handshake: checkOrigin} - s.serveWebSocket(w, req) -} diff --git a/vendor/golang.org/x/net/websocket/websocket.go b/vendor/golang.org/x/net/websocket/websocket.go deleted file mode 100644 index 1f4f7be..0000000 --- a/vendor/golang.org/x/net/websocket/websocket.go +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package websocket implements a client and server for the WebSocket protocol -// as specified in RFC 6455. -// -// This package currently lacks some features found in an alternative -// and more actively maintained WebSocket package: -// -// https://godoc.org/github.com/gorilla/websocket -// -package websocket // import "golang.org/x/net/websocket" - -import ( - "bufio" - "crypto/tls" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "sync" - "time" -) - -const ( - ProtocolVersionHybi13 = 13 - ProtocolVersionHybi = ProtocolVersionHybi13 - SupportedProtocolVersion = "13" - - ContinuationFrame = 0 - TextFrame = 1 - BinaryFrame = 2 - CloseFrame = 8 - PingFrame = 9 - PongFrame = 10 - UnknownFrame = 255 - - DefaultMaxPayloadBytes = 32 << 20 // 32MB -) - -// ProtocolError represents WebSocket protocol errors. -type ProtocolError struct { - ErrorString string -} - -func (err *ProtocolError) Error() string { return err.ErrorString } - -var ( - ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} - ErrBadScheme = &ProtocolError{"bad scheme"} - ErrBadStatus = &ProtocolError{"bad status"} - ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} - ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} - ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} - ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} - ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} - ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} - ErrBadFrame = &ProtocolError{"bad frame"} - ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} - ErrNotWebSocket = &ProtocolError{"not websocket protocol"} - ErrBadRequestMethod = &ProtocolError{"bad method"} - ErrNotSupported = &ProtocolError{"not supported"} -) - -// ErrFrameTooLarge is returned by Codec's Receive method if payload size -// exceeds limit set by Conn.MaxPayloadBytes -var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit") - -// Addr is an implementation of net.Addr for WebSocket. -type Addr struct { - *url.URL -} - -// Network returns the network type for a WebSocket, "websocket". -func (addr *Addr) Network() string { return "websocket" } - -// Config is a WebSocket configuration -type Config struct { - // A WebSocket server address. - Location *url.URL - - // A Websocket client origin. - Origin *url.URL - - // WebSocket subprotocols. - Protocol []string - - // WebSocket protocol version. - Version int - - // TLS config for secure WebSocket (wss). - TlsConfig *tls.Config - - // Additional header fields to be sent in WebSocket opening handshake. - Header http.Header - - // Dialer used when opening websocket connections. - Dialer *net.Dialer - - handshakeData map[string]string -} - -// serverHandshaker is an interface to handle WebSocket server side handshake. -type serverHandshaker interface { - // ReadHandshake reads handshake request message from client. - // Returns http response code and error if any. - ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) - - // AcceptHandshake accepts the client handshake request and sends - // handshake response back to client. - AcceptHandshake(buf *bufio.Writer) (err error) - - // NewServerConn creates a new WebSocket connection. - NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) -} - -// frameReader is an interface to read a WebSocket frame. -type frameReader interface { - // Reader is to read payload of the frame. - io.Reader - - // PayloadType returns payload type. - PayloadType() byte - - // HeaderReader returns a reader to read header of the frame. - HeaderReader() io.Reader - - // TrailerReader returns a reader to read trailer of the frame. - // If it returns nil, there is no trailer in the frame. - TrailerReader() io.Reader - - // Len returns total length of the frame, including header and trailer. - Len() int -} - -// frameReaderFactory is an interface to creates new frame reader. -type frameReaderFactory interface { - NewFrameReader() (r frameReader, err error) -} - -// frameWriter is an interface to write a WebSocket frame. -type frameWriter interface { - // Writer is to write payload of the frame. - io.WriteCloser -} - -// frameWriterFactory is an interface to create new frame writer. -type frameWriterFactory interface { - NewFrameWriter(payloadType byte) (w frameWriter, err error) -} - -type frameHandler interface { - HandleFrame(frame frameReader) (r frameReader, err error) - WriteClose(status int) (err error) -} - -// Conn represents a WebSocket connection. -// -// Multiple goroutines may invoke methods on a Conn simultaneously. -type Conn struct { - config *Config - request *http.Request - - buf *bufio.ReadWriter - rwc io.ReadWriteCloser - - rio sync.Mutex - frameReaderFactory - frameReader - - wio sync.Mutex - frameWriterFactory - - frameHandler - PayloadType byte - defaultCloseStatus int - - // MaxPayloadBytes limits the size of frame payload received over Conn - // by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used. - MaxPayloadBytes int -} - -// Read implements the io.Reader interface: -// it reads data of a frame from the WebSocket connection. -// if msg is not large enough for the frame data, it fills the msg and next Read -// will read the rest of the frame data. -// it reads Text frame or Binary frame. -func (ws *Conn) Read(msg []byte) (n int, err error) { - ws.rio.Lock() - defer ws.rio.Unlock() -again: - if ws.frameReader == nil { - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return 0, err - } - ws.frameReader, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return 0, err - } - if ws.frameReader == nil { - goto again - } - } - n, err = ws.frameReader.Read(msg) - if err == io.EOF { - if trailer := ws.frameReader.TrailerReader(); trailer != nil { - io.Copy(ioutil.Discard, trailer) - } - ws.frameReader = nil - goto again - } - return n, err -} - -// Write implements the io.Writer interface: -// it writes data as a frame to the WebSocket connection. -func (ws *Conn) Write(msg []byte) (n int, err error) { - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - return n, err -} - -// Close implements the io.Closer interface. -func (ws *Conn) Close() error { - err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) - err1 := ws.rwc.Close() - if err != nil { - return err - } - return err1 -} - -// IsClientConn reports whether ws is a client-side connection. -func (ws *Conn) IsClientConn() bool { return ws.request == nil } - -// IsServerConn reports whether ws is a server-side connection. -func (ws *Conn) IsServerConn() bool { return ws.request != nil } - -// LocalAddr returns the WebSocket Origin for the connection for client, or -// the WebSocket location for server. -func (ws *Conn) LocalAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Origin} - } - return &Addr{ws.config.Location} -} - -// RemoteAddr returns the WebSocket location for the connection for client, or -// the Websocket Origin for server. -func (ws *Conn) RemoteAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Location} - } - return &Addr{ws.config.Origin} -} - -var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") - -// SetDeadline sets the connection's network read & write deadlines. -func (ws *Conn) SetDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetDeadline(t) - } - return errSetDeadline -} - -// SetReadDeadline sets the connection's network read deadline. -func (ws *Conn) SetReadDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetReadDeadline(t) - } - return errSetDeadline -} - -// SetWriteDeadline sets the connection's network write deadline. -func (ws *Conn) SetWriteDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetWriteDeadline(t) - } - return errSetDeadline -} - -// Config returns the WebSocket config. -func (ws *Conn) Config() *Config { return ws.config } - -// Request returns the http request upgraded to the WebSocket. -// It is nil for client side. -func (ws *Conn) Request() *http.Request { return ws.request } - -// Codec represents a symmetric pair of functions that implement a codec. -type Codec struct { - Marshal func(v interface{}) (data []byte, payloadType byte, err error) - Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) -} - -// Send sends v marshaled by cd.Marshal as single frame to ws. -func (cd Codec) Send(ws *Conn, v interface{}) (err error) { - data, payloadType, err := cd.Marshal(v) - if err != nil { - return err - } - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) - if err != nil { - return err - } - _, err = w.Write(data) - w.Close() - return err -} - -// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores -// in v. The whole frame payload is read to an in-memory buffer; max size of -// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds -// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire -// completely. The next call to Receive would read and discard leftover data of -// previous oversized frame before processing next frame. -func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { - ws.rio.Lock() - defer ws.rio.Unlock() - if ws.frameReader != nil { - _, err = io.Copy(ioutil.Discard, ws.frameReader) - if err != nil { - return err - } - ws.frameReader = nil - } -again: - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return err - } - frame, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return err - } - if frame == nil { - goto again - } - maxPayloadBytes := ws.MaxPayloadBytes - if maxPayloadBytes == 0 { - maxPayloadBytes = DefaultMaxPayloadBytes - } - if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) { - // payload size exceeds limit, no need to call Unmarshal - // - // set frameReader to current oversized frame so that - // the next call to this function can drain leftover - // data before processing the next frame - ws.frameReader = frame - return ErrFrameTooLarge - } - payloadType := frame.PayloadType() - data, err := ioutil.ReadAll(frame) - if err != nil { - return err - } - return cd.Unmarshal(data, payloadType, v) -} - -func marshal(v interface{}) (msg []byte, payloadType byte, err error) { - switch data := v.(type) { - case string: - return []byte(data), TextFrame, nil - case []byte: - return data, BinaryFrame, nil - } - return nil, UnknownFrame, ErrNotSupported -} - -func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - switch data := v.(type) { - case *string: - *data = string(msg) - return nil - case *[]byte: - *data = msg - return nil - } - return ErrNotSupported -} - -/* -Message is a codec to send/receive text/binary data in a frame on WebSocket connection. -To send/receive text frame, use string type. -To send/receive binary frame, use []byte type. - -Trivial usage: - - import "websocket" - - // receive text frame - var message string - websocket.Message.Receive(ws, &message) - - // send text frame - message = "hello" - websocket.Message.Send(ws, message) - - // receive binary frame - var data []byte - websocket.Message.Receive(ws, &data) - - // send binary frame - data = []byte{0, 1, 2} - websocket.Message.Send(ws, data) - -*/ -var Message = Codec{marshal, unmarshal} - -func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { - msg, err = json.Marshal(v) - return msg, TextFrame, err -} - -func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - return json.Unmarshal(msg, v) -} - -/* -JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. - -Trivial usage: - - import "websocket" - - type T struct { - Msg string - Count int - } - - // receive JSON type T - var data T - websocket.JSON.Receive(ws, &data) - - // send JSON type T - websocket.JSON.Send(ws, data) -*/ -var JSON = Codec{jsonMarshal, jsonUnmarshal} diff --git a/vendor/vendor.json b/vendor/vendor.json deleted file mode 100644 index 54a8a83..0000000 --- a/vendor/vendor.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comment": "", - "ignore": "test", - "package": [ - { - "checksumSHA1": "F+tqxPGFt5x7DKZakbbMmENX1oQ=", - "path": "golang.org/x/net/websocket", - "revision": "ca1201d0de80cfde86cb01aea620983605dfe99b", - "revisionTime": "2019-07-23T18:48:14Z", - "tree": true - } - ], - "rootPath": "go-stress-testing" -} From 1f530a7f92e367ed53d71cc95c66d1a0f5b6fe77 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 4 Jul 2020 11:11:20 +0800 Subject: [PATCH 43/75] fix --- .gitignore | 3 ++- server/client/http_client.go | 5 ++++- server/golink/http_link.go | 9 +++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index ca38fc5..2f4c6ac 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ go-stress-testing-mac go-stress-testing-linux go-stress-testing-win.exe model/modeltest/* -model/modeltest* \ No newline at end of file +model/modeltest* +server.go \ No newline at end of file diff --git a/server/client/http_client.go b/server/client/http_client.go index 48f5c18..de4f3e8 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -10,6 +10,7 @@ package client import ( "crypto/tls" "fmt" + "go-stress-testing/heper" "io" "net/http" "time" @@ -21,7 +22,7 @@ import ( // body 请求的body // headers 请求头信息 // timeout 请求超时时间 -func HttpRequest(method, url string, body io.Reader, headers map[string]string, timeout time.Duration) (resp *http.Response, 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{ @@ -51,7 +52,9 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, req.Header.Set(key, value) } + startTime := time.Now() resp, err = client.Do(req) + requestTime = uint64(heper.DiffNano(startTime)) if err != nil { fmt.Println("请求失败:", err) diff --git a/server/golink/http_link.go b/server/golink/http_link.go index 4b7f223..260c50b 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -8,11 +8,9 @@ package golink import ( - "go-stress-testing/heper" "go-stress-testing/model" "go-stress-testing/server/client" "sync" - "time" ) // http go link @@ -26,14 +24,13 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg for i := uint64(0); i < totalNumber; i++ { var ( - startTime = time.Now() + // startTime = time.Now() isSucceed = false errCode = model.HttpOk ) - resp, err := client.HttpRequest(request.Method, request.Url, request.GetBody(), request.Headers, request.Timeout) - requestTime := uint64(heper.DiffNano(startTime)) - // resp, err := server.HttpGetResp(request.Url) + resp, requestTime, err := client.HttpRequest(request.Method, request.Url, request.GetBody(), request.Headers, request.Timeout) + // requestTime := uint64(heper.DiffNano(startTime)) if err != nil { errCode = model.RequestErr // 请求错误 } else { From 3525e721558ed8e16387ef7d48ab40988155cb1c Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 18 Jul 2020 10:18:03 +0800 Subject: [PATCH 44/75] =?UTF-8?q?=E6=96=B0=E7=89=88postman=20curl=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/curl_model.go | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/model/curl_model.go b/model/curl_model.go index 8a8bc12..320aaff 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -121,10 +121,15 @@ func ParseTheFile(path string) (curl *CURL, err error) { index = strings.Index(data, endSymbol) if index <= -1 { - break + index = len(data) + // break } value = data[:index] - data = data[index+1:] + if len(data) >= index+1 { + data = data[index+1:] + } else { + data = "" + } // 去除首尾空格 data = strings.TrimFunc(data, func(r rune) bool { @@ -135,12 +140,17 @@ func ParseTheFile(path string) (curl *CURL, err error) { 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) // } @@ -157,7 +167,7 @@ func (c *CURL) String() (url string) { // GetUrl func (c *CURL) GetUrl() (url string) { - keys := []string{"curl","--url"} + keys := []string{"curl", "--url"} value := c.getDataValue(keys) if len(value) <= 0 { @@ -173,17 +183,14 @@ func (c *CURL) GetUrl() (url string) { func (c *CURL) GetMethod() (method string) { method = "GET" - var ( - postKeys = []string{"--d", "--data", "--data-binary $", "--data-binary"} - ) - value := c.getDataValue(postKeys) + body := c.GetBody() - if len(value) >= 1 { + if len(body) > 0 { return "POST" } keys := []string{"-X", "--request"} - value = c.getDataValue(keys) + value := c.getDataValue(keys) if len(value) <= 0 { @@ -220,10 +227,11 @@ func (c *CURL) GetHeadersStr() string { // 获取body func (c *CURL) GetBody() (body string) { - keys := []string{"--data", "-d", "--data-raw", "--data-binary"} + keys := []string{"--data", "-d", "--data-urlencode", "--data-raw", "--data-binary"} value := c.getDataValue(keys) if len(value) <= 0 { + body = c.getPostForm() return } @@ -233,3 +241,17 @@ func (c *CURL) GetBody() (body string) { return } + +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 +} From 760552c7c1e951134cf65b0d6d78274365edbfa1 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 1 Aug 2020 09:43:21 +0800 Subject: [PATCH 45/75] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8A=A0=E6=9D=83=E5=8E=8B=E6=B5=8B=E5=92=8C=E5=88=86=E6=AD=A5?= =?UTF-8?q?=E5=8E=8B=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/request_model.go | 40 +++++++---- server/golink/http_link.go | 59 ++++++++++++----- server/golink/http_link_many.go | 82 +++++++++++++++++++++++ server/golink/http_link_weigh.go | 110 +++++++++++++++++++++++++++++++ server/golink/websocket_link.go | 7 +- tests/servers.go | 39 +++++++++++ 6 files changed, 308 insertions(+), 29 deletions(-) create mode 100644 server/golink/http_link_many.go create mode 100644 server/golink/http_link_weigh.go create mode 100644 tests/servers.go diff --git a/model/request_model.go b/model/request_model.go index e9b6fa6..e4c5ce9 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -68,12 +68,10 @@ type VerifyWebSocket func(request *Request, seq string, msg []byte) (code int, i type Request struct { Url string // Url Form string // http/webSocket/tcp - Method string // 方法 get/post/put + Method string // 方法 GET/POST/PUT Headers map[string]string // Headers Body string // body Verify string // 验证的方法 - VerifyHttp VerifyHttp // 验证的方法 - VerifyWebSocket VerifyWebSocket // 验证的方法 Timeout time.Duration // 请求超时时间 Debug bool // 是否开启Debug模式 @@ -87,6 +85,31 @@ func (r *Request) GetBody() (body io.Reader) { return } +func (r *Request) getVerifyKey() (key string) { + key = fmt.Sprintf("%s.%s", r.Form, r.Verify) + + return +} + +// 获取数据校验方法 +func (r *Request) GetVerifyHttp() VerifyHttp { + verify, ok := verifyMapHttp[r.getVerifyKey()] + if !ok { + panic("GetVerifyHttp 验证方法不存在:" + r.Verify) + } + + return verify +} + +func (r *Request) GetVerifyWebSocket() VerifyWebSocket { + verify, ok := verifyMapWebSocket[r.getVerifyKey()] + if !ok { + panic("GetVerifyWebSocket 验证方法不存在:" + r.Verify) + } + + return verify +} + // NewRequest // url 压测的url // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json @@ -146,8 +169,6 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } var ( - verifyHttp VerifyHttp - verifyWebSocket VerifyWebSocket ok bool ) @@ -159,7 +180,7 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } key := fmt.Sprintf("%s.%s", form, verify) - verifyHttp, ok = verifyMapHttp[key] + _, ok = verifyMapHttp[key] if !ok { err = errors.New("验证器不存在:" + key) @@ -172,7 +193,7 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } key := fmt.Sprintf("%s.%s", form, verify) - verifyWebSocket, ok = verifyMapWebSocket[key] + _, ok = verifyMapWebSocket[key] if !ok { err = errors.New("验证器不存在:" + key) @@ -192,8 +213,6 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa Headers: headers, Body: body, Verify: verify, - VerifyHttp: verifyHttp, - VerifyWebSocket: verifyWebSocket, Timeout: timeout, Debug: debug, } @@ -247,13 +266,12 @@ func (r *Request) IsParameterLegal() (err error) { r.Verify = "json" key := fmt.Sprintf("%s.%s", r.Form, r.Verify) - value, ok := verifyMapHttp[key] + _, ok := verifyMapHttp[key] if !ok { return errors.New("验证器不存在:" + key) } - r.VerifyHttp = value return } diff --git a/server/golink/http_link.go b/server/golink/http_link.go index 260c50b..d922576 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -8,9 +8,10 @@ package golink import ( + "sync" + "go-stress-testing/model" "go-stress-testing/server/client" - "sync" ) // http go link @@ -23,20 +24,9 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg // fmt.Printf("启动协程 编号:%05d \n", chanId) for i := uint64(0); i < totalNumber; i++ { - var ( - // startTime = time.Now() - isSucceed = false - errCode = model.HttpOk - ) - - resp, requestTime, err := client.HttpRequest(request.Method, request.Url, request.GetBody(), request.Headers, request.Timeout) - // requestTime := uint64(heper.DiffNano(startTime)) - if err != nil { - errCode = model.RequestErr // 请求错误 - } else { - // 验证请求是否成功 - errCode, isSucceed = request.VerifyHttp(request, resp) - } + list := getRequestList(request) + + isSucceed, errCode, requestTime := sendList(list) requestResults := &model.RequestResults{ Time: requestTime, @@ -51,3 +41,42 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg return } +// 多个接口分步压测 +func sendList(requestList []*model.Request) (isSucceed bool, errCode int, requestTime uint64) { + + errCode = model.HttpOk + for _, request := range requestList { + succeed, code, u := send(request) + isSucceed = succeed + errCode = code + requestTime = requestTime + u + if succeed == false { + + break + } + } + + return +} + +// send 发送一次请求 +func send(request *model.Request) (bool, int, uint64) { + var ( + // startTime = time.Now() + isSucceed = false + errCode = model.HttpOk + ) + + 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)) + if err != nil { + errCode = model.RequestErr // 请求错误 + } else { + // 验证请求是否成功 + errCode, isSucceed = newRequest.GetVerifyHttp()(newRequest, resp) + } + return isSucceed, errCode, requestTime +} diff --git a/server/golink/http_link_many.go b/server/golink/http_link_many.go new file mode 100644 index 0000000..427985d --- /dev/null +++ b/server/golink/http_link_many.go @@ -0,0 +1,82 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2020/7/31 +* Time: 8:36 下午 + */ + +package golink + +import ( + "time" + + "go-stress-testing/model" +) + +// 接口分步压测 +type ReqListMany struct { + list []*model.Request +} + +func (r *ReqListMany) getCount() int { + return len(r.list) +} + +var ( + clientList *ReqListMany +) + +// 接口分步压测示例 +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 +} + +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 new file mode 100644 index 0000000..8c59fcf --- /dev/null +++ b/server/golink/http_link_weigh.go @@ -0,0 +1,110 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2020/7/31 +* Time: 8:36 下午 + */ + +package golink + +import ( + "math/rand" + "time" + + "go-stress-testing/model" +) + +// 接口加权压测 +type ReqListWeigh struct { + list []Req + weighCount uint32 // 总权重 +} + +type Req struct { + req *model.Request // 请求信息 + weights uint32 // 权重,数字越大访问频率越高 +} + +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() +} + +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") + + return nil +} diff --git a/server/golink/websocket_link.go b/server/golink/websocket_link.go index 0a44cfb..5b58c7c 100644 --- a/server/golink/websocket_link.go +++ b/server/golink/websocket_link.go @@ -9,11 +9,12 @@ package golink import ( "fmt" + "sync" + "time" + "go-stress-testing/heper" "go-stress-testing/model" "go-stress-testing/server/client" - "sync" - "time" ) const ( @@ -100,7 +101,7 @@ func webSocketRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, fmt.Println("读取数据 失败~") } else { // fmt.Println(msg) - errCode, isSucceed = request.VerifyWebSocket(request, seq, msg) + errCode, isSucceed = request.GetVerifyWebSocket()(request, seq, msg) } } diff --git a/tests/servers.go b/tests/servers.go new file mode 100644 index 0000000..61578c9 --- /dev/null +++ b/tests/servers.go @@ -0,0 +1,39 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2020/8/1 +* Time: 09:27 + */ + +package main + +import ( + "log" + "net/http" + "runtime" +) + +const ( + httpPort = "8088" +) + +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)) + + return + } + + http.HandleFunc("/", hello) + err := http.ListenAndServe(":"+httpPort, nil) + + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} From ab0ca6d0b53fc572fde4800bb2867d14799e16d2 Mon Sep 17 00:00:00 2001 From: lizhuo200701 Date: Fri, 14 Aug 2020 10:42:26 +0800 Subject: [PATCH 46/75] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dheader=20=E4=B8=AD?= =?UTF-8?q?=E8=AE=BE=E7=BD=AEHost=20=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/client/http_client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/client/http_client.go b/server/client/http_client.go index de4f3e8..afa0e7b 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -39,7 +39,10 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, 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 { From 3f10a27eb941d6a8ed2596570ed0b6c4ca9f7a87 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 12 Sep 2020 19:30:26 +0800 Subject: [PATCH 47/75] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heper/heper.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/heper/heper.go b/heper/heper.go index fe4f626..19d30dd 100644 --- a/heper/heper.go +++ b/heper/heper.go @@ -13,11 +13,6 @@ import ( // 时间差,纳秒 func DiffNano(startTime time.Time) (diff int64) { - - startTimeStamp := startTime.UnixNano() - endTimeStamp := time.Now().UnixNano() - - diff = endTimeStamp - startTimeStamp - + diff = int64(time.Since(startTime)) return } From 0c63b11507e34d331ac9c4d8f90e6d801d8e562c Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sun, 13 Sep 2020 11:40:01 +0800 Subject: [PATCH 48/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de4c22e..376c1ab 100644 --- a/README.md +++ b/README.md @@ -611,6 +611,7 @@ func main() { - 压测结果 +- [压测结果 示例](https://github.com/link1st/go-stress-testing/issues/32) | 并发数 | go_stress_testing QPS | | :----: | :----: | @@ -622,7 +623,6 @@ func main() { | 50 | 19922.56 | | 80 | 19155.33 | | 100 | 18336.46 | -| 200 | 16813.86 | 从压测的结果上看:效果还不错,压测QPS有接近2W From 486201f27cc61f3b11d1dc6558d7c6d7e33c9881 Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Mon, 28 Sep 2020 14:32:12 +0800 Subject: [PATCH 49/75] Create statistics_test.go --- server/statistics/statistics_test.go | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 server/statistics/statistics_test.go diff --git a/server/statistics/statistics_test.go b/server/statistics/statistics_test.go new file mode 100644 index 0000000..6a60833 --- /dev/null +++ b/server/statistics/statistics_test.go @@ -0,0 +1,36 @@ +/** +* Package statistics +* +* User: link1st +* Date: 2020/9/28 +* Time: 14:02 + */ + +package statistics + +import ( + "reflect" + "testing" +) + +// TestPrintMap +func TestPrintMap(t *testing.T) { + + tt := map[string]struct { + a map[int]int + result string + }{ + "test1": {a: map[int]int{ + 200: 50, + 500: 10, + 100: 20, + }, result: "100:20;200:50;500:10"}, + } + + for _, value := range tt { + str := printMap(value.a) + if !reflect.DeepEqual(value.result, str) { + t.Errorf("数据不一致 预期:%v 实际:%v", value.result, str) + } + } +} From 1b7e9ea0a9eef69bfc3fb554a015f6037be1c62c Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Mon, 28 Sep 2020 14:32:41 +0800 Subject: [PATCH 50/75] add sort --- server/statistics/statistics.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index efaaf3a..5a89554 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -9,10 +9,12 @@ package statistics import ( "fmt" - "go-stress-testing/model" + "sort" "strings" "sync" "time" + + "go-stress-testing/model" ) var ( @@ -192,6 +194,8 @@ func printMap(errCode map[int]int) (mapStr string) { mapArr = append(mapArr, fmt.Sprintf("%d:%d", key, value)) } + sort.Strings(mapArr) + mapStr = strings.Join(mapArr, ";") return From ded88511e90f07e24832087baf38cb1d88198005 Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Wed, 21 Oct 2020 20:38:13 +0800 Subject: [PATCH 51/75] =?UTF-8?q?fix=20=E5=B9=B3=E5=9D=87=E8=80=97?= =?UTF-8?q?=E6=97=B6=E7=94=B1=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=B9=B3=E5=9D=87?= =?UTF-8?q?=E8=80=97=E6=97=B6=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E5=B9=B3=E5=9D=87=E8=AF=B7=E6=B1=82=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/statistics/statistics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 5a89554..e86d765 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -147,7 +147,7 @@ func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, su // 平均时长 总耗时/总请求数/并发数 纳秒=>毫秒 if successNum != 0 && concurrent != 0 { - averageTime = float64(processingTime) / float64(successNum*1e6*concurrent) + averageTime = float64(processingTime) / float64(successNum*1e6) } // 纳秒=>毫秒 From 0beab0abacb22b35a941eda84deb2999de73f844 Mon Sep 17 00:00:00 2001 From: wuhelong Date: Fri, 30 Oct 2020 10:09:25 +0800 Subject: [PATCH 52/75] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=BC=A0=E8=BE=93=E5=B9=B6=E8=AE=B0=E5=BD=95=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 5 +++- go.sum | 1 + model/request_model.go | 46 ++++++++++++++++----------------- server/client/http_client.go | 7 +++-- server/golink/http_link.go | 36 +++++++++++++++++++------- server/statistics/statistics.go | 29 ++++++++++++++------- 6 files changed, 79 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index d9021ef..4a8c992 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module go-stress-testing go 1.14 -require golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 +require ( + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 + golang.org/x/text v0.3.0 +) diff --git a/go.sum b/go.sum index c037375..bc41edb 100644 --- a/go.sum +++ b/go.sum @@ -2,4 +2,5 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/model/request_model.go b/model/request_model.go index e4c5ce9..7b2a197 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -66,14 +66,14 @@ type VerifyWebSocket func(request *Request, seq string, msg []byte) (code int, i // 请求结果 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模式 + 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模式 // 连接以后初始化事件 // 循环事件 切片 时间 动作 @@ -169,7 +169,7 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } var ( - ok bool + ok bool ) switch form { @@ -207,14 +207,14 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa } request = &Request{ - Url: url, - Form: form, - Method: strings.ToUpper(method), - Headers: headers, - Body: body, - Verify: verify, - Timeout: timeout, - Debug: debug, + Url: url, + Form: form, + Method: strings.ToUpper(method), + Headers: headers, + Body: body, + Verify: verify, + Timeout: timeout, + Debug: debug, } return @@ -272,17 +272,17 @@ func (r *Request) IsParameterLegal() (err error) { return errors.New("验证器不存在:" + key) } - return } // 请求结果 type RequestResults struct { - Id string // 消息Id - ChanId uint64 // 消息Id - Time uint64 // 请求时间 纳秒 - IsSucceed bool // 是否请求成功 - ErrCode int // 错误码 + Id string // 消息Id + ChanId uint64 // 消息Id + Time uint64 // 请求时间 纳秒 + IsSucceed bool // 是否请求成功 + ErrCode int // 错误码 + ReceivedBytes int64 } func (r *RequestResults) SetId(chanId uint64, number uint64) { diff --git a/server/client/http_client.go b/server/client/http_client.go index afa0e7b..75445fd 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -9,13 +9,16 @@ package client import ( "crypto/tls" - "fmt" "go-stress-testing/heper" "io" + "log" "net/http" + "os" "time" ) +var logErr = log.New(os.Stderr, "", 0) + // HTTP 请求 // method 方法 GET POST // url 请求的url @@ -59,7 +62,7 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, resp, err = client.Do(req) requestTime = uint64(heper.DiffNano(startTime)) if err != nil { - fmt.Println("请求失败:", err) + logErr.Println("请求失败:", err) return } diff --git a/server/golink/http_link.go b/server/golink/http_link.go index d922576..d9eb8fd 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -14,6 +14,8 @@ import ( "go-stress-testing/server/client" ) +var buf = make([]byte, 1024*1024) + // http go link func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request) { @@ -26,12 +28,13 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg list := getRequestList(request) - isSucceed, errCode, requestTime := sendList(list) + isSucceed, errCode, requestTime, contentLength := sendList(list) requestResults := &model.RequestResults{ - Time: requestTime, - IsSucceed: isSucceed, - ErrCode: errCode, + Time: requestTime, + IsSucceed: isSucceed, + ErrCode: errCode, + ReceivedBytes: contentLength, } requestResults.SetId(chanId, i) @@ -41,15 +44,17 @@ func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg return } + // 多个接口分步压测 -func sendList(requestList []*model.Request) (isSucceed bool, errCode int, requestTime uint64) { +func sendList(requestList []*model.Request) (isSucceed bool, errCode int, requestTime uint64, contentLength int64) { errCode = model.HttpOk for _, request := range requestList { - succeed, code, u := send(request) + succeed, code, u, length := send(request) isSucceed = succeed errCode = code requestTime = requestTime + u + contentLength = contentLength + length if succeed == false { break @@ -60,11 +65,12 @@ func sendList(requestList []*model.Request) (isSucceed bool, errCode int, reques } // send 发送一次请求 -func send(request *model.Request) (bool, int, uint64) { +func send(request *model.Request) (bool, int, uint64, int64) { var ( // startTime = time.Now() - isSucceed = false - errCode = model.HttpOk + isSucceed = false + errCode = model.HttpOk + contentLength = int64(0) ) newRequest := getRequest(request) @@ -75,8 +81,18 @@ func send(request *model.Request) (bool, int, uint64) { if err != nil { errCode = model.RequestErr // 请求错误 } else { + + contentLength = 0 + for { + n, err := resp.Body.Read(buf) + contentLength += int64(n) + if err != nil { + break + } + } + // 验证请求是否成功 errCode, isSucceed = newRequest.GetVerifyHttp()(newRequest, resp) } - return isSucceed, errCode, requestTime + return isSucceed, errCode, requestTime, contentLength } diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index e86d765..ac9dca4 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -15,11 +15,15 @@ import ( "time" "go-stress-testing/model" + + "golang.org/x/text/language" + "golang.org/x/text/message" ) var ( // 输出统计数据的时间 exportStatisticsTime = 1 * time.Second + p = message.NewPrinter(language.English) ) // 接收结果并处理 @@ -45,6 +49,7 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy failureNum uint64 // 处理失败数,code不为0 chanIdLen int // 并发数 chanIds = make(map[uint64]bool) + receivedBytes int64 ) statTime := uint64(time.Now().UnixNano()) @@ -60,7 +65,7 @@ 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) + go calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIdLen, errCode, receivedBytes) case <-stopChan: // 处理完成 @@ -99,6 +104,8 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy errCode[data.ErrCode] = 1 } + receivedBytes += data.ReceivedBytes + if _, ok := chanIds[data.ChanId]; !ok { chanIds[data.ChanId] = true chanIdLen = len(chanIds) @@ -111,7 +118,7 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy endTime := uint64(time.Now().UnixNano()) requestTime = endTime - statTime - calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIdLen, errCode) + calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum, chanIdLen, errCode, receivedBytes) fmt.Printf("\n\n") @@ -127,7 +134,7 @@ func ReceivingResults(concurrent uint64, ch <-chan *model.RequestResults, wg *sy } // 计算数据 -func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64, chanIdLen int, errCode map[int]int) { +func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64, chanIdLen int, errCode map[int]int, receivedBytes int64) { if processingTime == 0 { processingTime = 1 } @@ -158,27 +165,31 @@ func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, su // 打印的时长都为毫秒 // 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) + table(successNum, failureNum, errCode, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat, chanIdLen, receivedBytes) } // 打印表头信息 func header() { fmt.Printf("\n\n") // 打印的时长都为毫秒 总请数 - fmt.Println("─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────") - result := fmt.Sprintf(" 耗时│ 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时│平均耗时│ 错误码") + fmt.Println("─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────") + result := fmt.Sprintf(" 耗时│ 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 错误码") fmt.Println(result) // result = fmt.Sprintf("耗时(s) │总请求数│成功数│失败数│QPS│最长耗时│最短耗时│平均耗时│错误码") // fmt.Println(result) - fmt.Println("─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────") + fmt.Println("─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────") return } // 打印表格 -func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIdLen int) { +func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIdLen int, receivedBytes int64) { // 打印的时长都为毫秒 - result := fmt.Sprintf("%4.0fs│%7d│%7d│%7d│%8.2f│%8.2f│%8.2f│%8.2f│%v", requestTimeFloat, chanIdLen, successNum, failureNum, qps, maxTimeFloat, minTimeFloat, averageTime, printMap(errCode)) + 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, + p.Sprintf("%d", receivedBytes), + p.Sprintf("%d", receivedBytes/int64(requestTimeFloat)), + printMap(errCode)) fmt.Println(result) return From 39b7f2cb7b08e1c2d7f2232e9316932f9175f9fe Mon Sep 17 00:00:00 2001 From: gongzhihong Date: Fri, 13 Nov 2020 17:57:59 +0800 Subject: [PATCH 53/75] .gitignore add vendor --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2f4c6ac..0ff29f2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ go-stress-testing-linux go-stress-testing-win.exe model/modeltest/* model/modeltest* -server.go \ No newline at end of file +server.go +vendor \ No newline at end of file From 7067b6917ed9e141b52278eac456a5e827cbb1a0 Mon Sep 17 00:00:00 2001 From: gongzhihong Date: Fri, 13 Nov 2020 18:10:55 +0800 Subject: [PATCH 54/75] =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=B5=8B=E5=80=BC?= =?UTF-8?q?=E5=9C=A8resp.body=20read=E4=B9=8B=E5=90=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/golink/http_link.go | 3 +++ server/verify/http_verify.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/golink/http_link.go b/server/golink/http_link.go index d9eb8fd..4fe2c36 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -8,6 +8,8 @@ package golink import ( + "bytes" + "io/ioutil" "sync" "go-stress-testing/model" @@ -85,6 +87,7 @@ func send(request *model.Request) (bool, int, uint64, int64) { contentLength = 0 for { n, err := resp.Body.Read(buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf)) contentLength += int64(n) if err != nil { break diff --git a/server/verify/http_verify.go b/server/verify/http_verify.go index 8d29b23..d3ba561 100644 --- a/server/verify/http_verify.go +++ b/server/verify/http_verify.go @@ -8,6 +8,7 @@ package verify import ( + "bytes" "compress/gzip" "encoding/json" "fmt" @@ -29,9 +30,8 @@ func getZipData(response *http.Response) (body []byte, err error) { default: reader = response.Body } - body, err = ioutil.ReadAll(reader) - + response.Body = ioutil.NopCloser(bytes.NewReader(body)) return } From ee43255ba5689a4eaac5faad97f99f856d45c9a1 Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Fri, 20 Nov 2020 21:03:29 +0800 Subject: [PATCH 55/75] fix bug Divisor is not zero https://github.com/link1st/go-stress-testing/issues/40 --- server/statistics/statistics.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index ac9dca4..d490a74 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -184,11 +184,17 @@ func header() { // 打印表格 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 = receivedBytes / int64(requestTimeFloat) + } // 打印的时长都为毫秒 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, p.Sprintf("%d", receivedBytes), - p.Sprintf("%d", receivedBytes/int64(requestTimeFloat)), + p.Sprintf("%d", speed), printMap(errCode)) fmt.Println(result) From 478ffc6cedf9ae038d9dbeac6f13c8da8509f418 Mon Sep 17 00:00:00 2001 From: sishui Date: Tue, 24 Nov 2020 12:35:15 +0800 Subject: [PATCH 56/75] fix requestTimeFloat integer divide by zero --- server/statistics/statistics.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index d490a74..977cc9d 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -184,17 +184,11 @@ func header() { // 打印表格 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 = receivedBytes / int64(requestTimeFloat) - } // 打印的时长都为毫秒 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, p.Sprintf("%d", receivedBytes), - p.Sprintf("%d", speed), + p.Sprintf("%d", int64(float64(receivedBytes)/requestTimeFloat)), printMap(errCode)) fmt.Println(result) From 83a9e8a2e9d0c5eaae885177ebdb4d853a9ca6c0 Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Tue, 24 Nov 2020 15:35:00 +0800 Subject: [PATCH 57/75] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 376c1ab..d4125f8 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,7 @@ pip3 install websocket-client 编写压测脚本 **test.py** ```python -from locust import HttpLocust, TaskSet, task +from locust import HttpUser, TaskSet, task # 定义用户行为 class UserBehavior(TaskSet): @@ -328,9 +328,8 @@ class UserBehavior(TaskSet): def baidu_index(self): self.client.get("/") - -class WebsiteUser(HttpLocust): - task_set = UserBehavior # 指向一个定义的用户行为类 +class WebsiteUser(HttpUser): + task = [UserBehavior] # 指向一个定义的用户行为类 min_wait = 3000 # 执行事务之间用户等待时间的下界(单位:毫秒) max_wait = 6000 # 执行事务之间用户等待时间的上界(单位:毫秒) ``` From 2855acc096b64868a987017e556cb5dc7ca476a0 Mon Sep 17 00:00:00 2001 From: sishui Date: Tue, 24 Nov 2020 17:00:58 +0800 Subject: [PATCH 58/75] =?UTF-8?q?=E5=85=BC=E5=AE=B9=20requestTimeFloat=20?= =?UTF-8?q?=E6=9C=89=E6=A6=82=E7=8E=87=E4=B8=BA0=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/statistics/statistics.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 977cc9d..042fc69 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -184,11 +184,20 @@ func header() { // 打印表格 func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64, chanIdLen int, receivedBytes int64) { + var ( + speed int64 + ) + requestTimeFloat = 0.0 + if requestTimeFloat > 0 { + speed = int64(float64(receivedBytes)/requestTimeFloat) + }else{ + speed = 0 + } // 打印的时长都为毫秒 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, p.Sprintf("%d", receivedBytes), - p.Sprintf("%d", int64(float64(receivedBytes)/requestTimeFloat)), + p.Sprintf("%d", speed), printMap(errCode)) fmt.Println(result) From 135e65007c9df88ba97bc4ad56926bf0aabf737b Mon Sep 17 00:00:00 2001 From: sishui Date: Tue, 24 Nov 2020 17:02:42 +0800 Subject: [PATCH 59/75] =?UTF-8?q?=E5=85=BC=E5=AE=B9=20requestTimeFloat=20?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E4=B8=BA=200=20=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/statistics/statistics.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 042fc69..36880d5 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -187,10 +187,10 @@ func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, var ( speed int64 ) - requestTimeFloat = 0.0 + if requestTimeFloat > 0 { - speed = int64(float64(receivedBytes)/requestTimeFloat) - }else{ + speed = int64(float64(receivedBytes) / requestTimeFloat) + } else { speed = 0 } // 打印的时长都为毫秒 From e29aacf0157fa267a862833937a2eb2728392ba6 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 19:10:26 +0800 Subject: [PATCH 60/75] =?UTF-8?q?fix=20bug=20=E7=BD=91=E9=A1=B5=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=20gzip=20=E5=8E=8B=E7=BC=A9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/golink/http_link.go | 15 +-------------- server/statistics/statistics.go | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/server/golink/http_link.go b/server/golink/http_link.go index 4fe2c36..b4a4407 100644 --- a/server/golink/http_link.go +++ b/server/golink/http_link.go @@ -8,16 +8,12 @@ package golink import ( - "bytes" - "io/ioutil" "sync" "go-stress-testing/model" "go-stress-testing/server/client" ) -var buf = make([]byte, 1024*1024) - // http go link func Http(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request) { @@ -83,16 +79,7 @@ func send(request *model.Request) (bool, int, uint64, int64) { if err != nil { errCode = model.RequestErr // 请求错误 } else { - - contentLength = 0 - for { - n, err := resp.Body.Read(buf) - resp.Body = ioutil.NopCloser(bytes.NewReader(buf)) - contentLength += int64(n) - if err != nil { - break - } - } + contentLength = resp.ContentLength // 验证请求是否成功 errCode, isSucceed = newRequest.GetVerifyHttp()(newRequest, resp) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 36880d5..68fd8f9 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -193,11 +193,23 @@ func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, } else { speed = 0 } + var ( + receivedBytesStr string + speedStr string + ) + // 判断获取下载字节长度是否是未知 + if receivedBytes == -1 { + receivedBytesStr = "" + speedStr = "" + } else { + 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, - p.Sprintf("%d", receivedBytes), - p.Sprintf("%d", speed), + receivedBytesStr, speedStr, printMap(errCode)) fmt.Println(result) From c2f8730e43166893ea5d30968f2e980730f6407a Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 19:16:24 +0800 Subject: [PATCH 61/75] =?UTF-8?q?fix=20bug=20=E7=BD=91=E9=A1=B5=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=20gzip=20=E5=8E=8B=E7=BC=A9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/statistics/statistics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 68fd8f9..98869d5 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -198,7 +198,7 @@ func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, speedStr string ) // 判断获取下载字节长度是否是未知 - if receivedBytes == -1 { + if receivedBytes <= 0 { receivedBytesStr = "" speedStr = "" } else { From f2d130578f67edda4f3b217117d3ccb1e5b7925e Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 23:13:11 +0800 Subject: [PATCH 62/75] add .gitignore --- .gitignore | 3 ++- test.sh | 0 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test.sh diff --git a/.gitignore b/.gitignore index 0ff29f2..7c6337f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ go-stress-testing-win.exe model/modeltest/* model/modeltest* server.go -vendor \ No newline at end of file +vendor +test.sh \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..e69de29 From 988c16745da7086904c675af88c8e7e42400c1d3 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 23:17:02 +0800 Subject: [PATCH 63/75] fix --- curl/{test.post.cur.txt => test.post.curl.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename curl/{test.post.cur.txt => test.post.curl.txt} (100%) diff --git a/curl/test.post.cur.txt b/curl/test.post.curl.txt similarity index 100% rename from curl/test.post.cur.txt rename to curl/test.post.curl.txt From 099e116e335e83101e925216255560100eb41acb Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 23:17:39 +0800 Subject: [PATCH 64/75] fix --- test.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.sh diff --git a/test.sh b/test.sh deleted file mode 100644 index e69de29..0000000 From 8e40f78d409a0e57986fec481124bca5870c6e7e Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Sat, 30 Jan 2021 23:36:40 +0800 Subject: [PATCH 65/75] fix --- server/statistics/statistics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/statistics/statistics.go b/server/statistics/statistics.go index 98869d5..390cca0 100644 --- a/server/statistics/statistics.go +++ b/server/statistics/statistics.go @@ -207,7 +207,7 @@ func table(successNum, failureNum uint64, errCode map[int]int, qps, averageTime, } // 打印的时长都为毫秒 - result := fmt.Sprintf("%4.0fs│%7d│%7d│%7d│%8.2f│%8.2f│%8.2f│%8.2f│%8s|%8s│%v", + 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, receivedBytesStr, speedStr, printMap(errCode)) From 2b4b14aaf026d08276531cf76f42de90efd3bc61 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 4 Feb 2021 00:49:13 +0800 Subject: [PATCH 66/75] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9=20grpc=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8E=8B=E6=B5=8B=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + go.sum | 77 ++++++++++++ model/request_model.go | 3 + proto/pb.pb.go | 229 +++++++++++++++++++++++++++++++++++ proto/pb.proto | 26 ++++ server/client/grpc_client.go | 66 ++++++++++ server/dispose.go | 9 ++ server/golink/grpc_link.go | 79 ++++++++++++ tests/grpc/main.go | 51 ++++++++ 9 files changed, 542 insertions(+) create mode 100644 proto/pb.pb.go create mode 100644 proto/pb.proto create mode 100644 server/client/grpc_client.go create mode 100644 server/golink/grpc_link.go create mode 100644 tests/grpc/main.go diff --git a/go.mod b/go.mod index 4a8c992..6150d8d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module go-stress-testing go 1.14 require ( + github.com/golang/protobuf v1.4.2 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 golang.org/x/text v0.3.0 + google.golang.org/grpc v1.35.0 ) diff --git a/go.sum b/go.sum index bc41edb..ed4a46a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,83 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/model/request_model.go b/model/request_model.go index 7b2a197..02aa87a 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -25,6 +25,7 @@ const ( FormTypeHttp = "http" FormTypeWebSocket = "webSocket" + FormTypeGRPC = "grpc" ) var ( @@ -157,6 +158,8 @@ func NewRequest(url string, verify string, timeout time.Duration, debug bool, pa 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 url = fmt.Sprintf("http://%s", url) diff --git a/proto/pb.pb.go b/proto/pb.pb.go new file mode 100644 index 0000000..9d25eae --- /dev/null +++ b/proto/pb.pb.go @@ -0,0 +1,229 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb.proto + +package protobuf + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// 请求 +type Request struct { + // UserName 用户昵称 + UserName string `protobuf:"bytes,1,opt,name=userName,proto3" json:"userName,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Request) Reset() { *m = Request{} } +func (m *Request) String() string { return proto.CompactTextString(m) } +func (*Request) ProtoMessage() {} +func (*Request) Descriptor() ([]byte, []int) { + return fileDescriptor_f80abaa17e25ccc8, []int{0} +} + +func (m *Request) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Request.Unmarshal(m, b) +} +func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Request.Marshal(b, m, deterministic) +} +func (m *Request) XXX_Merge(src proto.Message) { + xxx_messageInfo_Request.Merge(m, src) +} +func (m *Request) XXX_Size() int { + return xxx_messageInfo_Request.Size(m) +} +func (m *Request) XXX_DiscardUnknown() { + xxx_messageInfo_Request.DiscardUnknown(m) +} + +var xxx_messageInfo_Request proto.InternalMessageInfo + +func (m *Request) GetUserName() string { + if m != nil { + return m.UserName + } + return "" +} + +// 响应 +type Response struct { + // 状态码 + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + // 状态码说明 + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` + // Data 返回数据 + Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_f80abaa17e25ccc8, []int{1} +} + +func (m *Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Response.Unmarshal(m, b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) +} +func (m *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(m, src) +} +func (m *Response) XXX_Size() int { + return xxx_messageInfo_Response.Size(m) +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func (m *Response) GetCode() int32 { + if m != nil { + return m.Code + } + return 0 +} + +func (m *Response) GetMsg() string { + if m != nil { + return m.Msg + } + return "" +} + +func (m *Response) GetData() string { + if m != nil { + return m.Data + } + return "" +} + +func init() { + proto.RegisterType((*Request)(nil), "protobuf.Request") + proto.RegisterType((*Response)(nil), "protobuf.Response") +} + +func init() { proto.RegisterFile("pb.proto", fileDescriptor_f80abaa17e25ccc8) } + +var fileDescriptor_f80abaa17e25ccc8 = []byte{ + // 170 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x28, 0x48, 0xd2, 0x2b, + 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x00, 0x53, 0x49, 0xa5, 0x69, 0x4a, 0xaa, 0x5c, 0xec, 0x41, + 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x52, 0x5c, 0x1c, 0xa5, 0xc5, 0xa9, 0x45, 0x7e, 0x89, + 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x70, 0xbe, 0x92, 0x0b, 0x17, 0x47, 0x50, + 0x6a, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0xaa, 0x90, 0x10, 0x17, 0x4b, 0x72, 0x7e, 0x0a, 0x44, 0x0d, + 0x6b, 0x10, 0x98, 0x2d, 0x24, 0xc0, 0xc5, 0x9c, 0x5b, 0x9c, 0x2e, 0xc1, 0x04, 0xd6, 0x06, 0x62, + 0x82, 0x54, 0xa5, 0x24, 0x96, 0x24, 0x4a, 0x30, 0x83, 0x85, 0xc0, 0x6c, 0x23, 0x27, 0x2e, 0x4e, + 0xc7, 0x82, 0xcc, 0xe0, 0xd4, 0xa2, 0xb2, 0xd4, 0x22, 0x21, 0x53, 0x2e, 0x2e, 0x8f, 0xd4, 0x9c, + 0x9c, 0xfc, 0xf0, 0xfc, 0xa2, 0x9c, 0x14, 0x21, 0x41, 0x3d, 0x98, 0x93, 0xf4, 0xa0, 0xee, 0x91, + 0x12, 0x42, 0x16, 0x82, 0xd8, 0xad, 0xc4, 0x90, 0xc4, 0x06, 0x16, 0x34, 0x06, 0x04, 0x00, 0x00, + 0xff, 0xff, 0x72, 0xb6, 0x62, 0x67, 0xcd, 0x00, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ApiServerClient is the client API for ApiServer service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ApiServerClient interface { + // 接口 + HelloWorld(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) +} + +type apiServerClient struct { + cc *grpc.ClientConn +} + +func NewApiServerClient(cc *grpc.ClientConn) ApiServerClient { + return &apiServerClient{cc} +} + +func (c *apiServerClient) HelloWorld(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/protobuf.ApiServer/HelloWorld", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ApiServerServer is the server API for ApiServer service. +type ApiServerServer interface { + // 接口 + HelloWorld(context.Context, *Request) (*Response, error) +} + +// UnimplementedApiServerServer can be embedded to have forward compatible implementations. +type UnimplementedApiServerServer struct { +} + +func (*UnimplementedApiServerServer) HelloWorld(ctx context.Context, req *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method HelloWorld not implemented") +} + +func RegisterApiServerServer(s *grpc.Server, srv ApiServerServer) { + s.RegisterService(&_ApiServer_serviceDesc, srv) +} + +func _ApiServer_HelloWorld_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApiServerServer).HelloWorld(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ApiServer/HelloWorld", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApiServerServer).HelloWorld(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _ApiServer_serviceDesc = grpc.ServiceDesc{ + ServiceName: "protobuf.ApiServer", + HandlerType: (*ApiServerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "HelloWorld", + Handler: _ApiServer_HelloWorld_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pb.proto", +} diff --git a/proto/pb.proto b/proto/pb.proto new file mode 100644 index 0000000..75198d6 --- /dev/null +++ b/proto/pb.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package protobuf; + +// ApiServer api 接口 +service ApiServer { + // HelloWorld 接口 + rpc HelloWorld (Request) returns (Response) { + } +} + +// 请求 +message Request { + // UserName 用户昵称 + string userName = 1; +} + +// 响应 +message Response { + // 状态码 + int32 code = 1; + // 状态码说明 + string msg = 2; + // Data 返回数据 + string data = 3; +} \ No newline at end of file diff --git a/server/client/grpc_client.go b/server/client/grpc_client.go new file mode 100644 index 0000000..475d337 --- /dev/null +++ b/server/client/grpc_client.go @@ -0,0 +1,66 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2019-08-15 +* Time: 21:03 + */ + +package client + +import ( + "context" + "fmt" + "google.golang.org/grpc" + "strings" + "time" +) + +type GrpcSocket struct { + conn *grpc.ClientConn + address string +} + +func NewGrpcSocket(address string) (s *GrpcSocket) { + var ( + newAddr string + ) + arr := strings.Split(address, "//") + if len(arr) >= 2 { + newAddr = arr[1] + } + s = &GrpcSocket{ + address: newAddr, + } + return +} + +func (g *GrpcSocket) getAddress() (address string) { + return g.address +} + +// 关闭 +func (g *GrpcSocket) Close() (err error) { + if g == nil { + return + } + if g.conn == nil { + return + } + g.conn.Close() + return +} + +// Link 建立连接 +func (g *GrpcSocket) Link() (err error) { + ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) + conn, err := grpc.DialContext(ctx, g.address, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return fmt.Errorf("GetConn: 连接失败 address:%s %w", g.address, err) + } + g.conn = conn + return +} + +func (g *GrpcSocket) GetConn() (conn *grpc.ClientConn) { + return g.conn +} diff --git a/server/dispose.go b/server/dispose.go index ee28403..3d078a3 100644 --- a/server/dispose.go +++ b/server/dispose.go @@ -90,6 +90,15 @@ func Dispose(concurrency, totalNumber uint64, request *model.Request) { panic(data) } + case model.FormTypeGRPC: + // 连接以后再启动协程 + ws := client.NewGrpcSocket(request.Url) + err := ws.Link() + if err != nil { + fmt.Println("连接失败:", i, err) + continue + } + go golink.Grpc(i, ch, totalNumber, &wg, request, ws) default: // 类型不支持 wg.Done() diff --git a/server/golink/grpc_link.go b/server/golink/grpc_link.go new file mode 100644 index 0000000..a0de908 --- /dev/null +++ b/server/golink/grpc_link.go @@ -0,0 +1,79 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2019-08-21 +* Time: 15:43 + */ + +package golink + +import ( + "context" + pb "go-stress-testing/proto" + "sync" + "time" + + "go-stress-testing/heper" + "go-stress-testing/model" + "go-stress-testing/server/client" +) + +// Grpc grpc 接口请求 +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() + }() + for i := uint64(0); i < totalNumber; i++ { + grpcRequest(chanId, ch, i, request, ws) + } + return +} + +// 请求 +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 + ) + + // 需要发送的数据 + conn := ws.GetConn() + if conn == nil { + errCode = model.RequestErr + } else { + // TODO::请求接口示例 + c := pb.NewApiServerClient(conn) + var ( + ctx = context.Background() + req = &pb.Request{ + UserName: request.Body, + } + ) + rsp, err := c.HelloWorld(ctx, req) + // fmt.Printf("rsp:%+v", rsp) + if err != nil { + errCode = model.RequestErr + } else { + // 200 为成功 + if rsp.Code != 200 { + errCode = model.RequestErr + } else { + isSucceed = true + } + } + } + requestTime := uint64(heper.DiffNano(startTime)) + requestResults := &model.RequestResults{ + Time: requestTime, + IsSucceed: isSucceed, + ErrCode: errCode, + } + requestResults.SetId(chanId, i) + + ch <- requestResults +} diff --git a/tests/grpc/main.go b/tests/grpc/main.go new file mode 100644 index 0000000..c144fb9 --- /dev/null +++ b/tests/grpc/main.go @@ -0,0 +1,51 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2021/2/3 +* Time: 23:44 + */ + +package main + +import ( + "context" + "fmt" + pb "go-stress-testing/proto" + "google.golang.org/grpc" + "log" + "net" +) + +const ( + // port 监听端口 + port = ":8099" +) + +// server is used to implement helloWorld.GreeterServer. +type server struct { + pb.UnimplementedApiServerServer +} + +// HelloWorld hello world 接口 +func (s *server) HelloWorld(_ context.Context, req *pb.Request) (rsp *pb.Response, err error) { + rsp = &pb.Response{ + Code: 200, + Msg: "success", + Data: fmt.Sprintf("hello %s !", req.UserName), + } + return +} + +// main 主函数 +func main() { + fmt.Println("trpc server 启动中...") + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer() + pb.RegisterApiServerServer(s, &server{}) + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} From 17ab17392d318a347c4067b56d5d5ccc9adfd920 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 4 Feb 2021 01:01:16 +0800 Subject: [PATCH 67/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3=20--?= =?UTF-8?q?=20grpc=20=E6=8E=A5=E5=8F=A3=E5=8E=8B=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index d4125f8..8c8932e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ - 单台机器对 HTTP 短连接 QPS 1W+ 的压测实战 - 单台机器 100W 长连接的压测实战 +- 对 grpc 接口进行压测 +> 用户可以手动扩展支持 私有协议压测 ## 目录 - [1、项目说明](#1项目说明) @@ -31,6 +33,7 @@ - [4.2 用法](#42-用法) - [4.3 实现](#43-实现) - [4.4 go-stress-testing 对 Golang web 压测](#44-go-stress-testing-对-golang-web-压测) + - [4.5 grpc压测](#45-grpc压测) - [5、压测工具的比较](#5压测工具的比较) - [5.1 比较](#51-比较) - [5.2 如何选择压测工具](#52-如何选择压测工具) @@ -625,6 +628,28 @@ func main() { 从压测的结果上看:效果还不错,压测QPS有接近2W +### 4.5 grpc压测 +- 介绍如何压测 grpc 接口 +> [添加对 grpc 接口压测 commit](https://github.com/link1st/go-stress-testing/commit/2b4b14aaf026d08276531cf76f42de90efd3bc61) +- 1. 启动Server +```shell script +# 进入 grpc server 目录 +cd tests/grpc + +# 启动 grpc server +go run main.go +``` + +- 2. 对 grpc server 协议进行压测 +```shell script +# 回到项目根目录 +go run main.go -c 300 -n 1000 -u grpc://127.0.0.1:8099 -data world +``` + +- 如何扩展其它私有协议 +> 由于私有协议、grpc 协议 都涉及到代码的书写,所以需要 编写go 的代码才能完成 +> 参考 [添加对 grpc 接口压测 commit](https://github.com/link1st/go-stress-testing/commit/2b4b14aaf026d08276531cf76f42de90efd3bc61) + ## 5、压测工具的比较 ### 5.1 比较 From 752ea8aa476dade1d2d934aac6e4311f2bbea5ef Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 4 Feb 2021 01:05:01 +0800 Subject: [PATCH 68/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c8932e..12eda73 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - 单台机器对 HTTP 短连接 QPS 1W+ 的压测实战 - 单台机器 100W 长连接的压测实战 - 对 grpc 接口进行压测 -> 用户可以手动扩展支持 私有协议压测 +> 简单扩展即可支持 私有协议 ## 目录 - [1、项目说明](#1项目说明) From 5e2646cc6e2429072b43cd75e84d71decef2d522 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 4 Feb 2021 01:10:31 +0800 Subject: [PATCH 69/75] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 12eda73..fc302b3 100644 --- a/README.md +++ b/README.md @@ -641,9 +641,29 @@ go run main.go ``` - 2. 对 grpc server 协议进行压测 -```shell script +``` # 回到项目根目录 go run main.go -c 300 -n 1000 -u grpc://127.0.0.1:8099 -data world + +开始启动 并发数:300 请求数:1000 请求参数: +request: + form:grpc + url:grpc://127.0.0.1:8099 + method:POST + headers:map[Content-Type:application/x-www-form-urlencoded; charset=utf-8] + data:world + verify: + timeout:30s + debug:false + +─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬──────── + 耗时 │ 并发数 │ 成功数 │ 失败数 │ qps │最长耗时 │最短耗时 │平均耗时 │下载字节 │字节每秒 │ 错误码 +─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼──────── + 1s│ 186│ 14086│ 0│34177.69│ 22.40│ 0.63│ 8.78│ │ │200:14086 + 2s│ 265│ 30408│ 0│26005.09│ 32.68│ 0.63│ 11.54│ │ │200:30408 + 3s│ 300│ 46747│ 0│21890.46│ 40.84│ 0.63│ 13.70│ │ │200:46747 + 4s│ 300│ 62837│ 0│20057.06│ 45.81│ 0.63│ 14.96│ │ │200:62837 + 5s│ 300│ 79119│ 0│19134.52│ 45.81│ 0.63│ 15.68│ │ │200:79119 ``` - 如何扩展其它私有协议 From 824a493937d2c08a6a420ddd82e9ed39c1eb3c9e Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 4 Feb 2021 01:12:01 +0800 Subject: [PATCH 70/75] =?UTF-8?q?flag=20=E6=9C=80=E4=BD=B3=E5=AE=9E?= =?UTF-8?q?=E8=B7=B5=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/main.go b/main.go index 3e14d83..b7e4861 100644 --- a/main.go +++ b/main.go @@ -28,46 +28,44 @@ func (a *array) Set(s string) error { return nil } -// go 实现的压测工具 -// -// 编译可执行文件 -//go:generate go build main.go -func main() { - - runtime.GOMAXPROCS(1) - - var ( - concurrency uint64 // 并发数 - totalNumber uint64 // 请求数(单个并发/协程) - debugStr string // 是否是debug - requestUrl string // 压测的url 目前支持,http/https ws/wss - path string // curl文件路径 http接口压测,自定义参数设置 - verify string // verify 验证方法 在server/verify中 http 支持:statusCode、json webSocket支持:json - headers array // 自定义头信息传递给服务器 - body string // HTTP POST方式传送数据 - ) +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 + headers array // 自定义头信息传递给服务器 + body string // HTTP POST方式传送数据 +) - flag.Uint64Var(&concurrency, "c", 1, "并发数") - flag.Uint64Var(&totalNumber, "n", 1, "请求数(单个并发/协程)") - flag.StringVar(&debugStr, "d", "false", "调试模式") +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.Var(&headers, "H", "自定义头信息传递给服务器 示例:-H 'Content-Type: application/json'") flag.StringVar(&body, "data", "", "HTTP POST方式传送数据") - // 解析参数 flag.Parse() +} + +// go 实现的压测工具 +// 编译可执行文件 +//go:generate go build main.go +func main() { + + runtime.GOMAXPROCS(1) 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) - flag.Usage() return } - debug := strings.ToLower(debugStr) == "true" request, err := model.NewRequest(requestUrl, verify, 0, debug, path, headers, body) if err != nil { From 4b4ad0a812981fdda75ded8d22a6b1a175b11451 Mon Sep 17 00:00:00 2001 From: Link <562117637@qq.com> Date: Wed, 10 Mar 2021 20:23:39 +0800 Subject: [PATCH 71/75] Update http_client.go add `req.Close` --- server/client/http_client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/client/http_client.go b/server/client/http_client.go index 75445fd..af6e27a 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -42,6 +42,7 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, return } + req.Close = true // 在req中设置Host,解决在header中设置Host不生效问题 if _, ok := headers["Host"]; ok { req.Host = headers["Host"] From e4d4845b5268b93057528b08dcd146bd2c801011 Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 1 Apr 2021 12:40:40 +0800 Subject: [PATCH 72/75] add curl http put --- helper/helper.go | 30 ++++++++++++++++++++++++++++++ heper/heper.go | 18 ------------------ model/curl_model.go | 23 +++++++++++------------ server/client/http_client.go | 6 ++++-- server/golink/grpc_link.go | 14 +++++++++----- server/golink/websocket_link.go | 5 +++-- 6 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 helper/helper.go delete mode 100644 heper/heper.go diff --git a/helper/helper.go b/helper/helper.go new file mode 100644 index 0000000..489e0e7 --- /dev/null +++ b/helper/helper.go @@ -0,0 +1,30 @@ +/** +* Created by GoLand. +* User: link1st +* Date: 2019-08-21 +* Time: 15:40 + */ + +package helper + +import ( + "time" +) + +// DiffNano 时间差,纳秒 +func DiffNano(startTime time.Time) (diff int64) { + diff = int64(time.Since(startTime)) + return +} + +// InArrayStr 判断字符串是否在数组内 +func InArrayStr(str string, arr []string) (inArray bool) { + for _, s := range arr { + if s == str { + inArray = true + break + } + } + return +} + diff --git a/heper/heper.go b/heper/heper.go deleted file mode 100644 index 19d30dd..0000000 --- a/heper/heper.go +++ /dev/null @@ -1,18 +0,0 @@ -/** -* Created by GoLand. -* User: link1st -* Date: 2019-08-21 -* Time: 15:40 - */ - -package heper - -import ( - "time" -) - -// 时间差,纳秒 -func DiffNano(startTime time.Time) (diff int64) { - diff = int64(time.Since(startTime)) - return -} diff --git a/model/curl_model.go b/model/curl_model.go index 320aaff..e51fec8 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -13,6 +13,8 @@ import ( "io/ioutil" "os" "strings" + + "go-stress-testing/helper" ) // curl参数解析 @@ -181,24 +183,20 @@ func (c *CURL) GetUrl() (url string) { // GetMethod func (c *CURL) GetMethod() (method string) { - method = "GET" - - body := c.GetBody() - - if len(body) > 0 { - return "POST" - } - keys := []string{"-X", "--request"} value := c.getDataValue(keys) - if len(value) <= 0 { - return } - method = strings.ToUpper(value[0]) - + if helper.InArrayStr(method, []string{"GET", "POST", "PUT", "DELETE"}) { + return method + } + method = "GET" + body := c.GetBody() + if len(body) > 0 { + return "POST" + } return } @@ -255,3 +253,4 @@ func (c *CURL) getPostForm() (body string) { return } + diff --git a/server/client/http_client.go b/server/client/http_client.go index af6e27a..b0fe7bc 100644 --- a/server/client/http_client.go +++ b/server/client/http_client.go @@ -9,12 +9,13 @@ package client import ( "crypto/tls" - "go-stress-testing/heper" "io" "log" "net/http" "os" "time" + + "go-stress-testing/helper" ) var logErr = log.New(os.Stderr, "", 0) @@ -61,7 +62,7 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, startTime := time.Now() resp, err = client.Do(req) - requestTime = uint64(heper.DiffNano(startTime)) + requestTime = uint64(helper.DiffNano(startTime)) if err != nil { logErr.Println("请求失败:", err) @@ -73,3 +74,4 @@ func HttpRequest(method, url string, body io.Reader, headers map[string]string, return } + diff --git a/server/golink/grpc_link.go b/server/golink/grpc_link.go index a0de908..cd43c02 100644 --- a/server/golink/grpc_link.go +++ b/server/golink/grpc_link.go @@ -9,17 +9,19 @@ package golink import ( "context" - pb "go-stress-testing/proto" "sync" "time" - "go-stress-testing/heper" + "go-stress-testing/helper" + pb "go-stress-testing/proto" + "go-stress-testing/model" "go-stress-testing/server/client" ) // Grpc grpc 接口请求 -func Grpc(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, request *model.Request, ws *client.GrpcSocket) { +func Grpc(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg *sync.WaitGroup, + request *model.Request, ws *client.GrpcSocket) { defer func() { wg.Done() @@ -34,7 +36,8 @@ func Grpc(chanId uint64, ch chan<- *model.RequestResults, totalNumber uint64, wg } // 请求 -func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, ws *client.GrpcSocket) { +func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, request *model.Request, + ws *client.GrpcSocket) { var ( startTime = time.Now() isSucceed = false @@ -67,7 +70,7 @@ func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, reque } } } - requestTime := uint64(heper.DiffNano(startTime)) + requestTime := uint64(helper.DiffNano(startTime)) requestResults := &model.RequestResults{ Time: requestTime, IsSucceed: isSucceed, @@ -77,3 +80,4 @@ func grpcRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, reque ch <- requestResults } + diff --git a/server/golink/websocket_link.go b/server/golink/websocket_link.go index 5b58c7c..caffdad 100644 --- a/server/golink/websocket_link.go +++ b/server/golink/websocket_link.go @@ -12,7 +12,7 @@ import ( "sync" "time" - "go-stress-testing/heper" + "go-stress-testing/helper" "go-stress-testing/model" "go-stress-testing/server/client" ) @@ -105,7 +105,7 @@ func webSocketRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, } } - requestTime := uint64(heper.DiffNano(startTime)) + requestTime := uint64(helper.DiffNano(startTime)) requestResults := &model.RequestResults{ Time: requestTime, @@ -118,3 +118,4 @@ func webSocketRequest(chanId uint64, ch chan<- *model.RequestResults, i uint64, ch <- requestResults } + From 537c368ab0f5d62741898b24cc5c5252d926b7bb Mon Sep 17 00:00:00 2001 From: link1st <562117637@qq.com> Date: Thu, 1 Apr 2021 12:52:50 +0800 Subject: [PATCH 73/75] add default method --- model/curl_model.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/model/curl_model.go b/model/curl_model.go index e51fec8..db4a5cd 100644 --- a/model/curl_model.go +++ b/model/curl_model.go @@ -186,12 +186,17 @@ func (c *CURL) GetMethod() (method string) { keys := []string{"-X", "--request"} value := c.getDataValue(keys) if len(value) <= 0 { - return + return c.defaultMethod() } method = strings.ToUpper(value[0]) if helper.InArrayStr(method, []string{"GET", "POST", "PUT", "DELETE"}) { return method } + return c.defaultMethod() +} + +// defaultMethod 获取默认方法 +func (c *CURL) defaultMethod() (method string) { method = "GET" body := c.GetBody() if len(body) > 0 { @@ -254,3 +259,4 @@ func (c *CURL) getPostForm() (body string) { return } + From dd61ddd77626c97f56416b563d963601b28f76c5 Mon Sep 17 00:00:00 2001 From: wang Date: Thu, 1 Apr 2021 17:45:11 +0800 Subject: [PATCH 74/75] =?UTF-8?q?=E7=94=9F=E6=88=90=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 生成随机参数 比如 go run main.go -c 10 -n 10000 -u http://192.168.0.50:8091/data -data "flag=AccessRecord&name=random12&pic=random2&about=about3" --- helper/helper.go | 10 ++++++++++ model/request_model.go | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/helper/helper.go b/helper/helper.go index 489e0e7..f886f69 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -8,6 +8,7 @@ package helper import ( + "math/rand" "time" ) @@ -28,3 +29,12 @@ func InArrayStr(str string, arr []string) (inArray bool) { return } +func GetRandomString(len int) string { + r := rand.New(rand.NewSource(time.Now().Unix())) + bytes := make([]byte, len) + for i := 0; i < len; i++ { + b := r.Intn(26) + 65 + bytes[i] = byte(b) + } + return string(bytes) +} diff --git a/model/request_model.go b/model/request_model.go index 02aa87a..066f411 100644 --- a/model/request_model.go +++ b/model/request_model.go @@ -15,6 +15,9 @@ import ( "strings" "sync" "time" + "regexp" + "strconv" + "go-stress-testing/helper" ) const ( @@ -81,8 +84,16 @@ type Request struct { } func (r *Request) GetBody() (body io.Reader) { - body = strings.NewReader(r.Body) - + reg1 := regexp.MustCompile(`random(\d+)?`) + result := r.Body + match := reg1.FindAllStringSubmatch(result, -1) + if match != nil { + for _, v := range match { + l, _ := strconv.Atoi(v[1]) + result = strings.Replace(result, v[0], helper.GetRandomString(l), 1) + } + } + body = strings.NewReader(result) return } From 47142375f71e572af61aaacd92d35095da4c757d Mon Sep 17 00:00:00 2001 From: wang Date: Fri, 2 Apr 2021 17:46:52 +0800 Subject: [PATCH 75/75] =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=A7=8D=E5=AD=90?= =?UTF-8?q?=E6=94=B9=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helper/helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/helper.go b/helper/helper.go index f886f69..666331a 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -30,7 +30,7 @@ func InArrayStr(str string, arr []string) (inArray bool) { } func GetRandomString(len int) string { - r := rand.New(rand.NewSource(time.Now().Unix())) + r := rand.New(rand.NewSource(time.Now().UnixNano())) bytes := make([]byte, len) for i := 0; i < len; i++ { b := r.Intn(26) + 65