[nacos] Nacos <3> 应用场景及高频问题(FAQ)

场景: SpringCloud 微服务 集成 Nacos 配置中心

配置中心:简述

  • Nacos除了可以做注册中心,同样可以做配置中心来使用。
<!-- nacos 注册中心 能力 -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
	<version>${spring-cloud-alibaba.version}</version>
</dependency>

<!-- nacos 配置中心 能力 -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
	<version>${spring-cloud-alibaba.version}</version>
</dependency>
  • 所谓配置中心:一般SpringBoot项目都使用在本地resources下创建类似application.yml之类的配置文件来管理整个项目的一些配置信息。
  • 但当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。
  • 为此,我们需要一种统一配置管理方案,可以集中管理所有实例的配置。

  • 采用本地静态配置,无法保证实时性
  • 修改配置不灵活、且需要经过较长的测试发布周期,无法尽快通知到客户端
  • 还有些配置对实时性要求很高

比方说:主备切换配置或者碰上故障需要修改配置,这时通过传统的静态配置或者重新发布的方式去配置,那么响应速度是非常慢的,业务风险非常大

动态配置服务

  • 动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。
  • 动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。
  • 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
  • Nacos 提供了一个简洁易用的UI (控制台样例 Demo) 帮助您管理所有的服务和应用的配置。
  • Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。

多配置格式编辑器

  • Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常见配置格式在线编辑、语法高亮、格式校验,帮助用户高效编辑的同时大幅降低格式错误带来的风险。

  • Nacos支持配置标签的能力,帮助用户更好、更灵活的做到基于标签的配置分类及管理。同时支持用户对配置及其变更进行描述,方便多人或者跨团队协作管理配置。

SpringCloud微服务启动时,拉取配置的流程

  • 微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动。但如果尚未读取application.yml,又如何得知nacos地址呢?

  • 为此,spring cloud引入了一种新的配置文件:bootstrap.yaml/yml/properties文件,会在本地application.yml之前被读取。

  • bootstrap.yml 用来程序引导时执行,应用于更加早期配置信息读取。可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。一旦 bootStrap.yml 被加载,则内容不会被覆盖。
  • application.yml 可以用来定义应用级别的, 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。
  • 启动上下文时,Spring Cloud 会创建一个 Bootstrap Context,作为 Spring 应用的 Application Context 的父上下文。
  • Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。
  • 需要引入依赖: spring-cloud-starter-bootstrap
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

  • spring cloud中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application

Data ID

  • 配置文件的默认命名公式
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
  • prefix 默认为 spring.application.name 的值
  • spring.profile.active 即为当前环境对应的 profile,可以通过配置项 spring.profile.active 来配置。
  • file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置

通俗一点就是 “前缀-环境-扩展名”
Demo : student-service-dev.yaml

spring:
  application:
    name: student-service # 服务名称
  profiles:
    active: dev #开发环境,这里是dev
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
        file-extension: yaml # 文件后缀名

@RefreshScope

  • SpringCloud 使用 @RefreshScope 注解,实现配置文件的动态加载
  • @RefreshScope 注解是一个基于 Spring Cloud Config 的注解。它允许 Spring Boot 应用程序在运行时动态地刷新配置,而无需重启应用程序。使用 @RefreshScope 注解,可以在不停止应用程序的情况下修改配置。

在 Spring Boot 中,@RefreshScope 注解是基于 Spring Cloud Config 实现的。Spring Cloud Config 是一个用于集中化配置管理的工具。它可以将配置存储在 Git、SVN 或本地文件系统中,并将其提供给多个应用程序。
当应用程序中使用了 @RefreshScope 注解时,Spring Boot 将会监控配置文件的变化。当配置文件发生变化时,Spring Boot 将会重新加载配置并重新初始化相关的 Bean。这样,就可以在应用程序运行时动态地修改配置。

配置共享的优先级

  • 当nacos、服务本地同时出现相同属性时,优先级有高低之分:

  • 可通过spring.cloud.nacos.config.remote-first配置项来修改优先级
remote-first: true  # true 代表nacos配置中心的配置优先级高 否则本地配置优先级高 默认 false

命名空间Namespace

玩法1:用于环境隔离

  • 一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境,那怎么对这些微服务配置进行管理呢?

  • Nacos 引入命名空间 Namespace 的概念来进行多环境配置和服务的管理及隔离。

  • 例如,你可能存在本地开发环境dev、测试环境test、生产环境prod 三个不同的环境,那么可以创建三个不同的 Namespace 区分不同的环境。

成功创建新命名空间后,就可以在 springboot 的配置文件配置命名空间的 id 切换到对应的命名空间,并获取对应空间下的配置文件。
但在没有指定命名空间配置的情况下,默认的配置都是在 public 空间中。

玩法2:用于业务、业务团队隔离

Group分组

玩法1:业务、业务团队隔离

  • Group 也可以实现环境隔离的功能,但 Group 设计的目的主要是做同一个环境中的不同服务分组,把不同的微服务的配置文件划分到同一个分组里面去。
  • Nacos 如果不指定 Group,则默认的分组是 DEFAULT_GROUP
