起因
项目的SpringCloud服务发现采用Consul,由于在Consul特性中自带注册节点的健康检查功能,因此默认SpringCloud集成Consul服务发现时,未启用Loadbalancer的健康检查。
但在开发过程中发现远程调用与网关路由均出现请求超时的情况,在排查问题之后对consul的loadbalancer进行了改造解决了此问题。
排查过程
1. 网关概率性的请求超时
在前端项目调用网关API时,概率发生请求超时的问题;出现此问题第一时间检查路由参数,检查日志。检查后发现网关将服务前缀路由到了一个无法到达的host,于是立即检查consul控制台,检查注册服务节点信息,发现已注册的服务均被标记为不可用,且在节点数量为1时,控制台中显示服务存在4个注册的实例,只有一个实例是健康的,因此合理推测可能是由于网关负载均衡拉取了无效的节点请求无响应导致的超时报错。
2. 为何单实例会被注册成为多个IP
可能是电脑中配置了多网卡和VPN导致Consul取到了无法使用的IP,在多次重启服务后注册的无效IP服务实例无法被移除遗留下来,最终导致大量的请求超时。
解决办法
首先想到的解决方案就是手动删除无效注册节点,让服务恢复可用;通过consul提供的REST API删除了无效的节点后,服务恢复正常,但在多次重启后依旧出现问题。因此此方案并不能解决问题。
方案2:对SpringLoadbalancer进行增强,提供对注册到Consul服务列表中不健康的节点进行过滤,避免请求到不健康的节点。
可行性分析
查看Gateway服务中源码发现,在ReactiveLoadbalancerClientFilter中,实现了对负载均衡客户端的解析与构建,在filter方法中根据serviceId获取到了对应服务的负载均衡客户端,并由客户端获取到可用的服务host表,如图所示。

跟踪choose方法,默认配置下使用Loadbalancer实现是RoundRobinLoadBalancer,默认进行轮流请求。

继续断点跟踪,来到具体的LoadBalancer实现,进入到processInstanceResponse方法中,此处传入了一个ServiceInstance集合,我们对这个集合进行分析。

在集合中可以看到,instance实现是ConsulServiceInstance,在instance中发现了对应的healthService,并且找到了checks。除了自身check之外,还有我们注册到服务的check,在check中可以看到对应的status为PASSING。


完成了可行性分析后,开始编码。
实现代码
首先依旧是参考官网案例,找到SpringCloudCommons,在Loadbalancer中有关于ServiceInstanceListSupplier的描述。
ServiceInstanceListSupplier为用户提供了对服务实例列表进行自定义操作的功能,因此只需要实现ServiceInstanceListSupplier并将其注册为Bean即可。

编写代码,由于我们仅对现有服务实例进行判断与过滤,因此在可在原有Supplier的基础上进行功能增强,这里继承DelegatingServiceInstanceListSupplier实现一个ServiceInstanceListSupplier委托。
在委托中实现对ServiceInstance不健康的节点进行过滤,当然我们这里只对ConsulServiceInstance进行处理,其他服务实例放行,交给下游提供者处理。


编写自定义Loadbalancer配置类,基于演示,这里仅配置一个简单的可验证的服务实例提供配置Bean

进行代码测试,验证问题的解决。
网友评论