一、背景介绍
笔者从0到1落地istio的过程中,碰到了很多问题,现将这些问题整理出来,以便后续查阅。
开始之前,笔者先介绍下,笔者落地istio的现状,应用是java应用,使用的是http协议,spring cloud框架方式,客户端主要是okhttp, 服务端是tomcat。
istiod是1.15+版本,envoy是1.20+版本,部署方式是sidecar方式和业务结合使用,流量劫持采用的是iptables,只是劫持了http流量。
特别说明:这里主要整理了流量层面的问题,并没有把iptables劫持和xds配置部分问题列进来。
二、Mesh流量接管常见问题集
- 下游接口超时与TIME_WAIT激增
· 问题描述:
Mesh接管流量后,下游调用上游接口出现超时报错,同时监控发现TIME_WAIT状态的连接数瞬时增加10倍。
· 故障原因:
业务客户端使用的OkHttp连接池配置过小。在Sidecar资源为1C/1G、业务QPS超过200的场景下,HTTP请求因获取不到可用连接而排队等待,导致延迟和超时,进而引发连接频繁关闭,产生大量TIME_WAIT。
· 解决方案:
根据业务实际流量评估,调大OkHttp客户端的连接池大小(例如调整至100以上)。
配置参数示例:
httpclient.connection-pool.max-idle = 100
- 接口平均耗时增加20ms
· 问题描述:
业务流量被Mesh接管后,部分接口的平均响应时间增加了约20ms。
此场景也可能伴随TIME_WAIT连接数增加。
· 故障原因:
业务Feign客户端的read-timeout配置值过低(如1秒)。
Mesh组件引入的微小延迟(1-2ms)触发了该超时阈值,导致连接被断开,进而引起重试或异常处理,整体耗时增加。
· 解决方案:
适当增加Feign客户端的读取超时时间配置。例如调整参数:httpclient.read-timeout-seconds = 2(单位:秒)。
- TIME_WAIT连接数飙升
· 问题描述:
Istio流量接管后,TIME_WAIT状态的连接数飙升超过10倍。
· 故障原因:
业务使用Feign默认的客户端(基于JDK HttpUrlConnection),其Keep-Alive连接池会频繁关闭连接。
如果连接缓冲队列有未消费数据,会发送RST包强制断开,产生大量TIME_WAIT。
· 解决方案:
使用Apache HttpClient替代Feign默认实现。引入feign-httpclient依赖,并配置feign.httpclient.enabled=true。
可在Envoy访问日志中验证客户端信息。
- 流量染色Header丢失
· 问题描述:
Mesh的流量染色功能失效,由Java客户端自行添加的用于染色的Header被移除。
· 故障原因:
服务依赖了Sleuth(spring-cloud-starter-sleuth)。
Sleuth会在运行时通过Java Agent技术动态替换Feign Client,导致服务治理框架无法添加染色所需的Header,染色路由功能失效。
· 解决方案:
移除或下线Sleuth及相关依赖(如spring-cloud-sleuth-zipkin)。
- 启用GZip压缩后报错400
· 问题描述:
客户端配置了GZip压缩Header,Mesh接管流量后,请求返回400错误。
· 故障原因:
业务使用了Feign默认的基于HttpUrlConnection的Client,该客户端不支持GZip压缩。
即使Header声明了GZip,数据也不会被压缩,但之前收发双方均未严格校验,故不报错。
接入Mesh后,Envoy严格遵循规范处理GZip,导致数据格式不匹配而报错。
· 解决方案:
确保进行Feign RPC调用时,使用支持GZip的客户端(如OkHttp或Apache HttpClient)。
例如启用OkHttp:feign.okhttp.enabled=true,并引入相应依赖。
- 请求报错431(Request Header Fields Too Large)
· 问题描述:
Mesh接管流量后,请求返回431状态码。
· 故障原因:
Mesh组件(如Envoy)会在请求中附加一些内部Header(总计约2KB)。
若原有请求头较大,加上Mesh新增的Header后,总大小可能超过了业务服务端的默认请求头大小限制(如Spring Cloud默认8KB,Go默认4KB)。
· 解决方案:
调大业务服务端允许的最大HTTP请求头大小。例如,对于Spring Boot应用,调整server.max-http-header-size至60KB或更大,以适应Mesh添加的Header。
- GET请求Query参数过长报错
· 问题描述:
使用Tomcat的服务,配置max-http-header-size=8k。当GET请求的Query参数较长,使得请求头总长超过约3k时,Mesh接管后报错。
· 故障原因:
Tomcat将Query String的长度计算在HTTP请求头总长度内。Mesh接管后会新增约2KB的Header,使得总长度容易超过Tomcat的默认限制(8KB)。
· 解决方案:
-
业务优化:建议将携带大量参数的GET请求改造为POST请求。
-
调整配置:调大Tomcat服务的max-http-header-size配置值,使其大于"业务原有Header + Query参数 + Mesh新增Header"的总和。
-
重复Header导致报错
· 问题描述:
Mesh接管流量后,出现大量请求报错。
· 故障原因:
调用方使用了低版本Feign的Interceptor,并设置了Content-Type头,导致请求中出现了两个值不同的Content-Type头。
Envoy遵循HTTP标准,将这两个头合并为一个不合法的值如:
"application/json,application/json;charset=UTF-8",导致后端解析失败。
· 解决方案:
-
业务改造:通过流量抓包提前摸排,禁止业务代码设置重复的Header。
-
Mesh配置:Envoy支持配置不合并重复的Header,可通过此配置临时规避。
-
大Request Body导致RT增加
· 问题描述:
业务请求的Body过大(如66KB),Mesh接管后,接口响应时间(RT)增加超过30ms。
· 故障原因:
Envoy会对较大的HTTP请求Body进行分块(chunk)处理,这个处理过程会增加整体耗时。
· 解决方案:
优化业务请求,减少Body中的无效内容(如冗余的null字符),将Body大小压缩至合理范围(如5KB左右)。
若优化空间有限,可考虑在应用层启用压缩算法。
- 偶发性RT增加30ms+
· 问题描述:
Mesh接管流量后,偶发性出现RT增加30毫秒以上的情况。
· 故障原因:
业务客户端使用的OkHttp默认未关闭TCP Nagle算法。
而Envoy的出站监听器(outbound listeners)开启了TCP Delayed ACK机制。
两者协同工作时,Nagle算法等待ACK,Delayed ACK延迟发送ACK,造成了额外的延迟。
· 解决方案:
在业务客户端的OkHttp连接配置中,显式关闭Nagle算法(设置TCP_NODELAY为true)。
- 前端请求RT明显增加(非Mesh问题导致)
· 问题描述:
前端HTTP请求流量不大,但耗时明显增加。
· 故障原因:
前端处于弱网环境,将HTTP报文拆分成多个TCP小包发送。
后端服务需要收齐所有包后才开始处理,导致RT增加。
· 解决方案:
在网关上将来自前端的多个TCP小包合并后再转发给后端。但这会将延迟转移至前端与网关之间。从根本上建议前端考虑使用QUIC等更适合弱网的协议。
- 周期性(10分钟)出现503或RT长尾
· 问题描述:
每隔约10分钟,Envoy出站连接出现超时失败,业务表现为503错误或响应时间长尾现象。
业务报错:
feign.RetryableException: Read timed out executing。
· 故障原因:
Envoy Inbound配置了max_connection_duration: 600s(10分钟),连接达到10分钟后会被强制断开,导致正在该连接上处理的请求失败。
· 解决方案:
在Envoy的相应路由配置中添加重试策略(retry_policy),针对连接重置(reset)等错误进行重试。
长期方案是统一全局配置,避免为单个应用单独配置。
- 请求处理时间存在巨大差值
· 问题描述:
业务方记录到的请求接收时间,远晚于Envoy Inbound记录的发请求时间。
· 故障原因:
业务Pod所在的宿主机(特定机型,如s2)网络吞吐性能较差,存在宿主机级别的网络问题。
· 解决方案:
更换问题Pod所在的宿主机。建议建立对宿主机网络丢包率、延迟等指标的监控,并及时替换问题机器。
- 高QPS下偶发RT陡增
· 问题描述:
当业务流量超过300 QPS时,偶发出现RT明显增加的场景。
· 故障原因:
为节省资源,Envoy Sidecar分配的资源过低(如0.5核)。
在高流量时,Envoy与业务容器在同一Pod内竞争CPU资源,导致Sidecar处理能力不足,进而引起流量延迟。
· 解决方案:
提升Sidecar容器的资源配额。例如,将CPU request和limit调整至2核,并相应调整Envoy的worker线程数量。
- 长耗时请求报错
· 问题描述:
业务处理耗时超过15秒的HTTP请求,在Mesh接管后报错。
· 故障原因:
Envoy的 upstream response timeout 默认值为15秒。如果服务端处理超过15秒未返回响应,Envoy会主动断开连接并返回超时错误。
· 解决方案:
-
业务改造:评估将超过15秒的同步HTTP调用改为异步消息队列等更合适的方案。
-
调整超时:根据业务需要,在Envoy的路由配置中适当调大timeout值(如grpc_timeout_header_max或idle_timeout)。
三、关键知识介绍
1.启用okhttp配置
feign.httpclient.enabled = false
feign.okhttp.enabled = true
-
没有显式设置上述配置时,springcloud会根据是否引入了feign-httpclient包来决定启用httpclient或默认的jdkurlconnection实现
-
okhttp客户端相关配置参数默认值
httpclient.connect-timeout-seconds = 1
httpclient.read-timeout-seconds = 1
httpclient.write-timeout-seconds = 1
httpclient.connection-pool.max-idle = 100
httpclient.connection-pool.keep-alive-duration-seconds = 300
参数介绍:
httpclient.connect-timeout-seconds 通常是一个配置参数,用于设置HTTP客户端在尝试连接到远程服务器时等待连接建立的超时时间(以秒为单位)。如果在这个指定的时间内没有成功建立连接,那么HTTP客户端通常会抛出一个异常或错误,表示连接超时。
httpclient.read-timeout-seconds 同样是一个常见的配置参数,用于设置HTTP客户端从服务器读取响应数据的超时时间(以秒为单位)。这个超时时间是从客户端发送请求并成功建立连接后,等待服务器响应数据的时间。
如果服务器在这个指定的时间内没有发送任何数据或没有发送完所有数据,HTTP客户端通常会抛出一个异常或错误,表示读取超时。
httpclient.write-timeout-seconds 这个配置参数在某些HTTP客户端库中可能用于设置发送请求体到服务器的超时时间(以秒为单位)。然而,这个参数并不是所有HTTP客户端库都提供的标准配置选项。
发送请求体(write timeout)的超时设置在某些情况下可能不太常见,因为一旦连接建立,客户端通常会尽快将请求体写入连接中。然而,如果请求体非常大或者网络条件较差,写入过程可能会受到阻塞或延迟。
httpclient.connection-pool.max-idle 通常是一个与HTTP客户端连接池相关的配置参数,用于设置连接池中允许保持的最大空闲连接数。当连接池中的空闲连接数达到这个限制时,如果再有新的连接请求并且没有可用的空闲连接,连接池可能会选择关闭一些最久未使用的空闲连 接,以便为新的连接请求腾出空间。
这个参数对于优化HTTP客户端的性能和资源使用非常重要。如果设置得太高,可能会浪费系统资源(如内存和文件描述符),因为需要维护更多的空闲连接。如果设置得太低,则在高并发请求场景下可能会导致连接池中的空闲连接被迅速用完,从而增加新连接建立的开销和延迟。
httpclient.connection-pool.keep-alive-duration-seconds 这个配置参数(尽管这不是一个标准的配置属性名,因为它可能因库或框架而异)通常用于设置HTTP连接在连接池中保持活跃(即不被关闭)的最长时间(以秒为单位)。
在HTTP/1.1中,默认情况下,连接在请求完成后会保持打开状态,以便用于后续的请求,这被称为"持久连接"(persistent connection)或"HTTP keep-alive"。但是,为了管理资源,连接池可能会决定在一段时间后关闭这些空闲的连接,即使它们仍然是"活跃"的。
httpclient.connection-pool.keep-alive-duration-seconds 这样的配置参数允许你指定连接在连接池中可以保持活跃的最长时间。一旦达到这个时间限制,连接池可能会关闭这些连接,即使它们没有被使用。
- feign相关请求参数设置
feign client 默认连接和读超时时间设置
feign.client.config.default.connectTimeout = 3000
feign.client.config.default.readTimeout = 3000
服务粒度连接超和读超时时间配置,服务粒度优先级高于上述默认配置
feign.client.config.xxxclient.connect-timeout = 200
feign.client.config.xxxclient.read-timeout = 200
参数介绍:
feign.client.config.default.connectTimeout 是 Spring Cloud 中 Feign 客户端的一个配置属性,用于设置 Feign 客户端建立与服务端连接的超时时间(ms为单位)。这个配置是在使用 Feign 进行 HTTP 远程调用时,控制 Feign 客户端在尝试与远程服务建立连接时等待的最长时间。
feign.client.config.default.readTimeout 是 Spring Cloud Feign 客户端配置中的一个重要属性,用于设置 Feign 客户端从远程服务读取响应数据的超时时间(ms为单位)。当 Feign 客户端与远程服务建立连接后,等待服务端响应数据的时间超过这个设置的值时,Feign 客户端会抛出超时异常。
feign.client.config.xxxclient.connectTimeout 是 Spring Cloud Feign 中用于配置特定 Feign 客户端(其中 xxxclient 是你定义的 Feign 客户端名称)连接超时时间的属性(ms为单位)。这个属性允许你为不同的 Feign 客户端设置不同的连接超时时间,以满足不同服务的需要。
feign.client.config.xxxclient.read-timeout 是 Spring Cloud Feign 中用于配置特定 Feign 客户端(其中 xxxclient 是你定义的 Feign 客户端名称)读取响应数据的超时时间的属性(ms为单位)。这个属性允许你为不同的 Feign 客户端设置不同的读取超时时间,以满足不同服务的需要。
- 服务端相关参数配置
长连接自动关闭相关配置
spring.cloud.ddmc.handler.periodical-connection-close.enabled = true
最大观察连接数,超出按lru失效对老连接的观察,可关联middleware.soa进行配置,application里配置无效
spring.cloud.ddmc.handler.periodical-connection-close.max-watched-connections = 10000
1个连接持续多长时间后在请求处理完毕后进行关闭,可关联middleware.soa进行配置
application里配置无效 spring.cloud.ddmc.handler.periodical-connection-close.period-seconds = 600
tomcat keep alive长连接相关配置
开启或关闭connector定制功能
spring.cloud.ddmc.server.tomcat.keep-alive-connector-customizer.enabled = true
无请求时,连接保持多长时间,单位ms,可关联middleware.soa进行配置,application里配置无效
spring.cloud.ddmc.server.tomcat.keep-alive-timeout = 60000
1个连接上最大可处理的请求数,框架默认100,公共配置默认调整为10000,可关联middleware.soa进行配置
application里配置无效 spring.cloud.ddmc.server.tomcat.max-keep-alive-requests = 10000
tomcat 其它相关参数设置
最大请求头长度,注意,tomcat的实现是requestline + header的长度都算在里面了
server.maxHttpHeaderSize = 8*1024
最大连接数
server.tomcat.maxConnections = 8192
6.envoy链接相关配置文档
https://www.envoyproxy.io/docs/envoy/latest/faq/configuration/timeouts.html
7.envoy不合并header头玩法
通过运行态修改envoy对应的配置来达到效果,这个对于无损劫持流量来说比较有用,因为你不知道业务代码实现是不是有很多不规范的东西。
Envoy.Runtime.DisableMergeHeader