文章目录
最近搭建了一个TRON节点,同事不相信我的自建节点比官方更靠谱,咱们给他使用golang写一个压测脚本,测试一下。
本文档详细介绍了一个基于 Go 的并发 HTTP 请求工具,帮助你对 Tron 节点进行压力测试。本文档将从代码简介、环境配置、编译运行以及结果分析等方面进行详细说明。
一、代码说明
该工具通过并发多个 HTTP 请求,对 Tron 节点的处理能力进行压力测试。每个请求都会记录响应时间,并将结果输出。
1.1 主要功能
- 发送并发 HTTP 请求到指定 Tron 节点。
- 记录每个请求的响应时间和状态。
- 支持配置请求超时时间、并发 worker 数量和每个 worker 发送的请求数量。
1.2 代码示例
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
// worker 函数执行 HTTP 请求并记录响应时间
func worker(id int, wg *sync.WaitGroup, url string, payload string, results chan<- string, numRequests int, timeout time.Duration) {
defer wg.Done()
client := &http.Client{
Timeout: timeout,
}
for i := 0; i < numRequests; i++ {
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(payload)))
if err != nil {
results <- fmt.Sprintf("Worker %d: error creating request: %v", id, err)
return
}
req.Header.Set("Content-Type", "application/json")
start := time.Now()
resp, err := client.Do(req)
duration := time.Since(start)
if err != nil {
results <- fmt.Sprintf("Worker %d: error making request: %v", id, err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
results <- fmt.Sprintf("Worker %d: received non-OK status: %s, response body: %s", id, resp.Status, string(body))
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
results <- fmt.Sprintf("Worker %d: error reading response body: %v", id, err)
return
}
results <- fmt.Sprintf("Worker %d: success in %v, response: %s", id, duration, string(body))
}
}
func main() {
// Tron 节点的 URL
url := "http://节点地址:8090/wallet/getnowblock"
// 替换为你的实际账户地址
accountAddress := "your-account-address"
payload := fmt.Sprintf(`{"address":"%s"}`, accountAddress)
// 并发请求的 worker 数量
numWorkers := 200
// 每个 worker 发送的请求数量
numRequests := 1
// HTTP 请求的超时时间
timeout := 3 * time.Second
var wg sync.WaitGroup
results := make(chan string, numWorkers*numRequests)
// 启动 worker
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, url, payload, results, numRequests, timeout)
}
// 等待所有 worker 完成
wg.Wait()
close(results)
// 输出结果
for result := range results {
fmt.Println(result)
}
}
1.3 代码解释
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
导入包
- bytes:用于处理字节缓冲区,构造 HTTP 请求的 body。
- fmt:用于格式化输入输出。
- ioutil:提供 I/O 实用函数,读取 HTTP 响应的 body。
- net/http:用于发送 HTTP 请求。
- sync:提供同步原语,例如 WaitGroup。
- time:用于处理时间和定时器。
func worker(id int, wg *sync.WaitGroup, url string, payload string, results chan<- string, numRequests int, timeout time.Duration) {
defer wg.Done()
- 参数:
- id:worker 的 ID,用于标识日志中的具体 worker。
- wg:WaitGroup,用于等待所有 worker 完成。
- url:HTTP 请求的目标 URL。
- payload:请求体数据。
- results:用于存储结果的通道。
- numRequests:每个 worker 发送的请求数量。
- timeout:每个 HTTP 请求的超时时间。
- defer wg.Done():确保函数结束时调用 wg.Done(),通知 WaitGroup 当前 worker 已完成。
client := &http.Client{
Timeout: timeout,
}
创建 HTTP 客户端
- 创建一个带有超时设置的 HTTP 客户端。
for i := 0; i < numRequests; i++ {
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(payload)))
if err != nil {
results <- fmt.Sprintf("Worker %d:error creating request: %v", id, err)
return
}
req.Header.Set("Content-Type","application/json")
发送 HTTP 请求
- 循环发送 numRequests 个请求。
- 使用 http.NewRequest 创建新的 POST 请求。
- 设置请求头 Content-Type 为 application/json。
start := time.Now()
resp, err := client.Do(req)
duration := time.Since(start)
记录请求时间
- 记录请求开始时间。
- 发送请求并接收响应。
- 计算请求的总持续时间。
if err != nil {
results <- fmt.Sprintf("Worker %d: error making request: %v", id, err)
return
}
defer resp.Body.Close()
处理请求错误和响应
- 如果请求错误,记录错误信息并返回。
- 确保在函数结束时关闭响应体。
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
results <- fmt.Sprintf("Worker %d: received non-OK status: %s, response body: %s", id, resp.Status, string(body))
return
}
检查响应状态码
- 检查响应状态码是否为 200 (OK)。
- 如果不是 200,读取响应体并记录错误信息。
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
results <- fmt.Sprintf("Worker %d: error reading response body: %v", id, err)
return
}
读取响应体
- 读取响应体,如果读取错误,记录错误信息。
results <- fmt.Sprintf("Worker %d: success in %v, response: %s", id, duration, string(body))
}
}
记录成功请求
- 将成功请求的结果、持续时间和响应体发送到结果通道。
func main() {
// Tron 节点的 URL
url := "http://tron节点地址:8090/wallet/getnowblock"
// 替换为你的实际账户地址
accountAddress := "your-account-address"
payload := fmt.Sprintf(`{"address":"%s"}`, accountAddress)
main 函数
- 定义 Tron 节点的 URL 和请求体数据。
// 并发请求的 worker 数量
numWorkers := 200
// 每个 worker 发送的请求数量
numRequests := 1
// HTTP 请求的超时时间
timeout := 3 * time.Second
配置参数
- numWorkers:并发 worker 的数量。
- numRequests:每个 worker 发送的请求数量。
- timeout:HTTP 请求的超时时间。
var wg sync.WaitGroup
results := make(chan string,numWorkers*numRequests)
初始化 WaitGroup 和结果通道
- 创建一个 WaitGroup,用于同步所有 worker。
- 创建一个结果通道,用于存储每个请求的结果。
// 启动 worker
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, url, payload, results, numRequests, timeout)
}
启动并发 worker
- 启动 numWorkers 个并发 worker,每个 worker 在独立的 goroutine 中运行。
// 等待所有 worker 完成
wg.Wait()
close(results)
等待所有 worker 完成
- 使用 wg.Wait() 等待所有 worker 完成。
- 关闭结果通道,表示所有结果已经写入完成。
// 输出结果
for result := range results {
fmt.Println(result)
}
}
- 输出结果
迭代结果通道,打印每个请求的结果。
1.4 执行流程
- 初始化和配置:导入必要的包并定义 worker 函数和 main 函数中的参数。
- 创建并启动 worker:在 main 函数中,根据配置启动并发的 worker。
- 发送请求:每个 worker 根据 numRequests 发送指定数量的请求。
- 处理响应:每个请求记录开始时间、发送请求、接收响应并记录结束时间。处理请求的错误和响应状态码,并读取响应体。
- 记录结果:将每个请求的结果、持续时间和响应体发送到结果通道。
- 等待完成:等待所有 worker 完成,并关闭结果通道。
- 输出结果:迭代结果通道,打印每个请求的结果。
二、结果分析
程序的输出包括每个请求的响应时间和响应内容。以下是输出示例:
Worker 0: success in 150ms, response: {...}
Worker 1: success in 145ms, response: {...}
Worker 2: received non-OK status: 500 Internal Server Error, response body: {...}
...
Average request duration: 147ms
输出说明
- Worker X: success in Yms, response: {…}:表示 worker X 成功发送了请求,响应时间为 Y 毫秒,响应内容为 {…}。
- Worker X: received non-OK status: …:表示 worker X 收到了非 200 状态码的响应,响应内容显示在 {…} 中。
- Average request duration: …:表示所有请求的平均响应时间。
三、参数解释
3.1 numWorkers 和 numRequests 说明
numWorkers
- 定义:numWorkers 代表并发执行的 worker 数量。
- 作用:控制并发请求的数量。每个 worker 是一个独立的 goroutine,它会发送 HTTP 请求到指定的 URL。
- 作用机制:当程序运行时,会启动 numWorkers 个并发的 goroutine,每个 goroutine 都会执行 worker 函数。
numRequests
- 定义:numRequests 代表每个 worker 需要发送的请求数量。
- 作用:控制每个 worker 发送的请求总数。一个 worker 会在一个循环中发送多个请求,直到达到numRequests 的数量。
- 作用机制:每个 worker 启动后,会在一个循环中发送 numRequests 个 HTTP 请求。
两个参数的关系
- 并发度:numWorkers 决定了程序的并发度。更多的 workers 意味着更多的并发请求。
- 总请求数:总的请求数由 numWorkers 和 numRequests 的乘积决定。总请求数 = numWorkers × numRequests。
关系示例
numWorkers = 200
numRequests = 10
那么:
- 将启动 200 个并发的 worker。
- 每个 worker 发送 10 个请求。
- 总请求数为 200 × 10 = 2000 个请求。
3.2 使用场景
高并发测试
- 增大 numWorkers 可以增加并发请求的数量,用于测试服务器在高并发情况下的表现。
- 适用于需要模拟大量用户同时访问服务器的场景。
高负载测试
- 增大 numRequests 可以增加每个 worker 发送的请求总数,用于测试服务器在高负载情况下的表现。
- 适用于需要模拟少量用户频繁访问服务器的场景。
可以调整 numWorkers 和 numRequests 来模拟不同的压力场景。
- 如需模拟高并发但请求数少的情况,可以增加 numWorkers 并减少 numRequests。
- 如需模拟低并发但请求数多的情况,可以减少 numWorkers 并增加 numRequests。
通过合理配置 numWorkers 和 numRequests,可以有效地测试服务器在不同负载和并发条件下的性能表现。
四、注意事项
-
请确保 Tron节点可以处理大量并发请求,以避免节点过载。
-
调整超时时间以适应你的网络环境,避免因网络延迟导致的不必要错误。