Nacos 服务注册概述及客户端注册实例源码分析(一)(中)

简介: Nacos 服务注册概述及客户端注册实例源码分析(一)(中)

NamingClientProxyDelegate 实现

继续追踪 NamingClientProxyDelegate#registerService 方法的具体实现,代码如下:

public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
  getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}

真正调用注册服务方并不是代理实现类,而是通过当前实例是否为瞬时对象,来选择对应的客户端代理进行请求:

private NamingClientProxy getExecuteClientProxy(Instance instance) {
  return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}

如果当前实例为瞬时对象,则采用 gRPC 协议(NamingGrpcClientProxy)进行请求,否则采用(NamingHttpClientProxy)进行请求

private boolean ephemeral = true;

默认为瞬时对象,也就是说,2.0 版本默认采用了 gRPC 协议与 Nacos 服务进行交互

NamingGrpcClientProxy 实现

主要关注一下 registerService 方法的实现,主要作了以下两件事情:

1、缓存当前注册的实例信息用于恢复,缓存的数据结构为 ConcurrentMap<String, InstanceRedoData>:Key->groupName@@serviceName,value->前面封装的实例信息「Instance、groupName、serviceName」

2、另外一件事情就是封装了具体的参数,基于 gRPC 进行服务的调用和结果的处理

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);
  // 基于 gRPC 进行服务的调用
  doRegisterService(serviceName, groupName, instance);
}
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);
  // 标识该服务已被注册
  redoService.instanceRegistered(serviceName, groupName);
}

Nacos 入口源码流程图

Nacos 客户端实例注册源码案例分析

实际上我们在真实的生产的环境中,若让某个服务注册到 Nacos 中,首先需要引入依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在引入这个依赖之后,找到 spring-boot 自动装配文件:META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
  com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.context.ApplicationListener=\
  com.alibaba.cloud.nacos.discovery.logging.NacosLoggingListener

然后再 spring-boot 自动装配的功能,自动装配的源码实现可以看博主博客:SpringBoot 自动装配流程及源码剖析

首先找到加载 EnbaleAutoConfiguration 对应的类,然后在这里我们就能看见很多 Nacos 相关的内容,一般这种文件都会找 Auto 关键字的文件来进行查看,然后我们要现在要了解的是客户端注册,所以我们应该要找的是:NacosServiceRegistryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = {"spring.cloud.service-registry.auto-registration.enabled"},matchIfMissing = true)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class, NacosDiscoveryAutoConfiguration.class})
public class NacosServiceRegistryAutoConfiguration {
    public NacosServiceRegistryAutoConfiguration() {
    }
    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosServiceManager, nacosDiscoveryProperties);
    }
    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosRegistration nacosRegistration(ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers, NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration((List)registrationCustomizers.getIfAvailable(), nacosDiscoveryProperties, context);
    }
    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
}

在此类中有很多的 Bean 组件,这些都是 spring 容器启动时自动进行注入的,一般情况下我们可能会看每一个 Bean 组件初始化时具体作了什么,但是实际上这里最核心的是 NacosAutoServiceRegistration 类,注入它时还需要三个参数,这三个参数会提前被加载,尤其是 NacosServiceRegistry 这个参数后续中也会用到

NacosAutoServiceRegistration

通过以上的类图可以看出,NacosAutoServiceRegistration 继承自 AbstractAutoServiceRegistration,而这个类型又实现了 ApplicationListener 接口,一般只要实现了 ApplicationListener 接口的,只需要关注它的 onApplicationEvent 方法的实现逻辑,它会帮我们完成一些初始化加载的工作,该方法是在项目启动时-容器进行初始化时进行调用的

public void onApplicationEvent(WebServerInitializedEvent event) {
  this.bind(event);
}
@Deprecated
public void bind(WebServerInitializedEvent event) {
  ApplicationContext context = event.getApplicationContext();
  if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
    this.port.compareAndSet(0, event.getWebServer().getPort());
    this.start();
  }
}

