Go HTTP请求出现read: connection reset by peer错误的解决求助
解决Go并发请求中"Connection reset by peer"问题
我查阅了相关解答,将MaxConnsPerHost设置为并发请求数,但仍出现错误:read tcp 127.0.0.1:52517->127.0.0.1:8080: read: connection reset by peer。
客户端代码
package main import ( "fmt" "io" "net/http" "sync" "time" ) func main() { var wg sync.WaitGroup for i := 0; i < 400; i++ { wg.Add(1) go func(id int) { defer wg.Done() url := "http://127.0.0.1:8080/sleep" rt := http.Transport{ MaxIdleConns: 400, MaxIdleConnsPerHost: 400, MaxConnsPerHost: 400, IdleConnTimeout: 20 * time.Second, DisableKeepAlives: false, } client := http.Client{ Transport: &rt, } resp, err := client.Get(url) if err != nil { fmt.Printf("%d Request to [%s] failed, error: %v\n", i, url, err) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("%d Failed to read response from [%s], error: %v\n", i, url, err) return } fmt.Printf("%d Response from [%s] result: %s\n", i, url, string(body)) }(i) } wg.Wait() }
服务端代码
package main import ( "github.com/gin-gonic/gin" "time" ) func main() { r := gin.Default() r.GET("/sleep", func(c *gin.Context) { time.Sleep(10) c.JSON(200, gin.H{ "message": "Slept for 10 seconds", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
问题原因与解决方案
核心问题1:客户端每次请求创建新的Transport
当前客户端在每个goroutine里都新建http.Transport实例,导致每个请求使用独立连接池,MaxConnsPerHost的限制完全失效——400个goroutine会同时发起400个独立连接,远超系统或服务端的连接上限,最终触发连接重置。
核心问题2:服务端默认连接数限制
Gin默认的http.Server未调整最大连接数,系统默认文件描述符限制(通常为1024)会导致服务端无法处理过多并发连接,主动重置超出限制的连接。
修复步骤
1. 客户端复用全局Transport
将http.Transport和http.Client提升为全局变量,让所有goroutine复用同一个连接池:
package main import ( "fmt" "io" "net/http" "sync" "time" ) // 全局复用的客户端和Transport var client = &http.Client{ Transport: &http.Transport{ MaxIdleConns: 400, MaxIdleConnsPerHost: 400, MaxConnsPerHost: 400, IdleConnTimeout: 20 * time.Second, DisableKeepAlives: false, }, } func main() { var wg sync.WaitGroup for i := 0; i < 400; i++ { wg.Add(1) go func(id int) { defer wg.Done() url := "http://127.0.0.1:8080/sleep" resp, err := client.Get(url) if err != nil { fmt.Printf("%d Request to [%s] failed, error: %v\n", id, url, err) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("%d Failed to read response from [%s], error: %v\n", id, url, err) return } fmt.Printf("%d Response from [%s] result: %s\n", id, url, string(body)) }(i) } wg.Wait() }
2. 调整服务端最大连接数与超时
修改Gin的http.Server配置,增加最大连接数并设置合理超时:
package main import ( "github.com/gin-gonic/gin" "net/http" "time" ) func main() { r := gin.Default() r.GET("/sleep", func(c *gin.Context) { time.Sleep(10 * time.Millisecond) // 修正单位:原代码time.Sleep(10)为纳秒,与注释逻辑不符 c.JSON(200, gin.H{ "message": "Slept for 10 milliseconds", }) }) // 自定义Server配置,提升连接限制 srv := &http.Server{ Addr: ":8080", Handler: r, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, // 若需更高并发,需配合调整系统文件描述符限制 } if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { panic(err) } }
3. 调整系统文件描述符限制(必要时)
如果并发数超过系统默认限制,临时调整当前会话的限制:
ulimit -n 4096
或修改系统配置文件(如/etc/security/limits.conf)永久调整。
内容的提问来源于stack exchange,提问作者xmh
相关产品推荐
相关产品推荐

