一种在网络层清理机器假死时TCP连接的方案介绍

简介:

一、背景

  假如应用服务器A上有若干模块连接某数据库服务机器B,当B异常假死,需要将B的请求切换到备份系统,这样已经建立的连接就遗留了下来。如果A上hang住的连接占用的服务线程较多,就可能造成业务系统受到影响,因此需要即时清理掉hang住的连接。

二、问题分析

  出现问题时需要快速释放hang住的连接,我们可能用那些方式来解决呢?

  1,重启假死的机器:受限于响应速度、需要承担重启机器的风险

  2,重启程序:能快速中断hang住的连接,但是

  多类模块(php、java、C、script等)连接数据库,重启方式各异;

  部分模块需要加载数据,启动时间长;

  如果模块同时重启,服务可能会受影响;

  部分模块重启后cache失效,对性能有影响;

 

  3,网络层清理

  根据netstat命令或/proc/net/tcp找到所有与B有关的连接,通过iptables封禁。由于iptables需要包驱动,B假死时iptables不能正常工作,同时A--->B除了TCP重传和心跳检测外,应用层一般为同步读写模式,不保证有包交互;同时长短连接并存,以短连接为主,tcp_keeplive心跳检测特性不能利用。

  4,总结

  hang住的连接主要处于连接建立和ESTABLISHED阶段,建立连接时程序可以配置连接超时,处于连接建立阶段B--->A的包如果没有收到A会重传,这样主要解决机器A上处于阻塞读的连接即可。除了重启程序还有以下方案:

  a,配置读写超时,libmysqlclient默认为1年,应用上有业务需要执行几秒到几百秒,由于系统性能波动,偶尔会有sql执行时间较长,应用不好配置这个值,时间配置太大没有意义。

  b,外部构造RST包关闭连接,由于安全原因RST包需要携带正确的seq,因此需要先记录包序。

  c,使用iptables等工具需要有A ---> B的包驱动

  d,内核API

  问题抽象为:在机器B假死时,如何产生一个A ---> B的数据包

 

三、实现方案

  1,如果是SYN_SEND状态

  tcp本身有重传,线上重传参数有优化;复用重传数据包中的seq 构造一个 [R.] seq=0 ack=seq+1的包就可以关闭连接;其它方式(配置较小的连接超时,handoff对新连接会自动转向)

 

 

  2,如果是ESTABLISHED状态,大部分连接处在这种状态

  在 RFC793(TCP/IP协议)里找到这么一段:

If an incoming segment is not acceptable,an acknowledgment should be sent in reply (unless the RST bit is set, if so drop the segment and return).

内核代码net/ipv4/tcp_input.c中,有具体实现:

static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,

struct tcphdr *th, int syn_inerr)

{ …

省略…

/* Step 1: check sequence number */

if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {

/* RFC793, page 37: "In all states except SYN-SENT, all reset

* (RST) segments are validated by checking their SEQ-fields."

* And page 69: "If an incoming segment is not acceptable,

* an acknowledgment should be sent in reply (unless the RST

* bit is set, if so drop the segment and return)".

*/

if (!th->rst)

tcp_send_dupack(sk, skb);

goto discard;

}

…省略…

}

  因此,可以在A机器上模拟B给A发送一个不带RST标识、seq错误的数据包, A在接收到数据包后会回复一个用于ACK包,此时可以用iptables或其它方式捕获包上正确的seq,发送RST关闭连接,示例图如下:

 

  3,其它状态暂未处理(强制关闭连接的影响、非syn_send,established状态的处理)

 

 

 

四、具体实现

  环境机器A:192.168.1.4;

  环境机器B:192.168.1.13 ;

  端口3306

  方案1:使用iptables回复reset

  1,通过/proc/net/tcp(或netstat –nat|grep ESTABLISHED)获取所有A ---> B的连接

  2,添加iptables规则iptables -A OUTPUT -p tcp -d 192.168.2.13 --dport 3306 -j REJECT --reject-with tcp-reset

  3,对每一个连接,伪造B ---> A数据包hping3 --numeric --spoof 192.168.2.13 --destport <lport> --baseport 3306 --fin 192.168.2.4 #sendip -p ipv4 -is 192.168.2.13 -p tcp -ts 3306 -td <lport> -tff 1 -tfs 0 -tn 0 192.168.2.4

  4,删除iptables规则iptables -D OUTPUT -p tcp -d 192.168.2.13 --dport 3306 -j REJECT --reject-with tcp-reset

  方案2:

  1,通过/proc/net/tcp获取所有A ---> B的SYN_SENT或ESTABLISHED连接

  2,通过libpcap获取B交互的数据包副本,对匹配A ---> B包,回复RST,关闭连接;发送A--->B的fake FIN包,如果B协议栈还能工作,会回复ack;由于A的端口已被关闭,A上的协议栈会回复RST,关闭B--->A的半连接,包示例

10:08:06.338131 IP 192.168.2.10.43376 > 192.168.2.1.80: Flags [S], seq 1586426180, win 14600, options [mss 1460,sackOK,TS val 51262745 ecr 0,nop,wscale 5], length 0

