AWS NLB无法转发TCP连接至EC2自定义C服务程序问题排查
问题根因
- 首先存在认知偏差:客户端打印连接对端为NLB地址是四层负载均衡的正常表现。客户端的TCP连接对等端点始终是NLB的监听地址,客户端永远不会直接和后端EC2建立TCP连接,这个打印结果无法证明NLB没有转发流量。
- 核心故障原因:测试客户端的连接逻辑不符合NLB的转发规则。你的客户端在和NLB完成TCP三次握手后,没有发送任何应用层数据,也没有等待连接状态同步,仅打印几行日志就立刻关闭套接字,整个连接生命周期不到1毫秒。AWS NLB默认不会转发这类无任何应用层载荷的超短空连接,会直接在负载均衡层终结连接,因此后端C服务收不到连接请求。
- 这也解释了为什么替换为Apache时转发正常:测试Apache时使用的curl、浏览器等客户端,都会在连接建立后立刻发送HTTP请求(携带应用层载荷),NLB识别到有效数据后才会将连接正式转发到后端实例。
- 额外隐患:服务端代码存在文件描述符泄漏问题,
accept()返回的客户端套接字从未调用close()释放,服务长时间运行后会耗尽进程可用FD,导致无法接收新连接,不过该问题在服务刚启动、连接量极低时不会触发,不是本次故障的主因。
调整方案
- 修改客户端逻辑,连接建立后不要立刻关闭:
可以在连接成功后发送一段测试载荷,或增加短暂等待,给NLB留出转发连接的时间,示例修改:
// 原有连接成功的逻辑块 else { char* address = inet_ntoa(addr.sin_addr); printf("Connection Succesful at socket %d\n", sd); printf("%s\n", address); printf("Port is: %d\n", ntohs(addr.sin_port)); // 新增:发送测试载荷,触发NLB转发 send(sd, "hello", 5, 0); // 新增:等待1秒,保证连接状态同步到后端 sleep(1); }
- 修复服务端FD泄漏问题,每次处理完连接后释放套接字:
while(1) { struct sockaddr_in addr; socklen_t len = sizeof(addr); int client = accept(server, (struct sockaddr*)&addr, &len); printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); // 新增:处理完逻辑后关闭客户端套接字 close(client); }
- 验证说明:修改后重新测试即可看到后端服务正常收到连接。如果NLB目标组为实例类型,服务端打印的客户端源IP为你的本地公网IP;如果为IP类型,服务端打印的源IP为NLB内网IP,两种情况均属于正常表现,和目标组配置有关。
内容的提问来源于stack exchange,提问作者cyborg
相关产品推荐
相关产品推荐

