gateway网关转发请求到nacos不同namespace和不同group下服务实例源码改造
创始人
2024-02-17 16:43:32

问题

gateway转发请求到微服务,报错误页面,错误信息如下所示:
在这里插入图片描述

There was an unexpected error (type=Service Unavailable, status=503).
Unable to find instance xxx

报错信息显示找不到应用实例。即gateway无法在nacos实例中获取到路由配置的对应实例。查阅网上资料,大多数写的是由于版本原因,需要手动配置ribbon中loadbalancer的jar包就可以解决问题。但是项目中SpringBoot版本为2.2.X版本,所以SpringCloud Alibaba使用的是2.2.0.RELEASE版本,此版本并不用手动配置ribbon的jar包。

补充:
SpringCloud Alibaba使用版本为2.2.0.RELEASE意思是,我们在定义nacos客户端启动类jar包,gateway启动类jar包时,对应的jar包版本都是2.2.0.RELEASE版本,只需要在父工程pom里定义SpringCloud Alibaba版本即可。其他SpringCloud Alibaba组件在配置启动类jar包时,就无需再定义版本了。

问题定位

查阅了许多资料,其中有一篇博客写道当负载转发的服务实例在nacos中与gateway服务实例在nacos里不是同一个namespace或者不是同一个group,也会报上述错误。项目中要转发的服务实例和gateway服务实例在nacos中属于同一个namespace,但是属于不同group。
于是将gateway与要转发的服务实例改成同一个group,果然可以正确转发了,不再报错了。所以,要想使用gateway进行请求转发,所有的微服务实例与gateway服务实例在nacos中必须是同一个namesapce和同一个group。

但是在本项目中,不同的微服务实例放在不同group中,是提前设计好的,有业务需要的。如果都归属于同一个group,那么会影响到其他业务实现。那么如何让gateway可以转发nacos中不同group甚至不同namespace中的服务实例呢?

解决思路

首先,需要想清楚的是,gateway只能转发在nacos中同namespace和同group中的服务实例,是谁规定的呢?我们知道,在gateway中,通过Ribbon进行负载均衡。那么,进行负载均衡的实例,是在哪里获取到的呢?在gateway中需要依赖nacos注册中心的jar包,如下:

        com.alibaba.cloudspring-cloud-starter-alibaba-nacos-discovery

依赖这个jar包后,将gateway注册到nacos中,同时,这个jar包也定义了nacos中Open Api中方法的调用,可以通过调用Open Api获取到nacos中服务实例等信息。核心类是NacosServerList类。里面的核心方法是getServers()方法,源码如下所示:

private List getServers() {try {//获取到gateway服务实例所属分组String group = discoveryProperties.getGroup();//查询分组下serviceId实例,这里的serviceId就是gateway要负载均衡的服务实例。List instances = discoveryProperties.namingServiceInstance().selectInstances(serviceId, group, true);return instancesToServerList(instances);}catch (Exception e) {throw new IllegalStateException("Can not get service instances from nacos, serviceId=" + serviceId,e);}}

由上述源码可知,nacos中默认是获取同namespace和同group下的服务实例。因为nacos中namespace和group就是用于服务隔离的,在不同的group和不同namespacae中,可以定义相同服务实例名称。所以nacos这么做无可厚非。只不过在特殊业务场景中,需要获取到不同group甚至不同namespace中服务实例。所以要对上述源码进行修改。

下面还需要分析一个问题,即我们自定义实现了NacosServerList,如何加入到源码执行的流程中呢?即在nacos执行流程中,要使用我们定义的NacosServerList类,而不是jar包中原有的NacosServerList类,应该从哪儿切入呢?在idea中,点击NacosServerList类,就会查找哪里调用这个类了,好在就一个地方初始化了这个类,那么这个类就是NacosServerList的切入,我们在这里定义我们自己的NacosServerList就行。切入类如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {@Autowiredprivate PropertiesFactory propertiesFactory;@Bean@ConditionalOnMissingBeanpublic ServerList ribbonServerList(IClientConfig config,NacosDiscoveryProperties nacosDiscoveryProperties) {if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {ServerList serverList = this.propertiesFactory.get(ServerList.class, config,config.getClientName());return serverList;}NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);serverList.initWithNiwsConfig(config);return serverList;}@Bean@ConditionalOnMissingBeanpublic NacosServerIntrospector nacosServerIntrospector() {return new NacosServerIntrospector();}}

上述源码中,ribbonServerList()方法new出了NacosServerList对象,那么我们可以自己定义一个@Configuration类,覆盖这个类,来new出我们自己改造后的NacosServerList对象。这个想法是最好的解决思路,但是,经过研究发现,在NacosServerList类中,需要有IClientConfig类参与,才可以正常使用,而IClientConfig类,是框架自带的,研究了半天,完全没有搞懂这个类是干啥的。水平有限,只能放弃这种思路了。只能退而求其次,定义源码相同的包路径和类名和类名,来覆盖源码中类了。

解决方法

首先,定义NacosServerList类子类,来重写getServer()方法,获取不同分组下服务实例,代码如下:

public class AppNacosServerList extends NacosServerList {private NacosDiscoveryProperties discoveryProperties;private String serviceId;public AppNacosServerList(NacosDiscoveryProperties discoveryProperties) {super(discoveryProperties);this.discoveryProperties=discoveryProperties;}@Overridepublic List getInitialListOfServers() {return getServers();}@Overridepublic List getUpdatedListOfServers() {return getServers();}/*** nacos默认只寻找相同namespace和相同group里的服务实例。本项目需要获取同namespace中不同group* 里的项目,所以对源码进行改造,能获取不同group下的服务实例。* @return*/private List getServers() {try {//TODO 本项目中只获取同namesapce中不同group下服务实例。如果要获取不同namespace,可以调用nacos中提供的获取所有namespace的接口,然后循环遍历即可。//TODO 需要注意的是,在nacos提供的open api中,并没有获取所有分组group的方法,因此,我们只能自己去维护我们的项目里定义了哪些分组,然后手动获取这些分组下的服务实例。//获取master分组服务实例List instances1 = discoveryProperties.namingServiceInstance().selectInstances(serviceId,"master", true);//获取slave分组服务实例List instances2 = discoveryProperties.namingServiceInstance().selectInstances(serviceId,"slave", true);List instances=new ArrayList<>();instances.addAll(instances1);instances.addAll(instances2);//master分组和slave分组实例都加入处理return instancesToServerList(instances);}catch (Exception e) {throw new IllegalStateException("Can not get service instances from nacos, serviceId=" + serviceId,e);}}private List instancesToServerList(List instances) {List result = new ArrayList<>();if (CollectionUtils.isEmpty(instances)) {return result;}for (Instance instance : instances) {result.add(new NacosServer(instance));}return result;}@Overridepublic void initWithNiwsConfig(IClientConfig iClientConfig) {this.serviceId = iClientConfig.getClientName();}}

然后要覆盖NacosRibbonClientConfiguration源码类,将我们自己定义的AppNacosServerList加入其中即可。需要注意的是需要在项目中定义与NacosRibbonClientConfiguration相同的jar包路径,才能覆盖,如下图所示:
在这里插入图片描述
当然,可以直接覆盖NacosServerList类也行,这样可以少覆盖一个类。当然大体思路是一致的。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...