今天在调试代码的时候遇到了一个远程调用失败的问题,用Feign调用服务名时,无法从服务名解析成对应的URL。FeignClient接口如下:
@FeignClient("blogservice")
public interface BlogClient {
@RequestMapping("/category/getCategoryListTest")
List<CategoryVo> getCategoryList();
@RequestMapping("/category/getNacosConfig")
NacosConfigEntity getNacosConfig();
}
按理来说,FeignClient应该将"/blogservice/category/getNacosConfig"解析为Nacos中的blogservice服务,即“http://localhost:8888/category/getNacosConfig”,而我在调用时报错“nested exception is java.net.UnknownHostException”,且调用的URL显示的是服务名称,而不是真正的URL;在我显式指定了FeignClient对应的URL地址后,又能够成功访问了,说明确实出现了服务名解析问题。
//显式指定的方法,相当于写死URL,无法依靠负载均衡策略做分布式,没什么意义
@FeignClient(value = "blogservice", url = "http://localhost:8888/")
没道理啊,我另一个工程代码基本一模一样,调用没一点问题,于是我开始了对这个问题漫长的分析。
首先怀疑是负载均衡失效,Ribbon无法根据服务名寻找到正确的服务,以及它对应的URL。为了印证是负载均衡的问题,我用RestTemplate代替Feign来进行远程调用,同样用服务名来调用服务。这时报出了这样的错误“java.lang.IllegalStateException: No instances available for localhost”,意为没有找到对应的服务实例,而Nacos控制台的服务全部注册成功且健康无比,说明问题确实出现在负载均衡上。
接下来我继续使用RestTemplate,看看调整负载均衡策略是否能解决这个问题。于是我在RestTemplate上加入了@LoadBalanced开启负载均衡,并注入IRule中的随机策略。加入@LoadBalanced的RestTemplate会被加上一个loadBalancerInterceptor拦截器,在这个RestTemplate发起请求时,会将其拦住并寻找对应的实例地址,再根据负载均衡策略进行挑选。
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public IRule randomRule() {
return new RandomRule();
}
但是代码中IRule一直爆红,怎么都无法引入。IRule来自于“com.netflix.loadbalancer”中,虽然资料显示SpringCloudAlibaba是整合了Netflix相关组件的,但是SpringCloud新版本对于Netflix支持不是很友好,所以我自己引入了一个netflix-ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
这个时候IRule确实是不爆红了,再次调用接口还是显示没有服务实例。按理来说负载均衡策略应该已经生效了,数个服务实例也都正常运行,但还是没法正常调用,只能把思路转向另一个方向。
项目一开始的版本管理是SpringBoot 2.5.0 + SpringCloud 2020.0.3 + SpringCloud Alibaba 2.2.5.RELEASE,之前因为SpringBoot与SpringCloud版本有强对应关系,导致Nacos的无法正常启动,因此我调整过对应的版本很多次。但我在让SpringBoot和SpringCloud版本对应的时候,并没有管SpringCloud Alibaba的版本。于是我去搜索了一下他们三者的版本对应关系,果然一查发现SpringCloud Hoxton.SR8才能与SpringCloud Alibaba 2.2.5.RELEASE兼容,说明他们三者存在版本冲突问题。
发现这个问题后,我开始了漫长的排列组合,最后将所有版本回退,使用如下版本才正常了。
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<spring.cloud.alibaba-version>2.2.3.RELEASE</spring.cloud.alibaba-version>
<spring.boot-version>2.3.2.RELEASE</spring.boot-version>
RestTemplate调用正常后,我又用Feign进行远端调用也成功了,且负载均衡策略也正常运行。泪目,这问题是真坑啊。