spring:
  cloud:
    nacos:
      config:
        group: xxxx

玩法2:用于子团队/个人隔离

完整Demo

  • 版本信息
  • spring : 5.2.15.RELEASE
  • spring-boot : 2.3.12.RELEASE
  • spring-cloud-starter : 2.2.9.RELEASE
  • spring-cloud-starter-nacos-config : 2.2.7.RELEASE
  • spring-cloud-starter-nacos-discovery : 2.2.7.RELEASE
  • nacos-server : 2.1.2
  • nacos-client : 2.0.3
  • nacos 远程配置文件 : application-xxxx-service.yml

  • bootstrap.yml

spring:
  # 打包扫描文件,对应: application-xxx.yml
  profiles:
    active: config,database,xxl-job,${ENV_TYPE:dev}
  application:
    # 微服务名称(英文名)
    name: ${APPLICATION_NAME:xxxx-service}
    # 微服务中文名
    display-name: XXXX 服务
  cloud:
    nacos:
      discovery:
        enabled: true # (默认为 true ,可不配置此属性)
        # server-addr: http://127.0.0.1:8848 # nacos-local | 服务端地址(Nacos Server 启动监听的ip地址和端口) | 默认值: 无 | 此配置项取决于 生效的 spring.profiles
        server-addr: ${NACOS_PROTOCOL:http}://${NACOS_ENDPOINT:nacos.nacos:8848}
        # server-addr: ${NACOS_PROTOCOL:https}://${NACOS_ENDPOINT:config-dev.xxx.com:443}
        # server-addr: ${NACOS_SERVER_ADDR:https://config-dev.xxx.com} # huawei-cloud:cn-dev
        # server-addr: http://mse-xxxxxx-nacos-ans.mse.aliyuncs.com:8848 # aliyun-cloud:cn-dev
        # server-addr: http://mse-yyyyyy-nacos-ans.mse.aliyuncs.com:8848 # aliyun-cloud:cn-test
        # server-addr: http://mse-zzzzzz-nacos-ans.mse.aliyuncs.com:8848 # aliyun-cloud:cn-prod
        # server-addr: https://config-dev.xxx.com # huawei-cloud:cn-dev
        # server-addr: https://config-test.xxx.com # huawei-cloud:cn-test
        # server-addr: https://config-pt.xxx.com # huawei-cloud:cn-pt
        service: ${spring.application.name} # 服务名(service-name, 给当前的服务命名) | 默认值: ${spring.application.name}
        namespace: ${NACOS_SERVER_NAMESPACE:xxxx_office} # 命名空间(主要用于运行环境的隔离) | 默认值: 无
        group: ${NACOS_SERVER_GROUP:xxx_GROUP} # 服务分组(设置服务所处的分组) | 默认值: DEFAULT_GROUP
        username: ${NACOS_USER:nacos}
        password: ${NACOS_PASSWORD:nacos}
        # logName: # 日志文件名 | 默认值: 无
        # clusterName: DEFAULT
        # weight
        # metadata:
        # secure: false
        # accessKey: xx
        # secretKey: yy
      config: # nacos 配置中心的配置加载项必须放在 test.yml ,因为其启动顺序/优先级 高于 application.yml,否则会带来 spring ioc 的一系列加载问题
        # reference-doc https://blog.csdn.net/m0_57752520/article/details/124169746
        # reference-doc https://github.com/nacos-group/nacos-spring-boot-project/wiki/spring-boot-0.2.2-%E4%BB%A5%E5%8F%8A-0.1.2%E7%89%88%E6%9C%AC%E6%96%B0%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C
        # Spring Boot 通过 从 namespace 找到配置文件的命名空间,然后通过 {prefix}−{spring.profile.active}.${file-extension} 找到对应的 NACOS 配置文件
        enabled: true
        bootstrap:
          enable: true # 开启配置预加载功能
          log-enable: false
        server-addr: ${spring.cloud.nacos.discovery.server-addr} # 主配置服务器地址 # 此配置项取决于 生效的 spring.profiles
        namespace: ${spring.cloud.nacos.discovery.namespace} # 主配置
        group: ${spring.cloud.nacos.discovery.group} # 主配置 group-id ; 这里约定环境参数 ; eg: ${spring.profiles.active}
        name: ${spring.application.name} # 要指向需要改变的微服务的注册中心的名称
        username: ${spring.cloud.nacos.discovery.username}
        password: ${spring.cloud.nacos.discovery.password}
        # prefix: # eg: user-config
        # type: yaml # 主配置 配置文件类型
        file-extension: yaml # yml or yaml / properties / ...
        data-id: application-${spring.application.name}.yml  # 主配置 data-id
        remote-first: true  # true 代表nacos配置中心的配置优先级高 否则本地配置优先级高 默认 false
        auto-refresh: true # 主配置 开启自动刷新
        refresh-enabled: true
        enable-remote-sync-config: true  # 主配置 开启注册监听器预加载配置服务(除非特殊业务需求,否则不推荐打开该参数)
        max-retry: 2 # 主配置 最大重试次数
        config-retry-time: 3000  # 主配置 重试时间
        config-long-poll-timeout: 6000   # 主配置 配置监听长轮询超时时间
        extension-configs:
          - dataId: application-env.yml
            group: DEFAULT_GROUP
            refresh: true
          - dataId: application-${spring.application.name}.yml
            group: ${spring.cloud.nacos.discovery.group}
            refresh: true
        # extension-configs:  # 扩展配置:是一个数组,可有多个
          # nacos配置中心配置 dataId 时最好加上文件名后缀
          # 如果不加后缀,在扩展配置和共享配置读取配置时会出现以前两种情况:
          #   1、在下方dataId中不加后缀读取配置时会默认以properties读取,若文件是properties则没有问题,若不是那么配置就读取不到了。
          #   2、在下方dataId中加上后缀那么在nacos配置中心就找不到对应dataId的配置。
          # 注:最好是 nacos 配置中心 和 代码中配置的 dataId 是带有后缀且是一致的。避免踩坑!!!!!!!!!!!!!
          # - dataId: xx-yy-zz.yaml
          #  group: USER_GROUP
          #  refresh: true
        # shared-configs:  # 共享配置:是一个数组,可以有多个,配置方式与扩展配置一模一样 | 配置优先级:shared-configs < extension-configs < 默认
          # - dataId: xx-yy-zz.yml
          #  group: USER_GROUP
          #  refresh: true
        # shared-dataids: xx-yy-zz.yml