10:08:06.338500 IP 192.168.2.1.80 > 192.168.2.10.43376: Flags [S.], seq 1103847096, ack 1586426181, win 8192, options [mss 1460,nop,wscale 8,sackOK,TS val 31578197 ecr 51262745], length 0

10:08:06.338614 IP 192.168.2.10.43376 > 192.168.2.1.80: Flags [.], ack 1, win 457, options [nop,nop,TS val 51262745 ecr 31578197], length 0

10:08:13.450742 IP 192.168.2.1.80 > 192.168.2.10.43376: Flags [F], seq 0, win 512, length 0

10:08:13.450766 IP 192.168.2.10.43376 > 192.168.2.1.80: Flags [.], ack 1, win 457, options [nop,nop,TS val 51264523 ecr 31578197,nop,nop,sack 1 {3191120200:3191120201}], length 0

10:08:13.452394 IP 192.168.2.1.80 > 192.168.2.10.43376: Flags [R], seq 1103847097, win 512, length 0 10:08:13.453823 IP 192.168.2.10.43376 > 192.168.2.1.80: Flags [F], seq 0, win 512, length 0

10:08:13.454021 IP 192.168.2.1.80 > 192.168.2.10.43376: Flags [.], ack 1, win 260, options [nop,nop,TS val 31578908 ecr 51264523], length 0

10:08:13.454039 IP 192.168.2.10.43376 > 192.168.2.1.80: Flags [R], seq 1586426181, win 0, length 0

  对1的每一个连接,模拟B给A发送数据包,驱动2执行。

 















本文转自百度技术51CTO博客,原文链接:http://blog.51cto.com/baidutech/748596,如需转载请自行联系原作者

相关文章
|
15天前
|
传感器 运维 物联网
蓝牙Mesh网络:连接未来的智能解决方案
蓝牙Mesh网络:连接未来的智能解决方案
94 12
|
1天前
|
域名解析 存储 网络协议
TCP套接字【网络】
TCP套接字【网络】
18 10
|
1月前
|
网络协议 C语言
C语言 网络编程(十三)并发的TCP服务端-以进程完成功能
这段代码实现了一个基于TCP协议的多进程并发服务端和客户端程序。服务端通过创建子进程来处理多个客户端连接,解决了粘包问题,并支持不定长数据传输。客户端则循环发送数据并接收服务端回传的信息,同样处理了粘包问题。程序通过自定义的数据长度前缀确保了数据的完整性和准确性。
|
1月前
|
网络协议 C语言
C语言 网络编程(十一)TCP通信创建流程---服务端
在服务器流程中,新增了绑定IP地址与端口号、建立监听队列及接受连接并创建新文件描述符等步骤。`bind`函数用于绑定IP地址与端口,`listen`函数建立监听队列并设置监听状态,`accept`函数则接受连接请求并创建新的文件描述符用于数据传输。套接字状态包括关闭(CLOSED)、同步发送(SYN-SENT)、同步接收(SYN-RECEIVE)和已建立连接(ESTABLISHED)。示例代码展示了TCP服务端程序如何初始化socket、绑定地址、监听连接请求以及接收和发送数据。
|
1月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
1月前
|
网络协议 C语言
C语言 网络编程(十二)TCP通信创建-粘包
TCP通信中的“粘包”现象指的是由于协议特性,发送方的数据包被拆分并在接收方按序组装,导致多个数据包粘连或单个数据包分割。为避免粘包,可采用定长数据包或先传送数据长度再传送数据的方式。示例代码展示了通过在发送前添加数据长度信息,并在接收时先读取长度后读取数据的具体实现方法。此方案适用于长度不固定的数据传输场景。
|
1月前
|
缓存 网络协议 网络性能优化
C语言 网络编程(二)TCP 协议
TCP(传输控制协议)是一种面向连接、可靠的传输层协议,通过校验和、序列号、确认应答等机制确保数据完整性和可靠性。通信双方需先建立连接,再进行通信,采用三次握手建立连接,四次挥手断开连接。TCP支持任意字节长度的数据传输,具备超时重传、流量控制及拥塞控制机制。三次握手用于同步序列号和确认双方通信能力,四次挥手则确保双方均能完成连接关闭操作,保证数据传输的可靠性。
|
1月前
|
网络协议 C语言
C语言 网络编程(十)TCP通信创建流程---客户端
在TCP通信中,客户端需通过一系列步骤与服务器建立连接并进行数据传输。首先使用 `socket()` 函数创建一个流式套接字,然后通过 `connect()` 函数连接服务器。连接成功后,可以使用 `send()` 和 `recv()` 函数进行数据发送和接收。最后展示了一个完整的客户端示例代码,实现了与服务器的通信过程。
|
28天前
|
网络协议
网络协议概览:HTTP、UDP、TCP与IP
理解这些基本的网络协议对于任何网络专业人员都是至关重要的,它们不仅是网络通信的基础,也是构建更复杂网络服务和应用的基石。网络技术的不断发展可能会带来新的协议和标准,但这些基本协议的核心概念和原理将继续是理解和创新网络技术的关键。
65 0
|
2月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
69 0
下一篇
无影云桌面