写点什么

Nacos 源码简析之 Nacos Client 自动注册原理

作者:道嗔
  • 2022 年 5 月 02 日
  • 本文字数:2735 字

    阅读完需:约 9 分钟

本文使用 nacos 版本 V1.4.1

看到源码这两字,或许感觉到高大上一点,其实,不然。本文只是借用 nacos 的源码讲讲 spring 框架在一些技术框架上的拓展点。

  • nacos client 的自动注册原理

Nacos Client 自动注册原理

什么是 nacos client 自动注册?

简而言之,在一个 web 环境下依赖了 nacos 的客户端 JAR,nacos 就会自动将该 web 服务自动注册到配置的对应 nacos 服务上。(即不需要在应用的启动类上添加 @EnableDiscoveryClient

依赖如下:

<dependency>	<groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
复制代码

添加依赖完成!

该如何开始查看源码呢?

  • 优秀的框架日志中都会有详细的说明

正常日志输出:

从上述的日志信息中可以捕获到一个比较关键的类:

com.alibaba.cloud.nacos.registry.NacosServiceRegistry


注意有些时候异常的信息反馈的内容更多:(解决问题是一名优秀程序员的重要衡量标准)

com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register

本次源码查看从异常信息中开始!


  • 断点,查看调用栈

查看调用栈信息。

NacosClient#main

-> SpringApplication#run

-> ...

-> AbstractApplicationContext#publishEvent

-> AbstractAutoServiceRegistration#onApplicationEvent

-> AbstractAutoServiceRegistration#start

-> .NacosAutoServiceRegistration#register

-> NacosServiceRegistry#register


以上就是调用栈,故此,可以了解到 spring cloud 规范基于事件监听机制这个扩展点为其他框架标准预留了一个扩展点。

故,spring-cloud-alibaba-nacos* 是实现了 spring cloud 这套规范,而不是 nacos 特有的,有理由相信 eureka 也是支持自动注册的。


PS: 本人相信,行文至此,有经验的同仁已经可以梳理完成 nacos 是如何基于 spring cloud 规范完成自动注册的。

后续高能警告,详解自动注册的过程,有点啰嗦,可以忽略跳过。

Nacos Client 自动注册过程详解

该小节主要从以下两点进行详解:

  • 什么时候自动注册

  • 如何注册

什么时候自动注册

什么时候注册,这个其实很容易回答,从上文了解到整个自动注册过程是基于事件监听机制进行完成,故此只需了解到监听的事件类型即可。

看本文的同仁可以回忆一下 spring -> spring boot 内置了哪些事件?

个人可以思考一下如果交由各位实现,认为是监听哪个事件比较好呢?

public abstract class AbstractAutoServiceRegistration<R extends Registration>		implements AutoServiceRegistration, ApplicationContextAware,		ApplicationListener<WebServerInitializedEvent> {    .... }
复制代码


答案揭晓: WebServerInitializedEvent: web 容器初始化完成事件。

故此专注在:onApplicationEvent() 方法上:

onApplicationEvent: 事件监听回调方法

bind: 绑定当前应用端口信息

this.port.compareAndSet(0, event.getWebServer().getPort());


start: 开始注册

发布预注册事件

this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));

发布注册完成事件

this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));

如何注册

如果注册,简而言之,就是搜集当前应用配置的信息,组装成一个 Instance 并按照对应的 serviceId 和 group 远程请求 Nacos Server 接口完成注册。

  • 核心接口/类:

  • NamingService

  • Instance

public class Instance implements Serializable {    private static final long serialVersionUID = -742906310567291979L;    private String instanceId; # 示例id 由group和serviceId拼接    private String ip;    private int port;    private double weight = 1.0D;    private boolean healthy = true;    private boolean enabled = true;    private boolean ephemeral = true;    private String clusterName;    private String serviceName;    private Map<String, String> metadata = new HashMap();}
复制代码
  • 核心概念:

  • serviceId: 即 spring.application.name

  • group: 注册的分组, 即 spring.cloud.nacos.discovery.group

  • namespace:注册的命名空间,即 spring.cloud.nacos.discovery.namespace

  • clusterName: 注册的集群名称, 即 即 spring.cloud.nacos.discovery.cluster-name

以上图片摘自:Nacos 架构


言归正传,开始按照源码说明:


// NacosNamingServicepublic void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {    NamingUtils.checkInstanceIsLegal(instance);    String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);    if (instance.isEphemeral()) {      	// 心跳检测        BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);        beatReactor.addBeatInfo(groupedServiceName, beatInfo);    }  	// 注册服务  	// serverProxy -> NamingProxy (底层包装了 rpc client: http 方式)    serverProxy.registerService(groupedServiceName, groupName, instance);}
复制代码


// NamingProxypublic void registerService(String serviceName, String groupName, Instance instance) throws NacosException {          NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,                     instance);
final Map<String, String> params = new HashMap<String, String>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, serviceName); params.put(CommonParams.GROUP_NAME, groupName); params.put(CommonParams.CLUSTER_NAME, instance.getClusterName()); params.put("ip", instance.getIp()); params.put("port", String.valueOf(instance.getPort())); params.put("weight", String.valueOf(instance.getWeight())); params.put("enable", String.valueOf(instance.isEnabled())); params.put("healthy", String.valueOf(instance.isHealthy())); params.put("ephemeral", String.valueOf(instance.isEphemeral())); params.put("metadata", JacksonUtils.toJson(instance.getMetadata())); // rpc 调用 // 后续就按照servers 循环一个一个遍历注册 reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST); }
复制代码


至此,其实还没完成,上面经埋了两个坑,就是发布的两个事件:

InstancePreRegisteredEvent : 主要做缓存

InstanceRegisteredEvent: 监控检查相关


详见: NacosServiceManagerDiscoveryClientHealthIndicator


发布于: 刚刚阅读数: 3
用户头像

道嗔

关注

道可道,非常道 2018.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Nacos 源码简析之 Nacos Client 自动注册原理_源码_道嗔_InfoQ写作社区