X 参考文献

场景:指定NACOS注册中心中spring cloud微服务应用的IP

spring:
  cloud:
    nacos:
      discovery:
        ip: 127.0.0.1

修改完成、并重启服务之后在nacos查看的地址如下:

场景:curl请求NACOS常用功能接口

  • 测试版本
  • nacos-client : 2.0.3
  • nacos-server : 2.1.2
  • 推荐文献

登录/获取 accessToken

# 获取 accessToken
curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos'

响应内容
{"accessToken":"xxx.xxx.xxxx-xxx-xxx","tokenTtl":86400000,"globalAdmin":false,"username":"read_bdp"}

获取配置

# 获取配置 | NACOS-SERVER 2.1.2 实测
curl -X GET 'https://config.xx.com/nacos/v1/cs/configs?show=all&dataId=application-xx-service.yml&group=XXX_GROUP&tenant=bdp_office&namespaceId=bdp_office&accessToken=xxxxxxx'

服务注册

# 服务注册 | NACOS-SERVER 2.1.2 实测
accessToken="xxxxxxx"
echo "https://nacos-config.xx.com/nacos/v1/ns/instance?port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=bigdata_office&accessToken=${accessToken}"
curl -X POST "https://nacos-config.xx.com/nacos/v1/ns/instance?port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=bdp_office&accessToken=$accessToken"
  • 关键源码
  • nacos-client : 2.0.3
  • nacos-server : 2.1.2
com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate#registerService

--> com.alibaba.nacos.client.naming.remote.NamingClientProxy#registerService

--> com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#registerService
    @Override
    public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
                instance);
        redoService.cacheInstanceForRedo(serviceName, groupName, instance);
        doRegisterService(serviceName, groupName, instance);
    }

--> com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#doRegisterService
    public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
        InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
                NamingRemoteConstants.REGISTER_INSTANCE, instance);
        requestToServer(request, Response.class); // 调用 :NamingGrpcClientProxy#requestToServer
        redoService.instanceRegistered(serviceName, groupName);
    }

--> com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#requestToServer
    private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
            throws NacosException {
        try {
            request.putAllHeader(getSecurityHeaders());
            request.putAllHeader(getSpasHeaders(
                    NamingUtils.getGroupedNameOptional(request.getServiceName(), request.getGroupName())));
            Response response =
                    requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
            if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {
                throw new NacosException(response.getErrorCode(), response.getMessage());
            }
            if (responseClass.isAssignableFrom(response.getClass())) {
                return (T) response;
            }
            NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'",
                    response.getClass().getName(), responseClass.getName());
        } catch (Exception e) {
            throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
        }
        throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
    }
  • 注册成功的响应:

https://nacos-config.xx.com/nacos/v1/ns/instance?port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=bdp_office&accessToken=xxxxxxx
关键日志

[TID: N/A] [my-xxl-job-executor] [system] [2024/09/06 11:18:21.248] [INFO ] [main] [NamingGrpcClientProxy] registerService:112__||__[REGISTER-SERVICE] bdp_office registering service my-xxl-job-executor with instance Instance{instanceId='null', ip='192.168.19.181', port=9527, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='null', metadata={management.endpoints.web.base-path=/actuator, preserved.register.source=SPRING_CLOUD}}
[TID: N/A] [my-xxl-job-executor] [system] [2024/09/06 11:18:21.260] [INFO ] [main] [NacosServiceRegistry] register:75__||__nacos registry, BDP_GROUP my-xxl-job-executor-data-distribute 192.168.19.181:9527 register finished
  • 当前用户无服务注册权限的响应: (即 可写权限)

以 NACOS SERVER 2.1.2 为例,此错误发生在 强制启用身份认证之后
{"timestamp":"2024-09-06T10:42:11.602+08:00","status":403,"error":"Forbidden","message":"authorization failed!","path":"/nacos/v1/ns/instance"}

关键日志

...
Caused by: java.lang.reflect.UndeclaredThrowableException
	at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:147) ~[spring-core-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
	at com.alibaba.cloud.nacos.registry.NacosServiceRegistry.register(NacosServiceRegistry.java:82) ~[spring-cloud-starter-alibaba-nacos-discovery-2.2.7.RELEASE.jar!/:2.2.7.RELEASE]
	at org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration.register(AbstractAutoServiceRegistration.java:239) ~[spring-cloud-commons-2.2.9.RELEASE.jar!/:2.2.9.RELEASE]
	at com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration.register(NacosAutoServiceRegistration.java:78) ~[spring-cloud-starter-alibaba-nacos-discovery-2.2.7.RELEASE.
	...
...

...
Caused by: com.alibaba.nacos.api.exception.NacosException: Request nacos server failed:        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.requestToServer(NamingGrpcClientProxy.java:279) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.doRegisterService(NamingGrpcClientProxy.java:129) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.registerService(NamingGrpcClientProxy.java:115) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate.registerService(NamingClientProxyDelegate.java:95) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.NacosNamingService.registerInstance(NacosNamingService.java:145) ~[nacos-client-2.0.3.jar!/:?]
  ...
...

Caused by: com.alibaba.nacos.api.exception.NacosException: authorization failed!        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.requestToServer(NamingGrpcClientProxy.java:271) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.doRegisterService(NamingGrpcClientProxy.java:129) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.registerService(NamingGrpcClientProxy.java:115) ~[nacos-client-2.0.3.jar!/:?]        
  at com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate.registerService(NamingClientProxyDelegate.java:95) ~[nacos-client-2.0.3.jar!/:?]
  ...
...

获取注册的服务列表

protocol="https"
endpoint="config.xxxx.com:443"
username=nacos
password=xxxxxx

curl -X GET "$protocol://$endpoint/nacos/v1/ns/service/list?namespaceId=bigdata_office&pageNo=1&pageSize=5&accessToken=$accessToken"

response

{"count":0,"doms":[]}

场景:Nacos 2.x,调整 GRPC SDK客户端端口(GRpcSdkClient)、GRPC集群客户端端口(GRpcClusterClient)?

需求描述

  • NACOS 2.x 的端口需求:

2.x最大的变化之一就是端口。

  • MainPort/8848 (默认主端口),在此之外又新增了三个端口,新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成:
  • SdkGrpcPort/9848主端口+1000)NACOS SDK 客户端(nacos-client)gRPC请求服务端端口,用于客户端向服务端发起连接和请求
  • ClusterGrpcPort/9849主端口+1001`)服务端gRPC请求服务端端口,用于服务间同步等
  • JRaftPort/7848主端口-1000)Jraft请求服务端端口,用于处理服务端间的Raft相关请求

注:固定的计算策略,主要源自于

com.alibaba.nacos.common.remote.client.RpcClient#start

com.alibaba.nacos.common.remote.client.grpc.GrpcClient#connectToServer
com.alibaba.nacos.common.remote.client.grpc.GrpcClient#serverCheck
com.alibaba.nacos.common.remote.client.RpcClient#rpcPortOffset

com.alibaba.nacos.common.remote.client.grpc.GrpcClient#serverCheck
com.alibaba.nacos.shaded.com.google.common.util.concurrent.ListenableFuture
com.alibaba.nacos.common.remote.client.grpc.GrpcUtils#parse

Waited 3000 milliseconds (plus 1 milliseconds, 465000 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@3ea75b05[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@33a47707, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@4d290757, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@720a1fd0}}}}}]]
  • 因安全策略、统一管控等原因,如:Nginx 统一用80(http)443(https)等端口代理 NACOS 2.x8848端口及服务,则产生了一个问题:
  • 1、NACOS SDK 客户端端口,按照SdkGrpcPort端口的固定的计算策略————需占用: 1080端口(80+1000=1080) 或 1443端口(443+1000=1443)
  • 2、NACOS 服务间调用端口,按照ClusterGrpcPort端口的固定的计算策略————需占用: 1081端口(80+1001=1081) 或 1444端口(443+1001=1444)
  • 基于1、2,则:造成了网络策略等非常被动,只能走固定的端口,也容易给网络黑客留下可乘之机。

若 客户端 与 nacos server端的 gRPC 端口网络通信失败时,将报如下错误日志:

【关键信息】

  • 主端口为443 : ... server main port=443
  • 与1443端口,通信超时 : Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 93564 nanoseconds delay) ...
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.581] [INFO ] [main] [RpcClientFactory] lambda$createClient$0:77__||__[RpcClientFactory] create a new rpc client of a5c24981-4ae2-420b-9dc2-ab989fb4c728
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.581] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]RpcClient init label, labels={module=naming, source=sdk}
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.584] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]RpcClient init, ServerListFactory =com.alibaba.nacos.client.naming.core.ServerListManager
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.584] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]Registry connection listener to current client:com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.585] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]Register server push request handler:com.alibaba.nacos.client.naming.remote.gprc.NamingPushRequestHandler
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:09.586] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728] Try to connect to server on start up, server: {serverIp='config-center.com', server main port=443}
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:10.048] [ERROR] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 103219 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@1edb7320[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:10.049] [INFO ] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfInfoEnabled:60__||__[d24573f5-2525-4cdc-9285-129ebe83ac52_config-0] fail to connect server,after trying 1 times, last try server is {serverIp='config-center.com', server main port=443},error=unknown
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:12.584] [WARN ] [com.alibaba.nacos.client.naming.grpc.redo.0] [RedoScheduledTask] run:46__||__Grpc Connection is disconnect, skip current redo task
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:12.589] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 84026 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@7caa550[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:12.590] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728] Try to connect to server on start up, server: {serverIp='config-center.com', server main port=443}
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:13.252] [ERROR] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 92253 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@50f72a78[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:13.253] [INFO ] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfInfoEnabled:60__||__[d24573f5-2525-4cdc-9285-129ebe83ac52_config-0] fail to connect server,after trying 2 times, last try server is {serverIp='config-center.com', server main port=443},error=unknown
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:15.584] [WARN ] [com.alibaba.nacos.client.naming.grpc.redo.0] [RedoScheduledTask] run:46__||__Grpc Connection is disconnect, skip current redo task
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:15.592] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 83386 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@62417a16[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:15.593] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728] Try to connect to server on start up, server: {serverIp='config-center.com', server main port=443}
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:16.556] [ERROR] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 88277 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@16ca9314[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:16.556] [INFO ] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfInfoEnabled:60__||__[d24573f5-2525-4cdc-9285-129ebe83ac52_config-0] fail to connect server,after trying 3 times, last try server is {serverIp='config-center.com', server main port=443},error=unknown
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:18.585] [WARN ] [com.alibaba.nacos.client.naming.grpc.redo.0] [RedoScheduledTask] run:46__||__Grpc Connection is disconnect, skip current redo task
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:18.596] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 79749 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@53499d85[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:18.597] [INFO ] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728] try to re connect to a new server ,server is  not appointed,will choose a random server.
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:18.597] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:18.597] [INFO ] [main] [LoggerUtils] printIfInfoEnabled:60__||__[a5c24981-4ae2-420b-9dc2-ab989fb4c728]Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$4

...

[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:06.945] [ERROR] [com.alibaba.nacos.client.remote.worker] [LoggerUtils] printIfErrorEnabled:99__||__Server check fail, please check server config-center.com ,port 1443 is available , error =java.util.concurrent.TimeoutException: Waited 3000 milliseconds (plus 93564 nanoseconds delay) for com.alibaba.nacos.shaded.io.grpc.stub.ClientCalls$GrpcFuture@7dda830b[status=PENDING, info=[GrpcFuture{clientCall={delegate={delegate=ClientCallImpl{method=MethodDescriptor{fullMethodName=Request/request, type=UNARY, idempotent=false, safe=false, sampledToLocalTracing=true, requestMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@2053d869, responseMarshaller=com.alibaba.nacos.shaded.io.grpc.protobuf.lite.ProtoLiteUtils$MessageMarshaller@7a419da4, schemaDescriptor=com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestMethodDescriptorSupplier@14555e0a}}}}}]]
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:07.094] [WARN ] [main] [EndpointId] logWarning:155__||__Endpoint ID 'service-registry' contains invalid characters, please migrate to a valid format.
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:07.842] [INFO ] [main] [DirectJDKLog] log:173__||__Initializing ProtocolHandler ["http-nio-9527"]


...

[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:19.627] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Send request fail, request=SubscribeServiceRequest{headers={accessToken=eyJhbGciOiJIUzI1NiJ9.234efdsf3fwfczs.43589fhejer8952wdqw, app=unknown}, requestId='null'}, retryTimes=0,errorMessage=Client not connected,current status:STARTING
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:19.728] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Send request fail, request=SubscribeServiceRequest{headers={accessToken=eyJhbGciOiJIUzI1NiJ9.234efdsf3fwfczs.43589fhejer8952wdqw, app=unknown}, requestId='null'}, retryTimes=1,errorMessage=Client not connected,current status:STARTING
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:19.829] [ERROR] [main] [LoggerUtils] printIfErrorEnabled:99__||__Send request fail, request=SubscribeServiceRequest{headers={accessToken=eyJhbGciOiJIUzI1NiJ9.234efdsf3fwfczs.43589fhejer8952wdqw, app=unknown}, requestId='null'}, retryTimes=2,errorMessage=Client not connected,current status:STARTING
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:19.829] [ERROR] [main] [NacosWatch] start:138__||__namingService subscribe failed, properties:NacosDiscoveryProperties{serverAddr='https://config-center.com:443', endpoint='', namespace='xxxx_office', watchDelay=30000, logName='', service='xxxx-service', weight=1.0, clusterName='DEFAULT', group='BDP_GROUP', namingLoadCacheAtStart='false', metadata={management.endpoints.web.base-path=/actuator, preserved.register.source=SPRING_CLOUD}, registerEnabled=true, ip='192.168.23.68', networkInterface='', port=-1, secure=false, accessKey='', secretKey='', heartBeatInterval=null, heartBeatTimeout=null, ipDeleteTimeout=null, failFast=true}
com.alibaba.nacos.api.exception.NacosException: Request nacos server failed: 
        at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.requestToServer(NamingGrpcClientProxy.java:279) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.doSubscribe(NamingGrpcClientProxy.java:227) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.subscribe(NamingGrpcClientProxy.java:212) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate.subscribe(NamingClientProxyDelegate.java:147) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.client.naming.NacosNamingService.subscribe(NacosNamingService.java:393) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.cloud.nacos.discovery.NacosWatch.start(NacosWatch.java:134) [spring-cloud-starter-alibaba-nacos-discovery-2.2.7.RELEASE.jar!/:2.2.7.RELEASE]
        at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:895) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554) [spring-context-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) [spring-boot-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:755) [spring-boot-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:402) [spring-boot-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
        at cn.xxxx.service.app.XxxxServiceApplication.main(XxxxServiceApplication.java:56) [classes!/:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_412]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_412]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_412]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_412]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) [xxxx-service-app.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) [xxxx-service-app.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) [xxxx-service-app.jar:?]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) [xxxx-service-app.jar:?]
Caused by: com.alibaba.nacos.api.exception.NacosException: Client not connected,current status:STARTING
        at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:655) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:635) ~[nacos-client-2.0.3.jar!/:?]
        at com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy.requestToServer(NamingGrpcClientProxy.java:269) ~[nacos-client-2.0.3.jar!/:?]
        ... 26 more
[TID: N/A] [xxxx-service] [system] [2024/09/06 17:46:19.841] [INFO ] [main] [DirectJDKLog] log:173__||__Starting ProtocolHandler ["http-nio-9527"]

...

源码分析:nacos 2.x - 2.1.1 : GrpcSdkClient/GrpcClusterClient#rpcPortOffset 的端口配置太死板、太固定(sdkPort=主端口+1000clusterPort=主端口+1001),无法动态配置

注:以 nacos 2.0.3 版本为例做源码分析,其 发布于 2021.7.28
本节的源码分析,均基于 nacos-client:2.0.3 / nacos-common:2.0.3

RpcClient#currentRpcServernextRpcServer => 调用 RpcClient#resolveServerInfo 方法

com.alibaba.nacos.common.remote.client.RpcClient#currentRpcServernextRpcServer => 调用 RpcClient#resolveServerInfo 方法

  • RpcClient#currentRpcServer
protected ServerInfo currentRpcServer() {
	String serverAddress = getServerListFactory().getCurrentServer();
	return resolveServerInfo(serverAddress);// serverAddress's sampleValue : http://nacos.nacos:8848 或 nacos.nacos:8848
}
  • RpcClient#nextRpcServer
    protected ServerInfo nextRpcServer() {
        String serverAddress = getServerListFactory().genNextServer();
        return resolveServerInfo(serverAddress);
    }

RpcClient#resolveServerInfo 在获取【主端口】时的策略

  • RpcClient#resolveServerInfo
    private ServerInfo resolveServerInfo(String serverAddress) {
        String property = System.getProperty("nacos.server.port", "8848");
        int serverPort = Integer.parseInt(property);// 默认配置(nacos.server.port)的 8848 端口
        ServerInfo serverInfo = null;
        if (serverAddress.contains(Constants.HTTP_PREFIX)) {//Constants.HTTP_PREFIX = "http"
            String[] split = serverAddress.split(Constants.COLON); // Constants.COLON = ":"
            String serverIp = split[1].replaceAll("//", "");// serverIp sample = "nacos.nacos"
            if (split.length > 2 && StringUtils.isNotBlank(split[2])) {//用户配置的 serverAddress 提供了端口信息时
                serverPort = Integer.parseInt(split[2]);//以用户配置的  serverAddress 提供的端口为准
            }
            serverInfo = new ServerInfo(serverIp, serverPort);
        } else {
            String[] split = serverAddress.split(Constants.COLON);
            String serverIp = split[0];
            if (split.length > 1 && StringUtils.isNotBlank(split[1])) {
                serverPort = Integer.parseInt(split[1]);
            }
            serverInfo = new ServerInfo(serverIp, serverPort);
        }
        return serverInfo;
    }

GrpcClient#connectToServer :基于gRpc连接NACOS服务端时,SdkGrpcPortClusterGrpcPort端口依赖子类GrpcSdkClientGrpcClusterClientrpcPortOffset方法,但其端口计算策略较为固定,无法动态配置

  • com.alibaba.nacos.common.remote.client.grpc.GrpcClient#connectToServer
  • GrpcClientRpcClient 的一级实现子类

其实现了 RpcClient#connectToServer(ServerInfo)#serverCheck(ip ,port, requestBlockingStub)#sendResponse(...) 等核心方法

  • GrpcClusterClientGrpcSdkClientGrpcClient 的子类,RpcClient 的二级实现子类
@Override
public Connection connectToServer(ServerInfo serverInfo) {
	try {
		if (grpcExecutor == null) {
			int threadNumber = ThreadUtils.getSuitableThreadCount(8);
			grpcExecutor = new ThreadPoolExecutor(threadNumber, threadNumber, 10L, TimeUnit.SECONDS,
					new LinkedBlockingQueue<>(10000),
					new ThreadFactoryBuilder().setDaemon(true).setNameFormat("nacos-grpc-client-executor-%d")
							.build());
			grpcExecutor.allowCoreThreadTimeOut(true);
			
		}
		int port = serverInfo.getServerPort() + rpcPortOffset();//关键代码行
		//在子类 GrpcSdkClient 中的实现————固定的端口计算策略: public int rpcPortOffset() { return 1000; }
		//在子类 GrpcClusterClient 中的实现————固定的端口计算策略: public int rpcPortOffset() { return 1001; }
		RequestGrpc.RequestFutureStub newChannelStubTemp = createNewChannelStub(serverInfo.getServerIp(), port);//开辟1个新通信通道
		if (newChannelStubTemp != null) {
			
			Response response = serverCheck(serverInfo.getServerIp(), port, newChannelStubTemp);//检测与服务器的通信状态是否良好
			if (response == null || !(response instanceof ServerCheckResponse)) {
				shuntDownChannel((ManagedChannel) newChannelStubTemp.getChannel());//关闭检测通信通道
				return null;
			}
			
			BiRequestStreamGrpc.BiRequestStreamStub biRequestStreamStub = BiRequestStreamGrpc
					.newStub(newChannelStubTemp.getChannel());
			GrpcConnection grpcConn = new GrpcConnection(serverInfo, grpcExecutor);
			grpcConn.setConnectionId(((ServerCheckResponse) response).getConnectionId());
			
			//create stream request and bind connection event to this connection.
			StreamObserver<Payload> payloadStreamObserver = bindRequestStream(biRequestStreamStub, grpcConn);
			
			// stream observer to send response to server
			grpcConn.setPayloadStreamObserver(payloadStreamObserver);
			grpcConn.setGrpcFutureServiceStub(newChannelStubTemp);
			grpcConn.setChannel((ManagedChannel) newChannelStubTemp.getChannel());
			//send a  setup request.
			ConnectionSetupRequest conSetupRequest = new ConnectionSetupRequest();
			conSetupRequest.setClientVersion(VersionUtils.getFullClientVersion());
			conSetupRequest.setLabels(super.getLabels());
			conSetupRequest.setAbilities(super.clientAbilities);
			conSetupRequest.setTenant(super.getTenant());
			grpcConn.sendRequest(conSetupRequest);
			//wait to register connection setup
			Thread.sleep(100L);
			return grpcConn;
		}
		return null;
	} catch (Exception e) {
		LOGGER.error("[{}]Fail to connect to server!,error={}", GrpcClient.this.getName(), e);
	}
	return null;
}

源码分析:nacos 2.1.2 及更高版本 : 支持基于JVM参数配置GRPC端口

注:本变更提交于 2022.8.23,即 最低版本为 nacos 2.1.2
关键 Commit : b36e6a50ab1c6b19e2b219cd01cb7850d1c31580

GrpcSdkClient#rpcPortOffset :

  • common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient#rpcPortOffset
public class GrpcSdkClient extends GrpcClient {
	@Override
	public int rpcPortOffset() {
		public int rpcPortOffset() {
			return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY,//"nacos.server.grpc.port.offset"
					String.valueOf(Constants.SDK_GRPC_PORT_DEFAULT_OFFSET)));//1000
		}
	}
	
	//...
}
  • 可通过 JVM 参数(-Dnacos.server.grpc.port.offset=xxx) 设置 SdkGrpcPort
  • GrpcClusterClient#rpcPortOffset
public class GrpcClusterClient extends GrpcClient {

    @Override
    public int rpcPortOffset() {
        return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY,//"nacos.server.grpc.port.offset"
                String.valueOf(Constants.CLUSTER_GRPC_PORT_DEFAULT_OFFSET)));//1001
    }

	//...
}
  • 可通过 JVM 参数(-Dnacos.server.grpc.port.offset=xxx) 设置 ClusterGrpcPort
  • common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstants
public class GrpcConstants {
    public static final String NACOS_SERVER_GRPC_PORT_OFFSET_KEY = "nacos.server.grpc.port.offset";

    public static final String NACOS_CLIENT_GRPC = "nacos.remote.client.grpc";
	
	...
}	
  • com.alibaba.nacos.api.common.Constants
public class Constants {
	//...

    public static final String DATA_ID = "dataId";

    public static final String TENANT = "tenant";

    public static final String GROUP = "group";

    public static final String NAMESPACE_ID = "namespaceId";
	
    public static final String ACCESS_TOKEN = "accessToken";

    public static final String USERNAME = "username";

    public static final Integer SDK_GRPC_PORT_DEFAULT_OFFSET = 1000;//关键代码行
    
    public static final Integer CLUSTER_GRPC_PORT_DEFAULT_OFFSET = 1001;//关键代码行

	
	//...
}

小结

  • 若使用 nacos server2.x,而 nacos-client2.1.2 以下,则:
  • 方法1:升级 nacos-client2.1.2及以上版本,则:可通过设置JVM参数(nacos.server.grpc.port.offset)来调整 SdkGrpcPort
  • 方法2:不升级 nacos-cliet 版本(如:2.0.3),则:网络策略(防火墙等),需遵照 SdkGrpcPort = MainPort + 1000的原则来开放端口。
  • 方法3:不升级 nacos-cliet 版本(如:2.0.3),则:若实际开放的主端口是443SdkGrpcPort实际开放的是9848,不便于改动网络端口时的配置技巧: (亲测,有效
  • serverAddr 配置为 https://config-center ,而非https://config-center:443

即:不要在serverAddr主动声明主端口,根据 RpcClient#resolveServerInfo的逻辑,会设定主端口为8848,那么SdkGrpcPort则会是9848,而原本应与443端口的通信也不会受影响

  • 方法4: 关闭 nacos 的 grpc端口 或 修改 nacos 的通信协议为nacos/http协议 (本方法,未亲测)
Nacos 默认情况下会使用 gRPC 协议进行数据通信。如果你需要关闭 gRPC,可以通过修改 Nacos 的配置文件来实现。

具体步骤:
Step1 找到 Nacos 服务端的配置文件application.properties或bootstrap.properties。在配置文件中添加或修改以下配置项:
	# 关闭gRPC
	nacos.grpc.enabled=false

Step2 重启 Nacos 服务端


修改客户端配置文件
方式1:在您的应用程序中,如果使用了Nacos作为配置中心或服务发现组件,通常会有一个Nacos客户端的配置文件(如application.properties或bootstrap.properties),在此文件中添加或修改以下配置项:
	nacos.client.protocol=nacos
	这里通过指定nacos.client.protocol属性为nacos,可以使得客户端优先使用原生的Nacos协议进行通信,而非gRPC协议。


方式2:Spring Cloud Alibaba项目。如果您使用的是Spring Cloud Alibaba项目,可以在配置文件中加入:
	# 值可能为 nacos ,也可能是 http (未亲测)
	spring.cloud.nacos.config.protocol=nacos
	spring.cloud.nacos.discovery.protocol=nacos
	
	这两行配置分别用于配置Nacos Config和Nacos Discovery组件,确保它们都采用原生Nacos协议通信。

注意,关闭 gRPC 可能会影响 Nacos 的一些功能,如服务注册与发现、配置管理等。在生产环境中,通常建议保持 gRPC 开启状态。关闭 gRPC 应谨慎进行,并确保你明白这可能带来的后果。
方法4,参考自 : nacos 2.x 以后如何关闭 grpc 通信? - 阿里云 / Nacos2.1 如何禁用GRPC? - 阿里云

推荐文献

【强烈推荐】

  • 关键 需求/ISSUE

U 关键源码分析

com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy
	含属性 : com.alibaba.nacos.common.remote.client.RpcClient
	
--> com.alibaba.nacos.common.remote.client.RpcClient
	含属性 : com.alibaba.nacos.common.remote.client.Connection
	
--> com.alibaba.nacos.common.remote.client.Connection
	含属性 : com.alibaba.nacos.common.remote.client.RpcClient.ServerInfo

--> com.alibaba.nacos.common.remote.client.RpcClient.ServerInfo
	protected String serverIp;
    protected int serverPort;

com.alibaba.nacos.client.naming.core.ServerListManager

com.alibaba.nacos.common.remote.client.RpcClient

  • com.alibaba.nacos.common.remote.client.RpcClient#resolveServerInfo

  • com.alibaba.nacos.common.remote.client.RpcClient#request(com.alibaba.nacos.api.remote.request.Request, long)

com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder : 加载远程配置数据

  • springcloud 应用是否拿到了 nacos 配置中心的配置文件数据的关键代码
com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData
    ...
    com.alibaba.nacos.api.config.ConfigService#getConfig
    ...
  • spring cloud 集成 注册中心和配置中心 关键配置文件: bootstrap.yml

X 参考文献

  • Nacos
posted @ 2024-09-07 13:57  千千寰宇  阅读(543)  评论(0)    收藏  举报