记一次应用服务调用超时问题的排查!

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

上周线网服务遇到了这样的一个现象:搜索服务的接口偶尔出现超时60多秒之后才返回的现象, 但超时后返回的结果是正确的。虽然是偶尔,但是这偶尔在网站页面发起的请求看起来像是系统卡死,会觉得搜索服务不可用了。

那么,这个问题应该怎么解决?又是什么原因造成的呢?

快速解决当务之急:应用层的控制

不管是什么原因造成的,首先应该解决当务之急,也就是超时太久的问题。

我们网站的Web后台服务使用的是Spring 框架,使用Open Feign微服务框架作为调用搜索服务的http客户端,整体拓扑结构如下图所示。

topo.png

使用的Open Feign的版本是:

<dependency>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <groupId>org.springframework.cloud</groupId>
    <version>3.0.4</version>
</dependency>
复制代码

因为Open Feign客户端默认的超时时间read-timeout是60s,所以超时60多秒之后,客户端会重新发起一个TCP连接。超时后重新发起连接,而重新发起的连接在几十毫秒内正确的返回了结果。

这种偶然超时的现象,可能是网络的某个环节出现了异常,但是目前还不清楚是哪个环节出现了问题。

所以我想,“既然Open Feign应用层能够控制超时重试,何不把超时重试的时间设置的短一些,因为超时是偶尔发生的,很大概率上,下一次连接重试的时候不会超时了,能很快的返回结果。”

为了快速的解决这个问题,我修改了配置,将 Feign 的searchClient超时重试设置为5s,如下,

feign:
  client:
    config:
      searchClient:
        read-timeout: 5000
复制代码

重启了web服务之后,虽然系统偶尔还是会发出搜索服务2S多的超时告警,但是这已经比配置修改之后好多了,系统看起来再没有卡死的情况,只是在偶然超时情况下响应稍微有点慢

排查:会是传输层和网络层的问题吗?

解决了当务之急后,还是需要找出偶尔超时的根本原因。

为什么会偶然超时呢?是网络质量问题还是其他什么原因?那就上手抓包看看。

使用命令tcpdump -A host search.svc.com来抓包,search.svc.com是搜索服务的host name,也是搜索服务的负载均衡服务器所在的域名。 下面分两种情况分别抓包并分析:

  • 一是正常响应的抓包和过程分析
  • 二是超时响应的抓包和过程分析

有正常响应的过程分析

正常响应的抓包非常简单,我们来看一下。

10:21:09.661932 IP local.44138 > search.svc.com: Flags [S], seq 2794259615, win 65535, options [mss 1460,sackOK,TS val 3974284369 ecr 0,nop,wscale 11], length 0
E..<.n@[email protected].........................
10:21:09.690231 IP search.svc.com > local.44138: Flags [S.], seq 3616653096, ack 2794259616, win 65535, options [mss 1424,sackOK,TS val 2325972550 ecr 3974284369,nop,wscale 11], length 0
E..<[email protected]...(...................
...F...Q....
10:21:09.690250 IP local.44138 > search.svc.com: Flags [.], ack 1, win 3072, options [nop,nop,TS val 3974284397 ecr 2325972550], length 0
E..4.o@[email protected].........)...........
...m...F
10:21:09.690341 IP local.44138 > search.svc.com: Flags [P.], seq 1:692, ack 1, win 3072, options [nop,nop,TS val 3974284397 ecr 2325972550], length 691: HTTP: GET 
...

10:21:09.718543 IP search.svc.com >local.44138: Flags [.], ack 692, win 3072, options [nop,nop,TS val 2325972579 ecr 3974284397], length 0
[email protected].^....p.......j...)...S.....].....
...c...m
10:21:09.752510 IP search.svc.com > local.44138: Flags [P.], seq 1:419, ack 692, win 3072, options [nop,nop,TS val 2325972613 ecr 3974284397], length 418: HTTP: HTTP/1.1 200 OK
[email protected].]X...p.......j...)...S.....h.....
.......mHTTP/1.1 200 OK
...
10:21:09.752519 IP local.44138 > search.svc.com: Flags [.], ack 419, win 3072, options [nop,nop,TS val 3974284459 ecr 2325972613], length 0
E..4.q@[email protected]...............
10:21:14.826969 IP local.44138 > search.svc.com: Flags [F.], seq 692, ack 419, win 3072, options [nop,nop,TS val 3974289534 ecr 2325972613], length 0
E..4.r@[email protected]...............
...~....
10:21:14.855288 IP search.svc.com > local.44138: Flags [F.], seq 419, ack 693, win 3072, options [nop,nop,TS val 2325977715 ecr 3974289534], length 0
[email protected].^....p.......j.......T...........
...s...~
10:21:14.855304 IP local.44138 > search.svc.com: Flags [.], ack 420, win 3072, options [nop,nop,TS val 3974289562 ecr 2325977715], length 0
E..4.s@[email protected]...............
.......s
复制代码

