TCP框架损坏和分裂

时间:2020-01-26 14:41:18

标签: linux networking tcp kernel

我们正在负载均衡器后面运行一个用Go编写的HTTP服务器。在随机数量的请求(每几亿个请求)之后,请求以某种方式被拆分为两个数据包,第二个数据包丢失了第一个字节。

我们已经分析了服务器库,fasthttp和Go的代码,但没有发现任何可以解释此行为的信息。

接下来,我们使用tcpdump捕获了许多这样的请求,它们没有被拆分-整个请求都在一个TCP数据包内,而且看起来正确。

仍然,Go服务器认为有两个数据包,并且strace确认套接字已被读取两次:

2289 23:09:37.239558 read(212, "GET /json HTTP/1.1\r\nContent-Type: application/x-stream\r\nAccept: application/x-stream\r\nAccept-Encoding: gzip\r\nUser-Agent: Java/Android\r\nConnection: Keep-Alive\r\nKeep-Alive: 5000\r\nHttp-version: HTTP/1.1\r\nHost: <snipsnip>\r\nX-TCPI: 91", 4096) = 229
32289 23:09:37.239620 read(212, "217.26.11   \r\n\r\n", 3867) = 16 

所以我们认为这是负载均衡器,在这种情况下,tcpdump会以某种方式修改数据包以使其看起来正确,或者是内核。

我们下一步应该去哪里?

1 个答案:

答案 0 :(得分:3)

还是,Go服务器认为有两个数据包...

这是错误的解释。 Go服务器从TCP套接字读取。 TCP套接字没有数据包的概念,它只能看到字节流。字节流在传输过程中的打包方式完全无关紧要,即,单个读取可能是单个数据包或多个数据包甚至是半个数据包等的结果。类似,TCP堆栈可能会将多个写入操作写入单个数据包或可能还会将一次写入分散到多个数据包中。

一个假设将完成特定打包,一次读取将与单个数据包匹配或在发送方进行写操作而导致在接收方进行完全读取的应用程序是错误的。它可能在大多数时候都能正常工作,但是如果在不同的环境下使用,机器上的负载更多,套接字缓冲区的大小不同等情况下,它可能会突然崩溃。这种错误的假设通常会导致难以触发和难以调试的问题。

...请求以某种方式被拆分为两个数据包,第二个数据包缺少第一个字节。 ...我们下一步应该看什么?

请检查数据包是否在数据到达服务器之前丢失了此字节,或者该字节是否在代码中。根据数据损坏的位置,您要么需要修复代码,要么需要修复服务器前的任何内容并破坏数据。甚至可能是一个坏客户。您可以通过策略性地在网络中的各个位置进行数据包捕获来缩小问题原因。