-
为什么会出现随机 504,408 错误代码?(二)
问题描述
在第一篇中讲述了这个问题的具体描述,这里不再赘述。由于这个问题比较复杂,在第一篇中分析出了,我们的 golang 程序编写存在问题,在 server 端断网的情况下,没有做特殊处理,导致 TCP 的发送缓冲区满了,进而导致死锁的情况。但是由于我们前面有 istio gateway,并且设置了 timeout 为 7s 钟,所以当请求在程序中死锁的情况下,最终会导致 istio timeout。所以这里会导致返回 504 gateway timeout
的情况,在这种情况下,回复 504 是期望的,但是回复 408 request time
,这里就有些疑问了。这篇将分析为什么 istio 会随机回复 408 的情况。
这里我们用的 istio 基于 1.8 的版本。
问题复现
这里复现则比较简单了,因为我们知道了程序异常的条件是死锁了,于是我在程序中直接 sleep 一段时间(大于在 VS 里面设置的 timeout 值),这样就可以复现问题。果然,当我用 h2load 通过 istio gateway 发送请求时,就会出现线上的问题,istio 回复了 408,504 的错误代码。这里其实为了对比,我还直接向服务发请求,这个时候,和之前的测试是一样的结果,并没有什么错误代码回复。从而可以肯定是 istio gateway 出现了问题。
问题分析
这里分析起来还是挺难的,毕竟涉及到了 istio 的处理,开始没什么头绪,然后咨询了一些人,刚好有个朋友有过类似的问题,他提出了 envoy 的 max_stream_duration 的参数,后来我在 envoy 代码里面看到这部分的处理,看到这种情况下这个时间超时的处理,正是回复 408 downstream request timeout
,和我们这个情况很类似,于是这给我了一个分析方向,从结果看,这也是个正确的方向。
既然知道和 envoy 这个 max_stream_duration 字段值有关,于是,首先我通过 istioctl pc route
查看了我本地环境上的 service 的 route 配置信息,看到我们的配置是 maxStreamDuration 和 timeout 设置的是同一个值,这和我们的期望很显然不符,因为我们根本没有设置 maxStreamDuration 这个值,并且 istio 也没有提供某个字段可以设置这个值。
结合 istio 的 route 配置看到对应的参数值,然后看了下对应版本 envoy 这部分的处理 onStreamMaxDurationReached
,源码在 source/common/http/conn_manager_impl.cc
文件下,它则是根据这些配置开启不同的 timer,如果 expire 了,则回复对应的错误代码,而对于 stream 超时的情况,则回复 408,对于 timeout 超时,则回复 504。
从这里我试着猜想了一下,如果当我把 max_stream_duration 的值设置小于 timeout,按照 envoy 逻辑,则会全部回复 408;当大于 timeout 的时候,则会全部回复 504。然后我试着去修改这个 maxStreamDuration 的值,去测试我的猜测。
我通过 envoyfilter 去修改这个参数,例如下面内容,这是修改 max_stream_duration 为 100s,去模拟大于 timeout 的情况。
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: testenvoyfilter
namespace: default
spec:
workloadSelector:
labels:
app: istio-ingressgateway
configPatches:
- applyTo: HTTP_ROUTE
match:
context: ANY
routeConfiguration:
vhost:
name: "*:8082"
route:
action: ANY
patch:
operation: MERGE
value:
route:
timeout: 7s
max_stream_duration:
max_stream_duration: 100s
经过设置不同的值,测试结果和我的猜想完全符合。于是可以断定,这个 408 的原因,就是这个 max_stream_duration 所致。
那为什么 istio 会自己给我们添加这个值呢,并且还是等于 timeout 的值。
这里可以看一下对应的提交。首先我们可以看到 istio 的这个提交 set max stream duration to the value specified in VS,它将 MaxStreamDuration 设置成了 Timeout 的值,所以这里也能够解释上面我环境里的问题,为什么我没有设置这个值,但是 istio 的 route 配置数据里面是有的,并且和 Timeout 的值是一样的。
但是他们后来测试发现了问题 Regression: istio.io egress test case gets 408 instead of 504,于是做了一轮讨论下来,发现有问题,最后他们又把这个部分 revert 了,可以参考这个提交 remove max stream duration。
这其中 istio 的人去 envoy 搞了个 PR honour routes timeout if max stream duration is set with out stream duration,但最后分析下来,这个是不对的,又将其 revert 出来了,参考这个提交 Revert “honour routes timeout if max stream duration is set with out …。
总之,他们对这个折腾了一番,然后 istio 觉得还是不设置 MaxStreamDuration 这个值。然后再 1.10 版本修复这个问题,具体可参考这个提交 [release-1.10] revert to use deprecated values for timeouts。
解决方案
目前没有什么好的解决方案,因为我们不想因为这个问题,用 envoyfilter 去修改这个值,而 istio 对于这个问题会在 1.10 版本才会修复。所以看后面我们自己的 mesh 同志们会怎么处理。
总结
对于这个问题,其实分析起来还是有点困难的,因为一般想不到那方面去,并且,我们这边对 mesh 也不是太了解。这次分析也是朋友提供了很有用的信息,才能朝着这个方向分析了下来。不然毫无可能怀疑到这上面来,更不能顺利找到这个问题的根本。