本文最后更新于 2024-03-13T09:40:27+08:00
因为业务需要发送大量的http的请求,会有很多的302跳转,使用的是go,对go中的httpClient做了一些重新配置,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var client *http.Client
func init() { client = &http.Client{ Transport: &http.Transport{ ReadBufferSize: 2048, WriteBufferSize: 2048, MaxResponseHeaderBytes: 4096, DialContext: (&net.Dialer{ Timeout: 10 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 1000, IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, SessionTicketsDisabled: false, ClientSessionCache: &sessionCache{cache: sync.Map{}}}, MaxIdleConnsPerHost: 1000, MaxConnsPerHost: 5000, DisableKeepAlives: false, }, Timeout: 10 * time.Second, } }
|
一般情况下,这样的配置应该会有不错的效果,但是部署到线上之后出现了,cpu稍微有点高(与优化后的对比),峰值30M的带宽全部吃满,导致很多请求其实发不出去了。
着重观察10点13之前的cpu与带宽情况,cpu不太高,但是带宽使用很大
重点是发送http请求使用时长,有一些已经超过20秒了,即便每次是500个请求,开启了500个协程,使用了连接池,依然花费巨大的时间。
前前后后几个月一直在分析具体原因,一次正常工作的下午,说再细细看一下这块的情况,仔细分析了当前场景与http发送的关系,结合机器的流量观察,发现有很多的与第三方建立的链接没有释放, 这不符合我们的逻辑,理论建立连接发送数据后就会断开,但是结果不是这样,优化了一下,禁止重定向,到达我们预定义的地址后就不再请求下次链接,修改后的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| client = &http.Client{ Transport: &http.Transport{ ReadBufferSize: 2048, WriteBufferSize: 2048, MaxResponseHeaderBytes: 4096, DialContext: (&net.Dialer{ Timeout: 10 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 1000, IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, SessionTicketsDisabled: false, ClientSessionCache: &sessionCache{cache: sync.Map{}}}, MaxIdleConnsPerHost: 1000, MaxConnsPerHost: 5000, DisableKeepAlives: false, }, Timeout: 10 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { if check.MaxRedirect(len(via)) { return errors.New("max redirect") } return http.ErrUseLastResponse }, }
|
- 让禁止重定向,让CheckRedirect返回一个http.ErrUseLastResponse,http.ErrUseLastResponse的结果会返回上一次跳转成功的结果,根据自己的业务去判断需要返回什么
- 与此同时,修改入参的请求为类似mq方式,接收请求,做了一个1500大小的chan作为延迟队列,启动协程去消费chan中的数据,再去发送
简单的代码逻辑如下:
接收请求
1 2 3 4 5 6 7
| for _, item := range reqList { if err := RequestAsyncProducer(item); err != nil { msg = err.Error() break } } return msg
|
请求异步发送到一个chan
1 2 3 4 5 6 7 8
| func RequestAsyncProducer(data Data) error { select { case topicRequests <- &data: return nil default: return errors.New("too busy") } }
|
消费chan中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| func consumer(parallel int) { ch := make(chan struct{}, parallel) for i := 0; i < parallel; i++ { ch <- struct{}{} } for { <-ch go func() { defer func() { if err := recover(); err != nil { ch <- struct{}{} fmt.Println("error", err) } }() var request *Data ticker := time.Tick(time.Duration(3) * time.Second) for { select { case request = <-topicRequests: toSend(*request) case <-ticker: } } }() } }
|
经过优化后的效果入最开的图,响应时间如下,异步消费了当然会很快了,带宽,cpu如最开始的10点13之后的,下降很大,现在带宽已不是问题了。