为了便于讲解,这里的客户端IP使用local替代,服务端依旧使用search.svc.com替代。可以看出整个传输的数据也很明朗,如下:

  • 最前面三次握手,TCP连接建立,对应的客户端的端口是44138
  • local发出 HTTP GET请求
  • GET请求的ack返回给local
  • search.svc.com发出Respone 200的数据
  • local回应收到Response的ack
  • 后面四次挥手,44138端口对应的连接最终正常关闭了。

客户端从10:21:09.690341时间点发出请求到10:21:09.752519时间点接收到响应,一共只花了约60ms,响应的速度是满足预期的

超时响应的过程分析

超时响应的抓包需要耐心的等待并过滤处理一下,抓到的包如下所示,我们来看一下。

10:21:09.645045 IP local.44136 > search.svc.com: Flags [S], seq 3363447430, win 65535, options [mss 1460,sackOK,TS val 3974284352 ecr 0,nop,wscale 11], length 0
E..<.k@[email protected]"....................
...@........
10:21:09.673728 IP search.svc.com > local.44136: Flags [S.], seq 2643294393, ack 3363447431, win 65535, options [mss 1424,sackOK,TS val 1194261969 ecr 3974284352,nop,wscale 11], length 0
E..<[email protected]"......9.........
G......@....
10:21:09.673758 IP local.44136 > search.svc.com: Flags [.], ack 1, win 3072, options [nop,nop,TS val 3974284380 ecr 1194261969], length 0
E..4.l@[email protected]"...x............
...\G...
10:21:09.673866 IP local.44136 > search.svc.com: Flags [P.], seq 1:637, ack 1, win 3072, options [nop,nop,TS val 3974284380 ecr 1194261969], length 636: HTTP: GET 
...
10:21:09.702373 IP search.svc.com > local.44136: Flags [.], ack 637, win 3072, options [nop,nop,TS val 1194261997 ecr 3974284380], length 0
[email protected]%......2.....
G......\

10:21:14.675216 IP local.44136 > search.svc.com: Flags [F.], seq 637, ack 1, win 3072, options [nop,nop,TS val 3974289382 ecr 1194261997], length 0
E..4.n@[email protected]%...x............
....G...
...

复制代码

由于内容比较多,有些地方省略了,这个过程是这样的

  • 最前面三次握手,TCP连接建立,对应的客户端的端口是44136
  • local发出 HTTP GET请求
  • GET请求的ack返回给local
  • local没有收到response,5S超时后发出FIN包,连接关闭 local后面会和search.svc.com建立新的连接,并再次发出请求,得到正确的Response。

local发出的http GET请求能很快的收到对应的回执ACK包,这说明网络接口层是没有问题的。一收一发都有了,说明从local到search.svc.com的网络的接收和发送都没有问题,可以排除这段网络异常的问题。

search.svc.com域名所在的网关对应的是负载均衡的服务,会将请求转发给后端的搜索服务,肯定是这个过程哪里出现了异常!

另外,继续查找监控和日志,发现搜索服务后端确实没有收到这个超时的请求。 那问题只能出在负载均衡服务了,负载均衡服务的内核已经回复了客户端ACK,却没有将请求正确的转发给后端。

problem.png

所以我们终于找到问题出在哪里了,最终我们只要排查下负载均衡服务为什么没有将请求正确的转发给后端就可以了。

这里负载均衡服务是使用云服务,云服务出现问题,可见服务提供商提供的服务并不总是那么靠谱,只能找他们的客服咨询了。

小结

网络问题有时候让人摸不着头脑,但是并不可怕,只要仔细分析,必然能找到问题的原因。 应用层出现网络问题时,我们先排查应用层的问题,尽量的规避问题,再查看底层网络或者对方是否存在问题,最终找到问题的根本原因!

猜你喜欢

转载自juejin.im/post/7131668824239603725