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 方法就可以将客户端实例注册进去了!!!


目录
相关文章
|
6天前
|
SpringCloudAlibaba 负载均衡 Java
【微服务 SpringCloudAlibaba】实用篇 · Nacos注册中心
【微服务 SpringCloudAlibaba】实用篇 · Nacos注册中心
21 3
|
6天前
|
安全 Linux Nacos
如何使用公网地址远程访问内网Nacos UI界面查看注册服务
如何使用公网地址远程访问内网Nacos UI界面查看注册服务
25 0
|
6天前
|
负载均衡 Cloud Native Java
Nacos 注册中心(2023旧笔记)
Nacos 注册中心(2023旧笔记)
20 0
|
6天前
|
Dubbo Java 应用服务中间件
深度剖析:Dubbo使用Nacos注册中心的坑
2020年笔者在做微服务部件升级时,Dubbo的注册中心从Zookeeper切换到Nacos碰到个问题,最近刷Github又有网友提到类似的问题,就在这篇文章里做个梳理和总结。
深度剖析:Dubbo使用Nacos注册中心的坑
|
6天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
|
6天前
|
Dubbo 关系型数据库 MySQL
nacos常见问题之命名空间配置数据上线修改如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
104 1
|
6天前
|
存储 运维 监控
NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
【2月更文挑战第33天】NACOS 配置中心和注册中心是分两个集群部署还是放在一个集群中
92 2
|
5天前
|
SpringCloudAlibaba 应用服务中间件 Nacos
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心(下)
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心
18 0
|
6天前
|
JSON SpringCloudAlibaba Java
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心(上)
【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心
22 1
|
6天前
|
Nacos
nacos 配置页面的模糊查询
nacos 配置页面的模糊查询

热门文章

最新文章