Spring Gateway集成 Nacos注册中心不能够发现服务的问题解决

本文详细讲述了将SpringCloud从Eureka迁移到Nacos过程中遇到的问题,包括如何修改配置、创建NacosReactiveDiscoveryClient以修复服务调用失败,并分享了解决过程和关键代码实现。

一、问题描述

我们现在是在用Nacos替换Eureka,原来Eureka和Spring gateway运行正常,可以通过Spring gateway调用注册到Eureka中的服务。

当前Spring cloud的版本是Hoxton.SR8,Nacos discovery的版本为0.9.0.RELEASE,使用的Nacos版本为2.0.3。

Nacos替换Eureka改动的地方如下:

1、去掉POM中Eureka的引入;

2、去掉主类中引入的@EnableEurekaClient注解;

3、引入Nacos依赖:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  <version>0.9.0.RELEASE</version>
  <exclusions>
    <exclusion>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
    </exclusion>
    <exclusion>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>com.alibaba.nacos</groupId>
  <artifactId>nacos-client</artifactId>
  <version>2.0.3</version>
</dependency>

4、引入spring-cloud-loadbalancer:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

5、spring-cloud-starter-openfeign中exclude掉ribbon的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-ribbon</artifactId>
        </exclusion>
    </exclusions>
</dependency>

6、application.yml中禁用nacos的ribbon:

ribbon:
  nacos:
    enabled: false

现在的现象:

  • Spring gateway启动不报错;
  • gateway也可以正常注册到Nacos;
  • 通过打断点到NacosDiscoveryClientAutoConfiguration,NacosDiscoveryClient会被初使化,但是调用时不会调用NacosDiscoveryClient.getInstances(String)获取被调用应用的Provider,会报类似下面的接口404错误:

{"msg":"/user-service/user/getByToken","result":404,"data":null}

其中user-service为注册到Nacos上面的服务名称,但是就是不能够被调用到。

  • Spring gateway的注册中心切换为Eureka,再次调用服务时,Eureka的CompositeDiscoveryClient.getInstances(String)方法就会被调用。

二、问题分析

前面走了一些弯路,也花了一些时间,最后想到的是与Eureka注册中心进行对比调试分析,下面例出关键的分析步骤。

Debug断点打到DiscoveryClientRouteDefinitionLocator两个构造函数上,只有参数是ReactiveDiscoveryClient的构造函数被加载:

继续跟踪到ReactiveCompositeDiscoveryClient.getServices()方法:

发现数组变量discoveryClients包含了两个实现,如下图所示:

其中有一个是EurekaReactiveDiscoveryClient,那在使用Nacos时就应该包含一个针对Nacos实现的ReactiveDiscoveryClient,于是切换为Nacos分支调试。

但是调试发现数组变量discoveryClients并没有包含针对Nacos实现的ReactiveDiscoveryClient:

于是参考EurekaReactiveDiscoveryClient以及NacosDiscoveryClient,立即就写了NacosReactiveDiscoveryClient:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.NacosServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
/**
 * 
 * @author fenglibin
 *
 */
@Slf4j
public class NacosReactiveDiscoveryClient implements ReactiveDiscoveryClient {

	public static final String DESCRIPTION = "Spring Cloud Nacos Reactive Discovery Client";

	private NacosDiscoveryProperties discoveryProperties;

	public NacosReactiveDiscoveryClient(NacosDiscoveryProperties discoveryProperties) {
		this.discoveryProperties = discoveryProperties;
	}

	@Override
	public String description() {
		return DESCRIPTION;
	}

	@Override
	public Flux<ServiceInstance> getInstances(String serviceId) {
		try {
			List<Instance> instances = discoveryProperties.namingServiceInstance().selectInstances(serviceId, true);

			return Flux.fromStream(hostToServiceInstanceList(instances, serviceId).stream());
		} catch (Exception e) {
			throw new RuntimeException("Can not get hosts from nacos server. serviceId: " + serviceId, e);
		}
	}

