美文网首页
SpringCloud @LoadBalanced注解使用报错

SpringCloud @LoadBalanced注解使用报错

作者: 左羊 | 来源:发表于2019-07-23 23:08 被阅读0次

    错误描述

    我遇到的问题是通过@Autowired注入的restTemplate无法访问IP加端口,new RestTemplate则可以访问?

    1.通过注入方式访问连接时报了以下异常。

    java.lang.IllegalStateException: No instances available for XXX

    image

    2.通过new RestTemplate则可以正常访问。

    解决方案
    首先我在config包中发现如下配置,我发现我在restTemplate模板对象上加@LoadBalanced注解

      @Bean
       @LoadBalanced
       public RestTemplate getRestTemplate(){
           return new RestTemplate();
       }
    

    为什么加上 @LoadBalanced就不能访问ip加端口呢?
    主要因为其使用ribbon组件,当我们应用依赖了eureka-client的时候,eureka-client依赖了ribbon,虽然看是ribbon没有存在感,但是ribbon默默的发挥着自己的负载均衡能力。在加了注解 @LoadBalanced 之后,我们的restTemplate 会走这个类RibbonLoadBalancerClient,serverid必须是我们访问的服务名称 ,当我们直接输入ip的时候获取的server是null,就会抛出异常。而这个服务名称是需要我们在Eureka中配置的,如果没有配置直接访问则会报错。
    RibbonLoadBalancerClient.java


    image

    通过Debug断点进入这个方法

    image
    public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
       Server server = null;
       if(serviceInstance instanceof RibbonServer) {
          server = ((RibbonServer)serviceInstance).getServer();
       }
       if (server == null) {
          throw new IllegalStateException("No instances available for " + serviceId);
       }
    
       RibbonLoadBalancerContext context = this.clientFactory
             .getLoadBalancerContext(serviceId);
       RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
    
       try {
          T returnVal = request.apply(serviceInstance);
          statsRecorder.recordStats(returnVal);
          return returnVal;
       }
       // catch IOException and rethrow so RestTemplate behaves correctly
       catch (IOException ex) {
          statsRecorder.recordStats(ex);
          throw ex;
       }
       catch (Exception ex) {
          statsRecorder.recordStats(ex);
          ReflectionUtils.rethrowRuntimeException(ex);
       }
       return null;
    }
    

    在这里我们就可以发现那个错误信息,因为没有找到相对应的服务,所以server为null,又因为server为null,所以会抛出java.lang.IllegalStateException: No instances available for XXX这个异常信息。

    有这么一句谚语说的好,“谁都会骗你,但源码是不会骗你的”。

    因为ribbon的作用是负载均衡,那么你直接使用ip地址,那么就无法起到负载均衡的作用,因为每次都是调用同一个服务,当你使用的是服务名称的时候,他会根据自己的算法去选择具有该服务名称的服务。

    结论:因为我的IP没有在Eureka中经行配置。
    为什么使用new RestTemplate则可以正常访问?
    因为添加了ribbon依赖后,会在项目启动的时候自动往RestTemplate中添加LoadBalancerInterceptor拦截器,直接new的对象没有加@LoadBalanced,通过Debug工具发现在访问时没有被该拦截器拦截。所以就能正常访问。

    总结

    1)用户创建RestTemplate
    2)添加了ribbon依赖后,会在项目启动的时候自动往RestTemplate中添加LoadBalancerInterceptor拦截器
    3)用户根据RestTemplate发起请求时,会将请求转发到LoadBalancerInterceptor去执行,该拦截器会根据指定的负载均衡方式获取该次请求对应的应用服务端IP、port
    4)根据获取到的IP、port重新封装请求,发送HTTP请求,返回具体响应

    参考:
    SpringCloud微服务实战
    https://blog.csdn.net/qq_26323323/article/details/81327669
    https://blog.csdn.net/weixin_42107940/article/details/84028403
    https://blog.csdn.net/awhip9/article/details/70599014

    相关文章

      网友评论

          本文标题:SpringCloud @LoadBalanced注解使用报错

          本文链接:https://www.haomeiwen.com/subject/yjbzlctx.html