前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >问题分析:引入新elastic api导致的TIME_WAIT堆积

问题分析:引入新elastic api导致的TIME_WAIT堆积

原创
作者头像
王昂
修改2019-11-02 20:40:24
3.9K0
修改2019-11-02 20:40:24
举报
文章被收录于专栏:Andy的技术专栏Andy的技术专栏

之前使用github.com/olivere/elastic库遇到了一个TIME_WAIT堆积的问题,因为问题比较共性(引入新库、性能测试、TIME_WAIT原理),所以简单记录下,新同学可以关注下

发生背景:业务引入新elastic api

之前业务调用ES是走原生RESTful,用golang的net/http直接写客户端。由于这种方式要自己拼表达式,所以有同学就引入了github.com/olivere/elastic,对表达式封装了一层,让代码更加简单高效,示例:

github.com/olivere/elastic示例.png
github.com/olivere/elastic示例.png

但是模块发布后,陆续发现服务请求ES无响应,导致服务不可用,立即回滚先恢复。

问题原因:http短连导致TIME_WAIT堆积

明确ES本身没问题后,查看服务机器发现非常多调用ES的链接处在TIME_WAIT状态,命令实例:

代码语言:txt
复制
[root@TENCENT64 ~]# netstat -n | grep "111.111.111.111:9200" | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 2
TIME_WAIT 10503

太多的TIME_WAIT链接占满了端口65535的限制,导致新链接无法发起。

这里开始抠github.com/olivere/elastic 的源码,为啥已用了全局client还会导致大量TIME_WAIT:

业务使用入口.png
业务使用入口.png
github.com/olivere/elastic/client.go.png
github.com/olivere/elastic/client.go.png
net/http/client.go.png
net/http/client.go.png

可以看到,/olivere/elastic的ESClient最后也是调用了/net/http库,那核心就是看下他怎么管理http的client了:

github.com/olivere/elastic/client.go
github.com/olivere/elastic/client.go
github.com/olivere/elastic/client.go
github.com/olivere/elastic/client.go
net/http/client.go
net/http/client.go
net/http/transport.go
net/http/transport.go

可以得出初步结论:/olivere/elastic的ESClient使用了/net/http的默认全局http.DefaultClient,http.DefaultClient底层通讯Transport默认使用了DefaultTransport,而DefaultTransport初始化没设置 MaxIdleConnsPerHost,于是采了默认的DefaultMaxIdleConnsPerHost=2。即只支持2个tcp链接复用,并发数大的话很容易就超了,连接池拿不到链接的话就默认新创建短连了

解决方法:合理设置/net/http连接池大小

解决大量TIME_WAIT的方法有很多,针对这个case,这边需在初始化/olivere/elastic/client的时候,设置底层/net/http合理的连接池数量,如:

合理设置/net/http连接池大小.png
合理设置/net/http连接池大小.png

再压测观察链接数就恢复正常了:

代码语言:txt
复制
[root@TENCENT64 ~]# netstat -n | grep "111.111.111.111:9200" | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 100

连接池大小要设置多少算够?这里一个比较简单的实践:

  1. 单个连接QPS = 1s/平均延迟
  2. 最大连接数 = 业务QPS/单个连接QPS + 一点富余量

可以参考:https://partners-intl.aliyun.com/help/doc-detail/98726.htm

思考反思:引入新库需谨慎,必须提前做功能和压力测试

开发过程很多同学会引入各种各样的第3方库,帮忙团队提高研发效率,但引入前必须提前做好:

  1. 团队评估:关注使用普及度、业内反馈等
  2. 功能测试:关注边缘变量、异常处理等
  3. 压力测试:关注性能消耗,资源占用等

相关扩展:TIME_WAIT状态有必要存在吗?

根据tcp的4次挥手状态转化图,可知主动关闭连接的一方会进入TIME_WAIT,停留2个MSL时间后关闭:

tcp的4次挥手状态转化.png
tcp的4次挥手状态转化.png

关闭就关闭了,TIME_WAIT状态还要存在的原因

1、保证完整全双工关闭链接

BadCase:A最后发的ACK丢了,B会重发FIN,如果A没有TIME_WAIT则系统会直接回RST,导致B直接抛异常

RST包:表示复位,直接关闭异常连接,比如服务器没监听的端口收到数据包

2、防止有未接收完的数据包

BadCase:B发完FIN后,之前B的旧数据分片到达(网络波动等影响),这时A这个端口起了新连接,新连接收到上个连接的旧分片可能会导致异常

相关扩展:导致大量TIME_WAIT的常见原因和解决方案

由此可见,如果SVR短期内有大量RPC短链请求,或者访问量大的WebSvr(主动断开链接)都容易导致大量TIME_WAIT产生

常见的解决方案:

  1. 开启socket重用
  2. 开启快速回收
  3. 改短连接为长连接
  4. 调整TIME_WAIT相关参数

几个相关参数:

  1. net.ipv4.tcp_tw_reuse:socket重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
  2. net.ipv4.tcp_tw_recycle:快速回收。允许处于TIME-WAIT状态的socket快速回收到100左右,默认为0,表示关闭
  3. net.ipv4.tcp_timestamps:记录单调递增时间戳,默认为1,表示开启
  4. net.ipv4.tcp_max_tw_buckets:允许最大TIME_WAIT连接数,在TIME_WAIT数量超过后,不会有新的TIME_WAIT产生(设置< 65535做降级保护)
  5. net.ipv4.tcp_fin_timeout:MSL时间,修改系默认的TIMEOUT 时间

ps:reuse和recycle必须在timestamps开启后才有用。但是不建议在NAT环境中启用,它会引起相关问题,可参考:

https://blog.51cto.com/hld1992/2285410

几个相关命令:

  1. 查看配置: /sbin/sysctl -a
  2. 修改配置: vi /etc/sysctl.conf
  3. 重新加载sysctl配置: /sbin/sysctl -p

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 发生背景:业务引入新elastic api
  • 问题原因:http短连导致TIME_WAIT堆积
  • 解决方法:合理设置/net/http连接池大小
  • 思考反思:引入新库需谨慎,必须提前做功能和压力测试
  • 相关扩展:TIME_WAIT状态有必要存在吗?
  • 相关扩展:导致大量TIME_WAIT的常见原因和解决方案
相关产品与服务
Elasticsearch Service
腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档