You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
var (
httpClient *client.Client
once sync.Once
//retryableErrPattern = regexp.MustCompile(connection is closed by peer while being in the connection pool)
)
// newClient
//
// @description: hertz客户端初始化
// @returnclient.Client
// @return error
func newClient() (client.Client, error) {
c, err := client.NewClient(
client.WithDialTimeout(3time.Second),
client.WithMaxConnsPerHost(1024), //每个主机可能建立的最大连接数
client.WithMaxConnDuration(10time.Second), //最大的连接持续时间,keep-alive 连接在此持续时间后被关闭
client.WithMaxConnWaitTimeout(10time.Second), //等待空闲连接的最大时间
client.WithClientReadTimeout(10time.Second), //完整读取响应(包括 body)的最大持续时间
client.WithWriteTimeout(10time.Second), //HTTP 客户端的写入超时时间
client.WithRetryConfig(
retry.WithMaxAttemptTimes(3), // 最大的尝试次数,包括初始调用
retry.WithInitDelay(3time.Millisecond), // 初始延迟
retry.WithMaxDelay(10time.Millisecond), // 最大延迟,不管重试多少次,策略如何,都不会超过这个延迟
retry.WithMaxJitter(10time.Millisecond), // 延时的最大扰动,结合 RandomDelayPolicy 才会有效果
retry.WithDelayPolicy(retry.CombineDelay(
retry.FixedDelayPolicy,
retry.BackOffDelayPolicy,
retry.RandomDelayPolicy,
)),
),
)
if err != nil {
return nil, err
}
//重试函数设置
c.SetRetryIfFunc(func(req *protocol.Request, resp *protocol.Response, err error) bool {
// cannot retry if the request body is not rewindable
if req.IsBodyStream() {
return false
}
//判断是否幂等
if isIdempotent(req) {
return true
}
// Retry non-idempotent requests if the server closes
// the connection before sending the response.
//
// This case is possible if the server closes the idle
// keep-alive connection on timeout.
//
// Apache and nginx usually do this.
if err == io.EOF {
return true
}
// 使用正则表达式匹配错误信息
//if err != nil && retryableErrPattern.MatchString(err.Error()) {
//
// return true
//}
if err != nil {
return true
}
return false
})
return c, err
}
Describe the Question
hertz client 发送http请求,但是偶尔会出现connection is closed by peer while being in the connection pool
Reproducible Code
package utils
import (
"context"
"encoding/json"
"github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/app/client/retry"
"github.com/cloudwego/hertz/pkg/common/config"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/protocol"
"io"
"net/http"
"net/url"
"sync"
"time"
)
var (
httpClient *client.Client
once sync.Once
//retryableErrPattern = regexp.MustCompile(
connection is closed by peer while being in the connection pool
))
// newClient
//
// @description: hertz客户端初始化
// @return client.Client
// @return error
func newClient() (client.Client, error) {
c, err := client.NewClient(
client.WithDialTimeout(3time.Second),
client.WithMaxConnsPerHost(1024), //每个主机可能建立的最大连接数
client.WithMaxConnDuration(10time.Second), //最大的连接持续时间,keep-alive 连接在此持续时间后被关闭
client.WithMaxConnWaitTimeout(10time.Second), //等待空闲连接的最大时间
client.WithClientReadTimeout(10time.Second), //完整读取响应(包括 body)的最大持续时间
client.WithWriteTimeout(10time.Second), //HTTP 客户端的写入超时时间
client.WithRetryConfig(
retry.WithMaxAttemptTimes(3), // 最大的尝试次数,包括初始调用
retry.WithInitDelay(3time.Millisecond), // 初始延迟
retry.WithMaxDelay(10time.Millisecond), // 最大延迟,不管重试多少次,策略如何,都不会超过这个延迟
retry.WithMaxJitter(10time.Millisecond), // 延时的最大扰动,结合 RandomDelayPolicy 才会有效果
retry.WithDelayPolicy(retry.CombineDelay(
retry.FixedDelayPolicy,
retry.BackOffDelayPolicy,
retry.RandomDelayPolicy,
)),
),
)
if err != nil {
return nil, err
}
//重试函数设置
c.SetRetryIfFunc(func(req *protocol.Request, resp *protocol.Response, err error) bool {
// cannot retry if the request body is not rewindable
if req.IsBodyStream() {
return false
}
//判断是否幂等
if isIdempotent(req) {
return true
}
// Retry non-idempotent requests if the server closes
// the connection before sending the response.
//
// This case is possible if the server closes the idle
// keep-alive connection on timeout.
//
// Apache and nginx usually do this.
if err == io.EOF {
return true
}
// 使用正则表达式匹配错误信息
//if err != nil && retryableErrPattern.MatchString(err.Error()) {
//
// return true
//}
if err != nil {
return true
}
return false
})
return c, err
}
// isIdempotent
//
// @description: 判断是否幂等
// @param req
// @return bool
func isIdempotent(req *protocol.Request) bool {
return req.Header.IsGet() ||
req.Header.IsHead() ||
req.Header.IsPut() ||
req.Header.IsDelete() ||
req.Header.IsOptions() ||
req.Header.IsTrace()
}
// InitClient
//
// @description: 初始化全局的HTTP客户端
func InitClient() {
once.Do(func() {
var err error
httpClient, err = newClient()
if err != nil {
// Handle error appropriately, possibly logging or panicking
hlog.Errorf("Failed to initialize Hertz client: %v", err)
panic(err) // or some other error handling strategy
}
})
}
// Do
//
// @description: 执行http请请求方法
// @param method
// @param urlStr
// @param body
// @param headers
// @param params
// @param respType
// @return error
func Do(method, urlStr string, body interface{}, headers map[string]string, params url.Values, respType interface{}) error {
req := &protocol.Request{}
res := &protocol.Response{}
//请求设置
req.SetOptions(config.WithDialTimeout(1time.Second),
config.WithReadTimeout(3time.Second),
config.WithWriteTimeout(3*time.Second))
//设置请求方式
req.SetMethod(method)
//设置请求url
req.SetRequestURI(urlStr)
//设置请求头
for k, v := range headers {
req.SetHeader(k, v)
}
// 设置查询参数
if params != nil {
req.SetQueryString(params.Encode())
}
//设置body
if body != nil {
req.Header.SetContentTypeBytes([]byte("application/json"))
bodyBytes, err := json.Marshal(body)
if err != nil {
return err
}
req.SetBody(bodyBytes)
}
err := httpClient.Do(context.Background(), req, res)
if err != nil {
return err
}
}
// SendGet
//
// @description: 发起GET请求
// @receiver c
// @param url
// @param headers
// @param params
// @param respType
// @return error
func SendGet(url string, headers map[string]string, params url.Values, respType interface{}) error {
return Do(http.MethodGet, url, nil, headers, params, respType)
}
// SendPost
//
// @description: 发起POST请求
// @receiver c
// @param url
// @param body
// @param headers
// @param params
// @param respType
// @return error
func SendPost(url string, body interface{}, headers map[string]string, params url.Values, respType interface{}) error {
//return Do(http.MethodPost, url, body, headers, params, respType)
maxAttempts := 3
delay := 1 * time.Millisecond
var err error
for attempt := 1; attempt <= maxAttempts; attempt++ {
err = Do(http.MethodPost, url, body, headers, params, respType)
if err == nil {
return nil
}
}
Expected behavior
希望可以规避这种错误的发生,
Screenshots
If applicable, add screenshots to help explain your question.
Hertz version:
github.com/cloudwego/hertz v0.9.5
Environment:
生产
Additional context
Add any other context about the question here.
The text was updated successfully, but these errors were encountered: