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

2023-11-01

一、问题描述

我们现在是在用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的怪问题都让我给碰上了!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • Django 创建第一个web项目

    版本说明 python 3 7 0 django 3 0 6 Django 管理工具 django admin 部署虚拟环境 安装virtualenv pip install virtualenv i https mirrors aliyu
  • 开源技术选型手册 (china-pub 首发) -目 录

    第1章c闲话开源社区篇cc 第2章cWeb框架篇cc 2 1cStrutsc 2 2cSpringc 2 3cSeamcc 第3章c开源Web服务器c 3 1cApachecc 3 2cLighttpdcc 3 3cNginxc 第4章c应
  • 【MySQL】varchar转int类型的方法

    MySQL varchar转int类型的方法 CAST函数的使用 1 问题描述 获取一个表user中age的最大值 由于历史原因 age是varchar类型的 2 问题解决 方案一 select max cast sex as UNSIGN
  • Blender辅助工具集:M3插件

    1 MACHIN3tools M3 插件 一个辅助工具集 MACHIN3tools An Addon to Streamline Blender 3 3 and beyond by machin3 io https github com m
  • Spring(DI)

    DI Dependency Injection 即依赖注入 对象之间的依赖由容器在运行期决定 即容器动态的将某个依赖注入到对象自重 基于XML配置注入依赖 有参构造函数注入依赖 bean类实现有参构造函数 public class Stud
  • 开始在CSDN上安家了哈!

    2014年计划完成50 原创blog 这是我的目标
  • vue项目打包部署到tomcat(详细)

    hash路由模式打包部署到tomcat 1 修改config index js文件下的assetsPublicPath为 2 修改router文件夹下index js添加 base 文件夹名称 例如 yuncheng 可以自己随意设置 3
  • 未找到 van-toast 节点,请确认 selector 及 context 是否正确

    1 json文件引入 van toast vant weapp toast index 2 js文件引入 import Toast from vant weapp toast toast 3 wxml写入
  • 微信小程序蓝牙BLE开发实战——遇到问题及踩坑(三)

    微信小程序蓝牙BLE开发实战 三 对于我这种小白 遇到问题是常见的哈 这里记录下 避免日后再踩坑 文章目录 微信小程序蓝牙BLE开发实战 三 1 iPhone6及6plus无法搜索到设备 解决方案 2 IOS无法获取 mac 地址 如何连接
  • 分布式任务调度平台xxl-job

    一 java的集中式任务调度 while true Thread sleep 轮询 线程休眠的方式实现定时任务 java util Timer java util TimerTask Timer是一种定时器工具 用于使用后台线程计划执行指定
  • 数字IC设计流程学习笔记

    一 规格定制 IC的规格定制包括物理指标 性能指标和功能指标 物理指标 封装 工艺 芯片面积 性能指标 功耗 速度 功能指标 接口 芯片功能 二 系统设计 系统设计是确定IC的算法模型和系统架构等 并通过一些高级语言 matlab等对算法模
  • 【tensorflow基础】读取mnist数据

    转载于 MNIST手写数字数据集读取方法 TensorFlow的封装让使用MNIST数据集变得更加方便 MNIST数据集是NIST数据集的一个子集 它包含了60000张图片作为训练数据 10000张图片作为测试数据 在MNIST数据集中的每
  • spring-security

    文章目录 csrf remember me 密码存储 权限继承 应 要求添加的代码 白名单相关说明 csrf A网站登录 B网站 使用 Copyright C
  • 传染病模型(4)——SIRS模型和SIER模型及matlab具体程序

    前言 常见的传染病模型按照具体的传染病的特点可分为 SI SIS SIR SIRS SEIR 模型 其中 S E I R 的现实含义如下 S Susceptible 易感者 指缺乏免疫能力健康人 与感染者接触后容易受到感染 E Expose
  • 一文了解亚马逊云科技适用于 Amazon Lightsail 的托管数据库

    Amazon Lightsail 是亚马逊云科技提供的一种易上手使用 月度价格经济实惠 并包括了计算实例 容器 存储 数据库的虚拟专用服务器 在创建时可以进行业务蓝图选择 可选择包含多种操作系统 Linux Windows 等 或操作系统加
  • C++中定义常量的几种方式

    概述 在程序运行过程中 始终不发生改变的量 称之为常量 在 C 语言中常量是个固定值 也就是说常量值在定义后不能进行修改 define 宏常量 define 是 C 语言中定义常量的方式 在 C 中也可以使用 define 的使用 defi
  • RocketMQ安装与启动

    分享知识 传递快乐 官网 https rocketmq apache org 1 准备环境 系统 Centos7 jdk 1 8 2 环境部署 解压 rocketmq 并进入 rocketmq 下的 bin 目录 调整启动内存 vim bi
  • C++ 函数模板

    函数模板是通用的函数描述 它们使用泛型来定义函数 其中的泛型可用具体的类型替换 通过将类型作为参数传递给模板 可使编译器生成该类型的函数 由于模板允许以泛型 而不是具体类型 的方式编写程序 因此有时候也被称为通用编程 在标准C 98添加关键
  • ubuntu14.04安装wireshakes

    网络攻防 这课要做一个嗅探器的大作业 想在linux是实现 于是先在ubuntu上下一个wiresharks看看它的一些功能和 废话少说 直接上安装过程与期间遇到的问题 安装编译工具 sudo apt get install build e
  • Spring Gateway集成 Nacos注册中心不能够发现服务的问题解决

    一 问题描述 我们现在是在用Nacos替换Eureka 原来Eureka和Spring gateway运行正常 可以通过Spring gateway调用注册到Eureka中的服务 当前Spring cloud的版本是Hoxton SR8 N