可能出现的性能问题
- 频繁的连接创建与销毁:每次发起HTTP请求都创建新的连接,开销大,包括TCP握手、TLS协商(若有)等,导致性能下降。
- 资源竞争:多个协程同时访问共享资源(如网络连接),可能产生资源竞争,导致数据不一致或性能损耗。
- 协程管理不善:大量协程并发运行,可能耗尽系统资源(如文件描述符等),且协程的调度也会带来额外开销。
优化方法
- 连接池:
- 使用
http.Transport
中的 MaxIdleConns
和 MaxIdleConnsPerHost
等参数设置连接池。例如:
tr := &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
}
client := &http.Client{Transport: tr}
- 连接池可以复用已建立的连接,减少连接创建与销毁的开销,提高性能。
- 协程管理:
- 限制并发数:使用
sync.WaitGroup
和 channel
来控制并发协程数量。例如:
var wg sync.WaitGroup
semaphore := make(chan struct{}, 50) // 最多允许50个协程并发
for _, url := range urls {
semaphore <- struct{}{}
wg.Add(1)
go func(u string) {
defer func() {
<-semaphore
wg.Done()
}()
// 发起HTTP请求操作
}(url)
}
wg.Wait()
- 合理分配资源:根据系统资源(如CPU、内存等)合理调整协程数量,避免资源耗尽。
性能对比测试思路
- 测试场景:模拟高并发请求多个相同或不同的HTTP端点。
- 指标:记录请求的平均响应时间、吞吐量(每秒请求数)、资源利用率(如CPU、内存使用情况)等。
- 测试步骤:
- 优化前:使用默认的
http.Client
,不做连接池和协程管理优化,发起大量并发请求,记录各项指标。
- 优化后:按照上述优化方法,设置连接池和合理管理协程,再次发起相同规模的并发请求,记录各项指标。
- 对比分析:对比优化前后的指标,评估优化效果。
示例代码框架
- 优化前:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
urls := []string{"http://example.com", "http://example.org"}
start := time.Now()
for _, url := range urls {
go func(u string) {
resp, err := http.Get(u)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
}(url)
}
time.Sleep(5 * time.Second)
elapsed := time.Since(start)
fmt.Printf("Total time: %s\n", elapsed)
}
- 优化后:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
func main() {
urls := []string{"http://example.com", "http://example.org"}
tr := &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
}
client := &http.Client{Transport: tr}
var wg sync.WaitGroup
semaphore := make(chan struct{}, 50)
start := time.Now()
for _, url := range urls {
semaphore <- struct{}{}
wg.Add(1)
go func(u string) {
defer func() {
<-semaphore
wg.Done()
}()
resp, err := client.Get(u)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
}(url)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Printf("Total time: %s\n", elapsed)
}