今年工作中发生的一个问题,因为太简单了,觉得不值得记录。今天读 plantegg 的一篇文章,想起来这件事。技术上很简单,但是故事本身还是挺有意思的。这里尽量客观的记录一下事情经过,因为是当事人,就不做评论了。
故事的起因是,我们提供了一个 HTTP 服务,给不同于我们部门的团队使用,这个服务有些复杂,它本身提供的是 gRPC 服务,但是我们为了给外部不同技术栈的团队使用,做了一个 HTTP 转 gRPC,其他的团队通过公网调用这个 HTTP 服务。
HTTP 再前面就是公司的通用网关了,所以集团外其他用户访问我们的服务链路是 公网 -> 4层网关 -> 7层网关 -> HTTP 转 gRPC 服务 -> 服务本身。
然后有一个 BU,他们说调用我们的服务请求并发提不上去,原因是他们那边的 NAT 端口耗尽了。从他们那边访问这个服务的出口是 客户端 -> NAT 设备 -> 公网 -> … 因为我们只在公网上暴露了 2 个 IP,TCP 的五元组里面 4 个基本已经固定了,2IP+协议+目的端口,所以只有他们 NAT 的端口是一个变量,很快就到了瓶颈。
于是他们工单给我们,要求我们在公网上暴露第二个 IP,以便可以支持更多的 TCP 连接。我们内部讨论之后拒绝了,要求他们使用 HTTP 长链接来调用,而不是短连接。因为他们是作为客户端连续并发调用多次请求,完全是长链接的场景。
第一天,他们测试使用长链接,但是 QPS 高不上去,甚至比原来还低。然后他们让我查一下这个链路上支持不支持长链接,是不是我们的配置有问题。我明确回复支持。
然后他们要求我抓一下网关的包,确认可以支持长链接。我拒绝了。表示对方要先证明不支持长链接,我再去排查。
然后他们继续找经过的中间件团队,要求他们挨个检查是否中间有丢失信息。群里已经有20多个人了,包括对方自己的 NAT 团队,我们的网关团队,我们的 gRPC 团队和服务团队。
第二天,依然要求我们这边去抓包。我依然拒绝在对方没有证明我们这边存在问题的情况系去帮忙排查。然后提供了一个 curl,这个 curl 可以使用同一个 tcp 连接发送 3个请求,可以明确证明链路上都是支持长连接的。命令和输出大体如下:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
curl \ -w "\nusing %{local_ip}:%{local_port} %{method} %{remote_ip}:%{remote_port}\n" --request GET \ --url http://httpbin.org/headers \ --header 'Content-Type: application/json' \ --data '{"N": 8}' \ --next \ -w "\nusing %{local_ip}:%{local_port} %{method} %{remote_ip}:%{remote_port}\n" --request GET \ --url http://httpbin.org/headers \ --header 'Content-Type: application/json' \ --data '{"N": 8}' \ --next \ -w "\nusing %{local_ip}:%{local_port} %{method} %{remote_ip}:%{remote_port}\n" --request GET \ --url http://httpbin.org/headers \ --header 'Content-Type: application/json' \ --data '{"N": 8}' { "headers": { "Accept": "*/*", "Content-Length": "8", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/7.85.0", "X-Amzn-Trace-Id": "Root=1-63a52ca9-5d0cbad02e0d159c38af96e5" } } using 10.22.76.27:60506 GET 52.45.51.124:80 { "headers": { "Accept": "*/*", "Content-Length": "8", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/7.85.0", "X-Amzn-Trace-Id": "Root=1-63a52ca9-69e5c3ee65d60eca0d169e91" } } using 10.22.76.27:60506 GET 52.45.51.124:80 { "headers": { "Accept": "*/*", "Content-Length": "8", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/7.85.0", "X-Amzn-Trace-Id": "Root=1-63a52caa-31db64a8358426fa1d0fe28a" } } using 10.22.76.27:60506 GET 52.45.51.124:80 |
但是并没有人去运行,这个群里多了很多级别更高的人物。
第三天一早,群里就要开会,拉了很多大佬,要求我加入帮忙排查,我依然拒绝了,我已经证明我们这边是没有问题的,如果要我排查我们的问题,需要对方先证明我们这边存在问题。然后让对方跑一下我昨天发的 curl ,看一下长连接到底可以不可以用。
有人去他们的程序运行的环境中跑了一下,从这个结果可以证明,所有的中间件都没有问题,大概率是他们的程序代码有问题。
下午,定位到 HTTP SDK 的客户端的参数用错了,程序会频繁关闭 TCP 连接。
我天你太耐丝了,都把命令喂到嘴边了,只要张嘴就行…
很有意思的经历
靠别人来 troubleshoot 的码农太多了
我的理解,长链接本身和 QPS 上不去没有直接关系,要看是不是支持并发的请求,就是 pipeline 这个功能点
文中提到了,并发上不去是因为被端口号限制住了,端口号耗尽是因为没有使用长连接。
因为 timewait 吗?
一般来说,是服务端关闭连接。我测下来,这种情况下,不会有“端口号耗尽”这个问题。服务端会放弃之前的 TW 连接,建立新的连接。
你们有这个问题,是因为客户端关闭了连接?那要查一下为什么了,而不仅仅是短连接问题。