Spring Cloud Feign启动Load balancer does not have available server for client分析

项目场景:

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对象中的到所有的服务, 然后根据规则从服务中获取一个匹配的服务(相关规则信息可以查看文章 Spring Cloud Ribbon负载均衡策略详解_学然后知不足!-CSDN博客

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. Feign 启动后还没有从注册中心得到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. 其他未知原因,只能通过断点调试了

   

   上一篇:Spring Cloud Feign 示例笔记

猜你喜欢

转载自blog.csdn.net/Beijing_L/article/details/121385089