项目场景:
Spring Cloud Feign 企业网站定制开发学习过程中遇到Feign企业网站定制开发访问的时候报错Load balancer does not have available server for client
问题描述:
企业网站定制开发完整报错日志如下
- path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: MS-CUSTOMER] with root cause
-
- com.netflix.client.ClientException: Load balancer does not have available server for client: MS-CUSTOMER
- at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.2.2.jar:2.2.2]
- at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.2.2.jar:2.2.2]
- at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.2.2.jar:2.2.2]
- at rx.Observable.unsafeSubscribe(Observable.java:10211) ~[rxjava-1.1.10.jar:1.1.10]
- at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.1.10.jar:1.1.10]
- at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.1.10.jar:1.1.10]
- at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.1.10.jar:1.1.10]
- at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.1.10.jar:1.1.10]
原因分析:
企业网站定制开发从报错信息上看Load balancer does not have available server for client: MS-CUSTOMER,表示负载均衡器没有找到有效的服务信息,此异常是 LoadBalancerContext从LoadBalancer对象中获取服务的时候, 没有找到所调用的服务信息
- Server svc = lb.chooseServer(loadBalancerKey);
- if (svc == null){
- throw new ClientException(ClientException.ErrorType.GENERAL,
- "Load balancer does not have available server for client: "
- + clientName);
- }
我们查询lb.chooseServer 实现的功能其实是从LoadBalancer对象中的到所有的服务, 然后根据规则从服务中获取一个匹配的服务(相关规则信息可以查看文章 )
List<Server> serverList = getLoadBalancer().getAllServers();
再看一下getAllServer接口,这个接口返回的是可用的和非可用的服务
- /**
- * @return All known servers, both reachable and unreachable.
- */
- public List<Server> getAllServers();
查看BaseLoadBalancer 如何实现的此方法发现, 服务取自List<Server>集合
- protected volatile List<Server> allServerList = Collections
- .synchronizedList(new ArrayList<Server>());
-
- @Override
- public List<Server> getAllServers() {
- return Collections.unmodifiableList(allServerList);
- }
-
- /**
- * Add a server to the 'allServer' list; does not verify uniqueness, so you
- * could give a server a greater share by adding it more than once.
- */
- public void addServer(Server newServer) {
- if (newServer != null) {
- try {
- ArrayList<Server> newList = new ArrayList<Server>();
-
- newList.addAll(allServerList);
- newList.add(newServer);
- setServersList(newList);
- } catch (Exception e) {
- logger.error("LoadBalancer [{}]: Error adding newServer {}", name, newServer.getHost(), e);
- }
- }
- }
-
- /**
- * Add a list of servers to the 'allServer' list; does not verify
- * uniqueness, so you could give a server a greater share by adding it more
- * than once
- */
- @Override
- public void addServers(List<Server> newServers) {
- if (newServers != null && newServers.size() > 0) {
- try {
- ArrayList<Server> newList = new ArrayList<Server>();
- newList.addAll(allServerList);
- newList.addAll(newServers);
- setServersList(newList);
- } catch (Exception e) {
- logger.error("LoadBalancer [{}]: Exception while adding Servers", name, e);
- }
- }
- }
解决方案:
从上面的源码分析可知,请求处理时候会从会先从一个缓存集合List<Server> 中得到可用和不可用的服务,然后根据规则Rule 过滤获可访问的服务信息并返回Server,那么获取Server为空引起报错的可能场景有如下几个
1. 启动后还没有从注册中心得到List<Server> 的时候lb#chooseServer获取结果为空
第一种可能:客户端没有开启从Eureka 中获取服务列表 ,所以需要检查配置是否错误配置成了 false
- eureka.client.register-with-eureka=true
- eureka.client.fetch-registry=true
第二种可能: 客户端已经开启了从注册中心获取服务列表, 获取列表是通过任务获取,此时任务还没执行List<Server>为空, 只需要等一会应用从注册中心检索服务后,问题会自行解决。
第三种可能: 检索后发现没有得到服务, 此时很有可能是你访问的服务应用没有正确注册到注册中心引起, 正确注册后Eureka注册中心可以正确看到发布的服务信息,发布失败一般也是上面的配置出错或其他配置问题
2. 根据Rule规则过滤从List<Server>中没有找到可用的Server
第一种可能: 因为getAllServers()返回的是可用和非可用, Rule规则会过滤到可用, 所以如果你的服务已经Down那么会找不到服务, 可以通过健康检查查看服务状态是否正常
第二种可能: 检查Fegin接口配置的服务名称和要访问的服务名称是否相同,注意字母顺序
3. 其他未知原因,只能通过断点调试了