高性能网关的网络连接

高性能网关的网络连接

对于高性能网关而言,连接的可复用是至关重要的,在高并发场景下,如果连接均为短链接,网关将疲于创建与断开连接。

Http 长连接设置

HTTP1.1通过使用 Connection:keep-alive 进行长连接,HTTP 1.1默认进行持久连接。在一次 TCP 连接中可以完成多个 HTTP 请求,但是对每个请求仍然要单独发 header。如果需要使用短链接,需要显示的设置请求头 Connection: close

通常在服务端会设置长连接的参数,主要是设置请求头里面的 Keep-Alive 参数。该参数包括了两个属性分别是 timeout 和 max。timeout 是最长空闲连接保持时间(单位是秒),max 最大长连接请求数。如果设置为0,则不生效。设置使用栗子是 Keep-Alive: timeout=5, max=1000 。以 tomcat 为例,在 server.xml 中配置 maxKeepAliveRequests 和 keepAliveTimeout 来控制上述参数。

TCP 长连接

TCP 长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(不发生RST包和四次挥手)。

连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接(一个TCP连接通道多个读写通信);

这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态;

TCP保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务器端检测到这种半开放的连接。

如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一:
1、客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。
2、客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送10个这样的探测 ,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
3、客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
4、客户机正常运行,但是服务器不可达,这种情况与2类似,TCP能发现的就是没有收到探查的响应。

短连接:
短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接(管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段);

内核针对 tcp 长连接的默认参数如下。

# 如果某个TCP连接在idle 2个小时后,内核才发起试探。如果probe 9次(每次75秒)不成功,内核认为连接已失效
tcp_keepalive_time = 7200 seconds (2 hours)
tcp_keepalive_probes = 9
tcp_keepalive_intvl = 75 seconds

查看该参数的路径是 /proc/sys/net/ipv4/tcp_keepalive_time;修改时需改动 /etc/sysctl.conf 文件,例如

sysctl -w net.ipv4.tcp_keepalive_time=30
sysctl -w net.ipv4.tcp_keepalive_probes=2
sysctl -w net.ipv4.tcp_keepalive_intvl=2

TIME_WAIT 问题

我们都知道TCP是三次握手,四次挥手。那挥手具体过程是什么?

1、主动关闭连接的一方,调用close(),协议层发送FIN包,主动关闭方进入FIN_WAIT_1状态
2、被动关闭的一方收到FIN包后,协议层回复ACK;然后被动关闭的一方,进入CLOSE_WAIT状态,主动关闭的一方等待对方关闭,则进入FIN_WAIT_2状态;此时,主动关闭的一方 等待 被动关闭一方的应用程序,调用close()操作
3、被动关闭的一方在完成所有数据发送后,调用close()操作;此时,协议层发送FIN包给主动关闭的一方,等待对方的ACK,被动关闭的一方进入LAST_ACK状态;
4、主动关闭的一方收到FIN包,协议层回复ACK;此时,主动关闭连接的一方,进入TIME_WAIT状态;而被动关闭的一方,进入CLOSED状态
5、等待 2MSL(Maximum Segment Lifetime, 报文最大生存时间),主动关闭的一方,结束TIME_WAIT,进入CLOSED状态。(在Centos 7.6.1810 的3.10内核版本上 2MSL 是60秒。)

来张TCP状态机大图,一目了然:
请添加图片描述
TCP状态及其描述如下表。

状态描述
LISTEN等待来自远程TCP应用程序的请求
SYN_SENT发送连接请求后等待来自远程端点的确认。TCP第一次握手后客户端所处的状态
SYN-RECEIVED该端点已经接收到连接请求并发送确认,该端点正在等待最终确认。TCP第二次握手后服务端所处的状态
ESTABLISHED代表连接已经建立起来了。这是连接数据传输阶段的正常状态
FIN_WAIT_1等待来自远程TCP的终止连接请求或终止请求的确认
FIN_WAIT_2在此端点发送终止连接请求后,等待来自远程TCP的连接终止请求
CLOSE_WAIT该端点已经收到来自远程端点的关闭请求,此TCP正在等待本地应用程序的连接终止请求
CLOSING等待来自远程TCP的连接终止请求确认
LAST_ACK等待先前发送到远程TCP的连接终止请求的确认
TIME_WAIT等待足够的时间来确保远程TCP接收到其连接终止请求的确认