最终在 start 方法中调用 register 方法进行服务注册

public void start() {
  if (!this.isEnabled()) {
    if (logger.isDebugEnabled()) {
      logger.debug("Discovery Lifecycle disabled. Not starting");
    }
  } else {
    if (!this.running.get()) {
      this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
      this.register();
      if (this.shouldRegisterManagement()) {
        this.registerManagement();
      }
      this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
      this.running.compareAndSet(false, true);
    }
  }
}

ServiceRegistry#register

分析到这里,可以得知真实的服务注册的入口和具体调用那个方法来注册

protected void register() {
  this.serviceRegistry.register(this.getRegistration());
}

但 serviceRegistry 实际上是一个接口,它的具体实现类是 NacosServiceRegistry,到这里就与之前分析到的源码连接在一起了,因为在这里调用了之前讲过的 registerInstance 实例注册方法,查看该类下的 register 方法🥱

public void register(Registration registration) {
  if (StringUtils.isEmpty(registration.getServiceId())) {
    log.warn("No service to register for nacos client...");
  } else {
    NamingService namingService = this.namingService();
    String serviceId = registration.getServiceId();
    String group = this.nacosDiscoveryProperties.getGroup();
    // 构建 Instance 实例
    Instance instance = this.getNacosInstanceFromRegistration(registration);
    try {
    // 向服务端注册此客户端
      namingService.registerInstance(serviceId, group, instance);
      log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
    } catch (Exception var7) {
      if (this.nacosDiscoveryProperties.isFailFast()) {
        log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
        ReflectionUtils.rethrowRuntimeException(var7);
      } else {
        log.warn("Failfast is false. {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
      }
    }
  }
}

接下来就是从已经分析过的链路进行调用了,如下:

NamingClientProxyDelegate#registerService—>NamingGrpcClientProxy#registerService

此段源码在标题「Nacos 客户端服务注册源码入口分析」分析过,在这不作过多阐述

NamingGrpcClientProxy#requestToServer

发送请求服务端进行注册实例,在此处会涉及到 rpcClient#request 方法调用

private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass) throws NacosException {
  try {
    request.putAllHeader(this.getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));
    Response response = this.requestTimeout < 0L ? this.rpcClient.request(request) : this.rpcClient.request(request, this.requestTimeout);
    if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {
      throw new NacosException(response.getErrorCode(), response.getMessage());
    }
    if (responseClass.isAssignableFrom(response.getClass())) {
      return response;
    }
    LogUtils.NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'", response.getClass().getName(), responseClass.getName());
  } catch (Exception var4) {
    throw new NacosException(500, "Request nacos server failed: ", var4);
  }
  throw new NacosException(500, "Server return invalid response");
}

此 RpcClient 对象是在 NamingGrpcClientProxy 构造函数中进行初始化的,随即会调用其下 start 方法启动,在里面会调用 connectToServer(serverInfo) 方法「主要操作:通过地址+端口来初始化 gRPC 连接信息后进行建立连接」

public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, Properties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException {
  super(securityProxy);
  this.namespaceId = namespaceId;
  this.uuid = UUID.randomUUID().toString();
  this.requestTimeout = Long.parseLong(properties.getProperty("namingRequestTimeout", "-1"));
  Map<String, String> labels = new HashMap();
  labels.put("source", "sdk");
  labels.put("module", "naming");
  this.rpcClient = RpcClientFactory.createClient(this.uuid, ConnectionType.GRPC, labels);
  this.redoService = new NamingGrpcRedoService(this);
  this.start(serverListFactory, serviceInfoHolder);
}
private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException {
  this.rpcClient.serverListFactory(serverListFactory);
  this.rpcClient.registerConnectionListener(this.redoService);
  this.rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder));
  this.rpcClient.start();
  NotifyCenter.registerSubscriber(this);
}

随即在 requestToServer 方法中调用 rpcClient#request 方法就可以将客户端实例注册进去了!!!


