为什么会有这篇文章
在 plantegg 大佬的知识星球里看到了 应用断开连接的时候如何让 OS 走 RST 流程?提问,想着写前一篇balancer刚好看到了 kubernetes 关于 tcp 探测的优化,所以顺便实验下巩固下,顺便推荐下 plantegg 大佬的星球性能优化的干货很多。
SO_LINGER 参数说明
https://man7.org/linux/man-pages/man7/socket.7.html
1
2
3
4
5
6
7
Sets or gets the SO_LINGER option. The argument is a linger structure.
struct linger {
int l_onoff; /* linger active */
int l_linger; /* how many seconds to linger for */
};
When enabled, a close(2) or shutdown(2) will not return until all queued messages for the socket have been successfully sent or the linger timeout has been reached. Otherwise, the call returns immediately and the closing is done in the background. When the socket is closed as part of exit(2), it always lingers in the background
这里说明的很详细了,如果设置了 linger
的 l_onoff
为 1,在设置 l_linger
单位秒,在调用close或者shutdown的时候,在 l_linger 规定的时间内 发送/接收 完数据,或者达到了 l_linger
规定的时间,会直接发送 rst 结束请求,所以这个l_linger
大小的设置需要谨慎,不然会出现数据还没发送或者接收完就关闭链接了,不过一般场景不会直接这么用rst请求。
实验
- 本地随便启动个 8080 端口的服务
- 运行如下程序 https://github.com/OnlyPiglet/blog_code_pratice/blob/main/network/direct_rst_connect.go
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
package main
import (
"net"
"syscall"
)
func NewRstDialer() *net.Dialer {
return &net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
// 此处激活so_linger,同时设置超时时间为0
syscall.SetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, &syscall.Linger{Onoff: 1, Linger: 0})
})
},
}
}
func main() {
rstDialer := NewRstDialer()
rstConn, _ := rstDialer.Dial("tcp", "127.0.0.1:8080")
_ = rstConn.Close()
finDialer := net.Dialer{}
finConn, _ := finDialer.Dial("tcp", "127.0.0.1:8080")
_ = finConn.Close()
}
效果展示
可以看到第一次在ack完之后调用close直接 rst 掉了链接,第二次调用close是正常的4次挥手流程。
使用场景
在k8s中存在一个tcp探针偶发失败的问题,原因就是由于k8s管理的pod数量过多时,短时间内会进行大量的tcp探测,如果走默认的timewait默认60s关闭,会导致单台kubelet 机器上的 很多资源比如 socket, ephemeral port, conntrack entry 等等会被占用 ,导致后续探测失败,具体的issue可以参考
如果你看不到评论,那么就真的看不到评论w(゜Д゜)w