请添加图片描述

为什么一定要有 TIME_WAIT ?

虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接到CLOSED状态。但是网络是不可靠的,发起方无法确保最后发送的ACK报文一定被对方收到,比如丢包或延迟到达,对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文。所以TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

简单讲,TIME_WAIT之所以等待2MSL的时长,是为了避免因为网络丢包或者网络延迟而造成的tcp传输不可靠,而这个TIME_WAIT状态则可以最大限度的提升网络传输的可靠性。

注意:一个连接没有进入 CLOSED 状态之前,这个连接是不能被重用的!

如何优化 TIME_WAIT 过多的问题

1、调整系统内核参数
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将 TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout = 修改系统默认的 TIMEOUT 时间
net.ipv4.tcp_max_tw_buckets = 5000 表示系统同时保持TIME_WAIT套接字的最大数量,(默认是18000). 当TIME_WAIT连接数量达到给定的值时,所有的TIME_WAIT连接会被立刻清除,并打印警告信息。但这种粗暴的清理掉所有的连接,意味着有些连接并没有成功等待2MSL,就会造成通讯异常。一般不建议调整
net.ipv4.tcp_timestamps = 1(默认即为1)60s内同一源ip主机的socket connect请求中的timestamp必须是递增的。也就是说服务器打开了 tcp_tw_reccycle了,就会检查时间戳,如果对方发来的包的时间戳是乱跳的或者说时间戳是滞后的,那么服务器就会丢掉不回包,现在很多公司都用LVS做负载均衡,通常是前面一台LVS,后面多台后端服务器,这其实就是NAT,当请求到达LVS后,它修改地址数据后便转发给后端服务器,但不会修改时间戳数据,对于后端服务器来说,请求的源地址就是LVS的地址,加上端口会复用,所以从后端服务器的角度看,原本不同客户端的请求经过LVS的转发,就可能会被认为是同一个连接,加之不同客户端的时间可能不一致,所以就会出现时间戳错乱的现象,于是后面的数据包就被丢弃了,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK,还可以通过下面命令来确认数据包不断被丢弃的现象,所以根据情况使用

其他优化:
net.ipv4.ip_local_port_range = 1024 65535 ,增加可用端口范围,让系统拥有的更多的端口来建立链接,这里有个问题需要注意,对于这个设置系统就会从1025~65535这个范围内随机分配端口来用于连接,如果我们服务的使用端口比如8080刚好在这个范围之内,在升级服务期间,可能会出现8080端口被其他随机分配的链接给占用掉
net.ipv4.ip_local_reserved_ports = 7005,8001-8100 针对上面的问题,我们可以设置这个参数来告诉系统给我们预留哪些端口,不可以用于自动分配。

CLOSE_WAIT 问题

主动关闭的一方发出 FIN 包,被动关闭的一方响应 ACK 包,此时,被动关闭的一方就进入了 CLOSE_WAIT 状态。如果一切正常,稍后被动关闭的一方也会发出 FIN 包,然后迁移到 LAST_ACK 状态。

通常,CLOSE_WAIT 状态在服务器停留时间很短,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 FIN 包,一般有如下几种可能:

程序问题:如果代码层面忘记了 close 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 close 也永远执行不到。
响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。
BACKLOG 太大:此处的 backlog 不是 syn backlog,而是 accept 的 backlog,如果 backlog 太大的话,设想突然遭遇大访问量的话,即便响应速度不慢,也可能出现来不及消费的情况,导致多余的请求还在队列里就被对方关闭了。

参考:
1、https://www.51cto.com/article/666745.html
2、https://zhuanlan.zhihu.com/p/377616470
3、https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值