目录
背景
通过Spring Cloud组件构建的服务集群(zuul网关),在第一次请求时经常会出现timeout的情况,然而第二次就正常了。
通过查看链路监控,发现第一次耗时是第二次的10多倍。在超时时间设置较短的情况下,第一次请求很容易会出现超时。经过分析很有很能是由于cloud配置懒加载造成,去官网翻看了下文档,zuul网关和各个调用服务之间的Ribbon进行客户端负载均衡的Client并不是在启动时就加载好,而是在第一次调用时懒加载配置,导致第一次的请求调用包括了创建Ribbon Client的时间。
通过启动日志信息就可以发现:
1. ribbon的饥饿加载
经过研究发现:使用Ribbon进行负载均衡的服务实例,是在调用时才会去创建对应的服务实例,并不是在服务启动时进行初始化。
所以第一次调用耗时不仅仅包含发送HTTP请求的时间,还包含了创建Ribbon Client时间,如果创建速度较慢,同时设置的请求超时又比较短,很容易就会出现超时情况。 在官网可以看到如下的配置说明:
Each Ribbon named client has a corresponding child Application Context that Spring Cloud maintains, this application context is lazily loaded up on the first request to the named client. This lazy loading behavior can be changed to instead eagerly load up these child Application contexts at startup by specifying the names of the Ribbon clients.
意为Spring Cloud为每个Ribbon客户端维护了一个子应用环境的上下文,应用的上下文在第一次请求到指定客户端的时候懒加载。可以通过如下配置进行修改:
ribbon:
eager-load:
enabled: true
clients: client1, client2, client3
按照如上的配置之后,服务启动时Ribbon客户端就会加载。
2. zuul网关的饥饿加载
Ribbon客户端启动时饥饿加载解决了。网关作为对外请求的入口,zuul内部使用Ribbon调用其他服务,Spring Cloud默认也是在第一次调用时懒加载Ribbon客户端。zuul同样需要维护子应用环境的上下文,同样需要配置饥饿加载。
Zuul internally uses Ribbon for calling the remote url’s and Ribbon clients are by default lazily loaded up by Spring Cloud on first call. This behavior can be changed for Zuul using the following configuration and will result in the child Ribbon related Application contexts being eagerly loaded up at application startup time.
具体配置如下:
zuul:
ribbon:
eager-load:
enabled: true
3. 总结
修改完成后再次重启服务,超时情况得到改善,请求多次未出现超时情况。
本文记录Spring Cloud的服务第一次请求超时的优化方式。
排查结果是因为Ribbon客户端懒加载造成,然后分别对zuul网关和服务之间调用的Ribbon客户端进行配置,使其在启动时加载好Ribbon客户端。
附:zuul网关相关的超时配置项
1、hystrix超时时间配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=20000
2、ribbon超时时间配置
# 连接超时时间
ribbon.ConnectTimeout=1000
# 最终设置的是SocketTimeout 2个数据包之间的传输超时时间
ribbon.ReadTimeout=10000
3、feign超时时间配置
feign.client.config.default.readTimeout=20000