我注意到HAProxy的探测机制是这样的:
Client -> [SYN] -> Server
Client <- [ACK/SYN] <- Server
Client -> [ACK/RST] -> Server
浏览HAProxy的源代码后,我认为他们并没有修改网络堆栈行为并在用户模式下实现这一点。所以我想这是套接字选项的一个技巧。你能给我一些提示吗?
我使用 TCP Fast Open 复制了所描述的行为。当使用
connect()
时,TCP客户端(您标记为服务器)将等待SYN-ACK并回复ACK。
这是我的例子:
#include <unistd.h>
#include <arpa/inet.h>
int main() {
int sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in sai;
sai.sin_family = AF_INET;
sai.sin_port = htons(1234);
sai.sin_addr.s_addr = inet_addr("127.0.0.1");
struct linger sl;
sl.l_onoff = 1; /* non-zero value enables linger option in kernel */
sl.l_linger = 0; /* timeout interval in seconds */
setsockopt(sock, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
int ret = sendto(sock, NULL, 0, MSG_FASTOPEN | MSG_MORE, &sai, sizeof(sai));
if(ret == -1) {
//Probing or TFO failed
//If TFO failed, try connect()
}
close(sock);
}
这实际上是一个非常合理的问题,如何为了进行健康检查而这样做。而且如何做到这一点并不明显。
我不知道如何使用正常的套接字操作获取Syn、Syn-Ack、Ack-Rst;我能想到的最接近的是打开连接,然后立即以 SO_LINGER 间隔 0 关闭它。
但是即使你可以复制它,仍然存在一个问题,即用户空间应用程序想要快速获知 Syn-Ack 是否到达(正常的 TCP 连接如果没有回复就会重传 Syn,并最终放弃)与您想要获得健康检查答案的速度相比,需要花费相当长的时间)。由于所有这些都是在内核 TCP 实现中处理的,因此我认为没有任何方法可以快速知道 Syn-Ack 已到达。这让我相信这不是通过正常的套接字操作来完成的。
我要做的是在用户空间应用程序中构造一个 Syn 数据包并使用原始套接字发送它。使用 Iptables/netfilter 将 Syn-Ack 回复发送到用户空间。如果 Syn-Ack 及时到达,您的健康检查就会通过。当它到达时,内核 TCP 堆栈也会看到它(除非您使用 Iptables/netfilter 阻止它)并立即回复重置(因为它不知道连接)。
这应该由默认选项“nolinger”控制,这将设置套接字选项SO_LINGER。当套接字关闭时检查它。你可以在内核源文件“net/ipv4/tcp.c”的“__tcp_close”中找到这样的信息