目录
相关文章
|
7月前
|
人工智能 Java API
Nacos 3.1.0 正式发布,支持 A2A 注册中心与 MCP 注册协议增强
3.1.0 发布核心全新功能-Agent 注册中心,助力构建基于 A2A 协议的多 Agent 协作的AI应用,同时 MCP 注册中心适配最新 MCP 官方注册中心协议及升级优化多项核心功能。
1902 67
|
10月前
|
网络协议 Shell PHP
简单的php版本nacos客户端
这是一个简单的 PHP 版本 Nacos 客户端,支持服务注册、配置发布、服务发现等功能。通过 Composer 安装,提供服务端与客户端示例代码,可快速集成至项目中。适用于基于 Nacos 的微服务架构开发,帮助实现服务治理与配置管理。
339 10
|
Cloud Native Java Nacos
springcloud/springboot集成NACOS 做注册和配置中心以及nacos源码分析
通过本文,我们详细介绍了如何在 Spring Cloud 和 Spring Boot 中集成 Nacos 进行服务注册和配置管理,并对 Nacos 的源码进行了初步分析。Nacos 作为一个强大的服务注册和配置管理平台,为微服务架构提供
4885 14
|
Java 网络安全 Nacos
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评。然而,“客户端不发送心跳检测”是使用中常见的问题之一。本文详细探讨了该问题的原因及解决方法,包括检查客户端配置、网络连接、日志、版本兼容性、心跳检测策略、服务实例注册状态、重启应用及环境变量等步骤,旨在帮助开发者快速定位并解决问题,确保服务正常运行。
284 5
|
网络安全 Nacos 开发者
Nacos作为流行的微服务注册与配置中心,“节点提示暂时不可用”是常见的问题之一
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。然而,“节点提示暂时不可用”是常见的问题之一。本文将探讨该问题的原因及解决方案,帮助开发者快速定位并解决问题,确保服务的正常运行。通过检查服务实例状态、网络连接、Nacos配置、调整健康检查策略等步骤,可以有效解决这一问题。
377 4
|
存储 Kubernetes 安全
Nacos-Controller 2.0:使用 Nacos 高效管理你的 K8s 配置
无论是使用 Nacos-Controller 实现配置的双向同步,还是直接在应用中接入 Nacos SDK 以获得更高级的配置管理特性,都能显著提升配置管理的灵活性、安全性和可维护性。使用 Nacos,您能够更好地管理和优化您的应用配置,从而提高系统的稳定性和可靠性。
994 50
|
存储 网络协议 Nacos
高效搭建Nacos:实现微服务的服务注册与配置中心
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台。它旨在帮助开发者更轻松地构建、部署和管理分布式系统,特别是在微服务架构中。
2610 81
高效搭建Nacos:实现微服务的服务注册与配置中心
|
JSON Java Nacos
SpringCloud 应用 Nacos 配置中心注解
在 Spring Cloud 应用中可以非常低成本地集成 Nacos 实现配置动态刷新,在应用程序代码中通过 Spring 官方的注解 @Value 和 @ConfigurationProperties,引用 Spring enviroment 上下文中的属性值,这种用法的最大优点是无代码层面侵入性,但也存在诸多限制,为了解决问题,提升应用接入 Nacos 配置中心的易用性,Spring Cloud Alibaba 发布一套全新的 Nacos 配置中心的注解。
1708 143
|
监控 Java 测试技术
Nacos 配置中心变更利器:自定义标签灰度
本文是对 MSE Nacos 应用自定义标签灰度的功能介绍,欢迎大家升级版本进行试用。
1231 212
|
存储 人工智能 测试技术
Nacos托管LangChain应用Prompts和配置,助力你的AI助手快速进化
AI 应用开发中,总有一些让人头疼的问题:敏感信息(比如 API-KEY)怎么安全存储?模型参数需要频繁调整怎么办?Prompt 模板改来改去,每次都得重启服务,太麻烦了!别急,今天我们就来聊聊如何用 Nacos 解决这些问题。

热门文章

最新文章