	@Override
	public Flux<String> getServices() {
		try {
			ListView<String> services = discoveryProperties.namingServiceInstance().getServicesOfServer(1,
					Integer.MAX_VALUE);

			return Flux.fromStream(services.getData().parallelStream());
		} catch (Exception e) {
			log.error("get service name from nacos server fail,", e);
			return Flux.fromStream(Collections.<String>emptyList().stream());
		}
	}

	private static ServiceInstance hostToServiceInstance(Instance instance, String serviceId) {
		NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();
		nacosServiceInstance.setHost(instance.getIp());
		nacosServiceInstance.setPort(instance.getPort());
		nacosServiceInstance.setServiceId(serviceId);

		Map<String, String> metadata = new HashMap<>();
		metadata.put("nacos.instanceId", instance.getInstanceId());
		metadata.put("nacos.weight", instance.getWeight() + "");
		metadata.put("nacos.healthy", instance.isHealthy() + "");
		metadata.put("nacos.cluster", instance.getClusterName() + "");
		metadata.putAll(instance.getMetadata());
		nacosServiceInstance.setMetadata(metadata);

		if (metadata.containsKey("secure")) {
			boolean secure = Boolean.parseBoolean(metadata.get("secure"));
			nacosServiceInstance.setSecure(secure);
		}
		return nacosServiceInstance;
	}

	private static List<ServiceInstance> hostToServiceInstanceList(List<Instance> instances, String serviceId) {
		List<ServiceInstance> result = new ArrayList<>(instances.size());
		for (Instance instance : instances) {
			result.add(hostToServiceInstance(instance, serviceId));
		}
		return result;
	}

}

或者集成如下starter:

    <dependency>
        <groupId>com.eeeffff</groupId>
        <artifactId>nacos-reactive-discovery-client</artifactId>
        <version>1.0.0</version>
    </dependency>

并在启动时创建Bean:

再次启动调试就可以看到数组变量discoveryClients中有NacosReactiveDiscoveryClient了:

 再次通过网关请求服务,就可以正常调用成功了。

 感觉Nacos的怪问题都让我给碰上了!

### Nacos-API 1.1.1 与 Nacos 核心版本的关系 Nacos 是阿里巴巴开源的一个动态服务发现、配置和服务管理平台,旨在帮助开发者更轻松地构建云原生应用[^1]。Nacos 提供了多个组件,其中包括 `nacos-api`,这是一个用于客户端与 Nacos 服务器交互的 Java API 库。 根据 Nacos 的版本发布历史和模块划分,`nacos-api-1.1.1` 是 Nacos 客户端 API 的一个具体版本。通常情况下,Nacos 的客户端 API 版本与其核心服务端版本保持一致或接近。因此,`nacos-api-1.1.1` 很可能对应于 Nacos 核心版本 **1.1.1** 或其相近版本[^4]。 在 Nacos 的开发过程中,客户端 API 和服务端功能会同步演进。例如,在 Nacos 1.2.1 中引入了一些新特性,这些特性需要通过更新的 `nacos-api` 来支持[^1]。类似地,`nacos-api-1.1.1` 应该与 Nacos 核心版本 1.1.1 配合使用,以确保功能的一致性和兼容性。 如果需要进一步确认版本关系,可以参考 Nacos 的官方文档或 Maven 仓库中的依赖信息。此外,Nacos 团队强调社区协作的重要性,并提供了详细的开发者角色定义和职责划分[^5],这有助于用户更好地理解版本迭代背景和功能改进。 ### 示例代码:如何检查 Nacos API 版本 以下是一个简单的示例代码,展示如何通过 Maven 引入 `nacos-api` 并检查其版本: ```java import com.alibaba.nacos.api.NacosFactory; public class NacosVersionCheck { public static void main(String[] args) { // 打印 Nacos API 的版本信息 System.out.println("Nacos API Version: " + NacosFactory.class.getPackage().getImplementationVersion()); } } ``` 此代码将输出当前使用的 `nacos-api` 的版本号,便于验证是否为 1.1.1。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值