《使用 Go 程序进行 HTTP 压测》
用 Go 并发发起 HTTP 请求做简单的压力测试:模拟 N 个用户、每个用户连续请求 M 次,验证验证码服务的性能。
1. 压测主程序
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"sync"
)
type Session struct {
Sessionstr string
Success string
}
var PUB = "YOUR_PUBLIC_KEY"
func getSession() string {
resp, err := http.Get(fmt.Sprintf("http://YOUR_HOST:8515/session_cb?pub=%s", PUB))
if err != nil {
log.Println(err)
return ""
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return ""
}
strBody := string(body)
length := len(strBody)
var session Session
json.Unmarshal([]byte(strBody[9:length-1]), &session)
return session.Sessionstr
}
func getImage(session string) {
resp, err := http.Get(fmt.Sprintf("http://YOUR_SERVER:8515/image?session=%s&pub=%s", session, PUB))
if err != nil {
log.Println(err)
}
defer resp.Body.Close()
}
func verify(session string) {
requestBody := url.Values{
"response": {"12345"},
"challenge": {session},
"privatekey": {"xx"},
"remoteip": {"1.1.1.1"},
}
resp, err := http.PostForm("http://YOUR_SERVER:8515/verify", requestBody)
if err != nil {
log.Println(err)
}
defer resp.Body.Close()
}
func main() {
// 增大每个主机的最大空闲连接数,防止压测时 TCP 连接耗尽
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 4096
const userNumber = 500 // 模拟用户数
const requestNumber = 1000 // 每个用户请求次数
var wg sync.WaitGroup
wg.Add(userNumber)
for num := 0; num < userNumber; num++ {
go func() {
defer wg.Done()
for cnt := 0; cnt < requestNumber; cnt++ {
session := getSession()
getImage(session)
verify(session)
}
}()
}
wg.Wait()
}
2. 压测问题
2.1 Cannot assign requested address
由于 Linux 分配的客户端连接端口用尽,无法建立新的 socket。虽然 socket 正常关闭,但端口不是立即释放,而是进入 TIME_WAIT 状态,默认等 60s 后才释放。
现象:ss -a | wc -l 连接数持续飙升,最终没有可用的 socket 连接。
解决:修改 /etc/sysctl.conf
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
立即生效:
sysctl -p
说明:
tcp_tw_reuse+tcp_tw_recycle用于复用 TIME_WAIT 连接,加速端口回收。
阅读 —
·
全站 —