Nacos源码学习计划-Day02-客户端自动注册和客户端心跳检测原理
ZealSinger 发布于 阅读:131 技术文档
如何找到源码阅读入口
对于一般的业务项目,其实我们可能是根据需要修改的业务看对应的接口功能,当自己学习的时候,可能是根据前端的页面进行一点点的一个接口一个接口的看,这个其实对于这种中间件/框架的源码而言,其实不是很实用。
类似于Nacos源码,我们首先采用猜的方式进行,最好是结合着当前服务的功能来猜。Nacos我们知道,他的作用是配置中心和注册中心,我们自己的一个项目,引入Nacos的依赖之后,在启动的时候就会自动的注册到Nacos上,是不是就可以猜测:Naocs肯定是监听了一个注册连接,让后对于我们服务的注册需求进行处理然后缓存,那么对于这个注册连接功能,肯定是依赖中会被自动注入,才能说我们的服务启动的时候,也顺带一起进行注册中心的注册
我们拿来一个需要Nacos依赖服务,通过Maven来查看一下nacos-discovery依赖里面的内容
(因为Spring在新版本中移除了META/INF/spring.factories,转而使用autoconfiguration.imports,所以这里新版本和旧版本Nacos中查看自动装配类不一样)
在高版本中,找autoconfiguration.imports文件,如下
在低版本中找META/INF/spring.factories,如下
可以看到,这里所写的全类名对应的类,都是Nacos一开始就需要加载的,所以这里就可以成为我们的阅读源码的入口,根据名字判断,这里有NacosServiceRegistryAutoConfiguration和注册相关的 ;NacosDiscoveryAutoConfiguration和服务发现相关的,对于我们有很好的指引
启动Nacos服务
自然,能本地通过跑代码的方式启动Nacos,对于我们学习Nacos源码是必不可少的,所以我们要能本地跑起Nacos服务。
启动服务自然就需要找到服务的启动类,一眼望去,Naocs的模块那么多,怎么找启动类?
我们可以利用nacos-server.jar
包,把 Jar 进行解压后,在META-INF
目录下有MANIFEST.MF
文件,打开后就可以看到Start-Class:com.alibaba.nacos.Nacos
,然后再 IDEA 搜索一下这个类,就找到啦!这个也是属于看源码技巧之一!
然后我们要单点模式启动Nacos,所以还需要加一个JVM参数
-Dnacos.standalone=true
启动之后,能成功跑起来,能访问Nacos默认的管理页面就基本没问题,最好找一个SpringBoot服务注册上去试一下
成功跑起来后,就可以开始我们的源码学习了
Nacos客户端自动注册的原理
我们首先来了解一下客户端自动注册的原理。从哪里开始看?前面有说到的imports文件中,可以看到有个自动配置类为NacosServiceRegistryAutoConfiguration,从名字上分析知道,这个和我们的自动注册肯定是有关系的,那么我去找找这个部分的源码
我们CTRL + 数标左键 跳转到NacosServiceRegistryAutoConfiguration这个配置类的代码中,可以看到,在这个配置类中注册了三个Bean,分别是NacosServiceRegister ; NacosRegistration ; NacosAutoServiceRegistration,我们分别来分析这个几个类
客户端为何会触发自动注册到Nacos
我们先来看NacosServiceRegistry
// 1.4.1的Bean注册的时候代码如下
public NacosServiceRegistry NacosServiceRegistry(
NacosDiscoveryProperties NacosDiscoveryProperties) {
return new Nacos ServiceRegistry( Nacos DiscoveryProperties);
}
可以看到,注册的该Bean需要借助到 NacosDiscoveryProperties 这个Bean作为参数,然后我们来看看这个Bean定义,可以看到,其成员就是我们引入Nacos作为服务中心的时候yml配置文件中所需要的参数
在这个类对象中,可以看到一个成员方法register,register的方法逻辑我们暂且不细看,大致看一眼发现,需要一个Registration对象为入参,主要逻辑是获取一些参数例如nameService , serviceId ,group等等,最后执行了一个namingService.registerInstance(serviceId, group, instance)方法,从这个方法名可以看出来,实际上的注册真正逻辑应该就是这个方法而来
到这里其实看代码的时候,会存在很多问题,入参Registration对象是什么?怎么来的?其余的所需要的参数信息怎么来的?registerInstance方法的底层逻辑是什么?
可以看到这里有很多的问题,但是我们还是和之前说过的,看源码需要有目的性,我们的当前的主线任务是弄懂客户端是如何自动注册的,既然知道了registerInstance方法是注册的方法,那么就需要知道在哪里调用了register方法,因为只有调用register方法的地方才会触发registerInstance方法。
对于上面的非主线任务,我们可以先放一放,如果执着于非主线任务,就会越看越深然后混乱,跳不出来了
我们此时回到刚刚的配置类中,可以看到第二个Bean,可以看到NacosRegistration这个Bean对象其实和上述register方法所需要的入参对象是同一个类型的。因为这里和主线无关,所以我们可以做个标记留个印象在这里,暂时先不细看
然后来看看第三个Bean对象,NacosAutoServiceRegistration从命名来看,就是和Auto自动注册相关的,而且其入参就是前面两个Bean,也就是说前面两个Bean参与了第三个Bean的构建,那么在NacosAutoServiceRegistration中就很有可能拥有前面两个Bean对象的相关属性的获取或者方法的调用
然后我们去看NacosAutoServiceRegistration的源码,可以看到其底层也有个register方法,在这个register方法中,会调用入参中的NacosServiceRegistry的register方法,也就是我们上面说到的那个register方法
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
// 调用了父类构造方法
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
// 父类构造方法
protected AbstractAutoServiceRegistration (ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
// 在这里把传入的第一个 Bean 对象,复制给了 this.serviceRegistry 属性
this .serviceRegistry = serviceRegistry;
this .properties = properties;
}
那么现在主线就是找NacosAutoServiceRegistration的register方法是在哪里调用的了
利用Idea的跳转功能发现,该register是对父类的register方法的重写,然后CTRL + ALT +F7可以发现,其在AbstractAutoServiceRegistration类的start方法中被调用
而该start方法在AbstractAutoServiceRegistration的onApplicationEvent方法中被调用(上述很多跳转的时候,都是存在两个及其以上的地方被调用,根据溯源判断,一些restart重启方法,还是同类方法调用肯定不是我们需要的)
而onApplicationEvent这个方法,是实现的Spring中的ApplicationListener接口的方法,也就是说溯源到最后就是利用Spring的监听机制
onApplicationEvent的入参标识监听的事件类型,这里是WebServerInitializedEvent代表服务已经初始化事件,在 Spring
容器启动的最后会执行 finishRefresh
方法,然后会发布一个事件,Nacos 客户端这里的监听器就会起作用,监听到这个事件最终调用start方法从而实现对于register方法的调用
那么到目前位置,我们知道了客户端自动注册的基本核心链路了,换句话而言就是自动执行register方法的链路
整个流程简单来说:
-
一开始我们通过
spring.factories
文件,找到了一个注册相关的Configuration
配置类,这个配置类里面定义了三个 Bean 对象。 -
创建第三个 Bean 对象,需要第一个、第二个 Bean 对象作为参数传进去,
第一个
Bean 对象里面就有真正的register
方法,并且赋值给了第三个 Bean 对象,父类中的serviceRegistry
属性。 -
在第三个 Bean 对象的父类当中,有实现 Spring
监听器
方法,所以在 Spring 容器启动的时候,会发布监听事件,从而执行 Nacos 注册的逻辑。
register方法的逻辑(客户端如何进行的注册)
现在就是看register方法的实际逻辑了
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
// 如下开始获取几个有关的参数
// 第一个NamingService 对象 内部包含了注册实例的真正方法 registerInstance namingService()方法内部的具体逻辑一下字看不明白,但是可以看到内部其实用了单例模式,还是使用了volatile的双重锁模式
NamingService namingService = namingService();
// 这个就是利用入参获取 服务ID/服务名称
String serviceId = registration.getServiceId();
// 获取服务分组 也就是Nacos管理平台上能看到的那个分组信息
String group = nacosDiscoveryProperties.getGroup();
// 获取Instance一个实例对象 该对象中封装了IP,PORT,Weigth权重,enable是否可用,healthy健康状态等等属性
Instance instance = getNacosInstanceFromRegistration(registration);
try {
/*
之前有说到,这个是最核心的方法,真正的注册实例的操作
该方法来自于NamingService接口,该接口也只有一个实现类NacosNamingService
*/
namingService.registerInstance(serviceId, group, instance);
// 下面的都可以不要看了暂时
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
if (nacosDiscoveryProperties.isFailFast()) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
else {
log.warn("Failfast is false. {} register failed...{},", serviceId,
registration.toString(), e);
}
}
}
我们跳入到registerInstance方法中,发现内容如下
/*
在高版本Nacos中,进行了逻辑整合,可能你看到的registerInstance方法源码
就下面两行代码逻辑 第一行逻辑和我们下面的第一行逻辑一样 其实就是一个判断心跳合理性的一个方法,暂时不需要过多理会
第二行逻辑就是注册实例,你会发现会有三个实现类的跳转,和我们下面逻辑类似的,该方法在NamingHttpClientProxy中的实现,将下面的除了第一行之外的逻辑都整合到了clientProxy.registerService·
NamingUtils.checkInstanceIsLegal(instance);
clientProxy.registerService(serviceName, groupName, instance);
*/
public void registerInstance(String serviceName, String groupName, Instance instance) throws Nacos Exception {
// 第一行代码也是检查什么,先不管,抓主线
NamingUtils.checkInstanceIsLegal(instance);
// 获取了 分组服务名字,具体做什么也暂时不知道
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
// 判断了一个属性
if (instance.isEphemeral()) {
// 如果 isEphemeral 为true, buildBeatInfo 翻译过来就是:构建心跳信息
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 添加心跳信息
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// 上面代码都是分支代码,有个印象就行,现在没必要都点进去看
// 核心在这里,又调用了 serverProxy 的注册方法
serverProxy.registerService(groupedServiceName, groupName, instance);
}
然后看registerService的逻辑(在高版本中对应的在NamingHttpClientProxy中),其逻辑如下
public void registerService(String serviceName, String groupName, Instance instance) throws Nacos Exception {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,
instance);
// 创建个Map 准备组装参数
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());
// ip、port就是刚刚那个instance里面的属性
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()));
// 请求地址、参数、Post,这很明显就是HTTP呀
// UtilAndComs.NacosUrlInstance 这行代码解释一下
// 这里是UtilAndComs常量类 NacosUrlInstance是/Nacos/v1/ns/instance
reqApi(UtilAndComs.NacosUrlInstance, params, HttpMethod.POST);
}
// 高版本中的代码逻辑如下 整体上其实是类似的 就是map中某些参数的格式和内容不太一样 然后就是随着功能的拓展新增了一些key-value键值对
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,
instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);// 封装为groupName@@serviceName
if (instance.isEphemeral()) {
throw new UnsupportedOperationException(
"Do not support register ephemeral instances by HTTP, please use gRPC replaced.");
}
final Map<String, String> params = new HashMap<>(32);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, groupedServiceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put(IP_PARAM, instance.getIp());
params.put(PORT_PARAM, String.valueOf(instance.getPort()));
params.put(WEIGHT_PARAM, String.valueOf(instance.getWeight()));
params.put(REGISTER_ENABLE_PARAM, String.valueOf(instance.isEnabled()));
params.put(HEALTHY_PARAM, String.valueOf(instance.isHealthy()));
params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral()));
params.put(META_PARAM, JacksonUtils.toJson(instance.getMetadata()));
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
}
通过上面的分析,我们其实就已经可以了解到,register方法的底层其实就是封装了HTTP请求参数,然后再最后的reqApi()方法中发起HTTP请求
如果你有看过Nacos的文档,并且尝试手动注册服务实例到Nacos,那么这部分内容应该比较好理解,可以看到,这个部分其实也就是主动注册实例逻辑的实现,这个也是属于Nacos结构中OpenAPI部分对外提供的功能接口之一
reqApi方法链路分析(支线任务)
reqApi中,利用传入的参数UtilAndComs.nacosUrlInstance作为方法URL,params作为请求体内容,HttpMethod.POST指定请求形式
reqApi的底层是调用的callServer方法,这个方法内会调用内部的nacosRestTemplate通过exchangeForm方法中的execute方法执行HTTP请求
可以看到executor方法有三个实现类,DefaultXXX是利用的Apache实现的HTTP请求相关功能 ; JdkXXX自然就是利用的JDK实现的 ; InterXXX是一个拓展功能的HTTPClientRequest
Nacos客户端如何发送服务心跳
在之前的逻辑中我们可以看到(1.4.1版本),在registerService中有这么一段逻辑
public void registerInstance(String serviceName, String groupName, Instance instance) throws Nacos Exception {
NamingUtils.checkInstanceIsLegal(instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
// ephemeral 这个属性默认就是为true
if (instance.isEphemeral()) {
// 构建BeatInfo对象
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 添加BeatInfo对象
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
serverProxy.registerService(groupedServiceName, groupName, instance);
}
instance对象的isEphemeral()方法逻辑,就是返回成员属性ephemeral,该成员是个布尔类型的数据,默认为True(实际上这个属性是代表临时节点还是非临时节点),他的具体含义我们暂且先不讨论,我们看看if代码中的内容,首先是第一行代码即buildBeatInfo方法,其作用是构建BeatInfo对象,从命名可以看出来,和心跳有关
public BeatInfo buildBeatInfo(String groupedServiceName, Instance instance) {
// 从set方法中也能猜测到该对象包含了IP Port等信息
BeatInfo beatInfo = new BeatInfo() ;
beatInfo.setServiceName(groupedServiceName) ;
beatInfo.setIp(instance.getIp()) ;
beatInfo.setPort(instance.getPort()) ;
beatInfo.setCluster(instance.getClusterName()) ;
beatInfo.setWeight(instance.getWeight()) ;
beatInfo.setMetadata(instance.getMetadata()) ;
beatInfo.setScheduled(false) ;
// 注意点,下面会仔细讲
beatInfo.setPeriod(instance.getInstanceHeartBeatInterval()) ;
return beatInfo ;
}
然后我们看最后那个setPeriod的逻辑,可以看到方法getInstanceHeartBeatInterval返回一个常量,默认值为5000(一个时间参数 5000ms),将其赋值给了BeatInfo的period成员
public static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5);
// 这里 getInstanceHeartBeatInterval() 的返回值是5000
beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
// 读取常量
public long getInstanceHeartBeatInterval() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,Constants.DEFAULT_HEART_BEAT_INTERVAL);
}
然后是if代码块中的第二行代码addBeatInfo方法
// 添加BeatInfo对象
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
// addBeatInfo 方法具体实现
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
NAMING_LOGGER.info(" [BEAT] adding beat: {} to beat map.", beatInfo) ;
String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()) ;
BeatInfo existBeat = null ;
//fix #1733
if ((existBeat = dom2Beat.remove(key)) != null) {
existBeat.setStopped(true) ;
}
dom2Beat.put(key, beatInfo) ;
// 以上代码都是分支代码,第一遍不需要详细去看
// 主线代码
executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS) ;
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()) ;
}
我们直接抓紧主线任务,从倒数第二行开始看,发现new了一个BeatTask对象
executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS) ;
// BeatTask类的定义 可以看到实现了Runnable接口 也就是说这个是个任务对象
class BeatTask implements Runnable {
BeatInfo beatInfo ;
// 可以利用BeatInfo进行构建
public BeatTask(BeatInfo beatInfo) {
this.beatInfo = beatInfo ;
}
public void run() {
// 判断是否需要停止
if (beatInfo.isStopped()) {
return ;
}
// 获取下一次执行的时间,也就是我们上面设置那个常量5000ms,同样还是5s
long nextTime = beatInfo.getPeriod() ;
try {
// serverProxy.sendBeat 发送心跳,这里就很关键,这个方法内部其实也是利用了requApi()方法发送了一个HTTP请求
JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled) ;
// 获取服务端返回的code状态码
int code = NamingResponseCode.OK ;
if (result.has(CommonParams.CODE)) {
code = result.get(CommonParams.CODE).asInt() ;
}
// 如果code = RESOURCE_NOT_FOUND 没有找到
// 很可能是之前注册的信息,已经被 Nacos 服务端移除了,所以返回这个错误信息
if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
// 然后重新组装参数,并且重新发起注册请求
Instance instance = new Instance() ;
instance.setPort(beatInfo.getPort()) ;
instance.setIp(beatInfo.getIp()) ;
instance.setWeight(beatInfo.getWeight()) ;
instance.setMetadata(beatInfo.getMetadata()) ;
instance.setClusterName(beatInfo.getCluster()) ;
instance.setServiceName(beatInfo.getServiceName()) ;
instance.setInstanceId(instance.getInstanceId()) ;
instance.setEphemeral(true) ;
try {
// 重新注册
serverProxy.registerService(beatInfo.getServiceName(),
NamingUtils.getGroupName(beatInfo.getServiceName()), instance) ;
}
}
}
// 又重新放入延迟任务当中,并且还是5秒,所以一直是个循环的状态
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS) ;
}
}
可以看到上述方法中执行了sendBeat这个很关键的方法,其源码内容如下
public JsonNode sendBeat(BeatInfo beatInfo, boolean lightBeatEnabled) throws Nacos Exception {
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());
}
// 组装参数 Map<String, String> params = new HashMap<String, String>(8);
Map<String, String> bodyMap = new HashMap<String, String>(2);
if (!lightBeatEnabled) {
bodyMap.put("beat", JacksonUtils.toJson(beatInfo));
}
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
params.put(CommonParams.CLUSTER_NAME, beatInfo.getCluster());
params.put("ip", beatInfo.getIp());
params.put("port", String.valueOf(beatInfo.getPort()));
// 发起请求,参数描述如下图 String result = reqApi(UtilAndComs. Nacos UrlBase + "/instance/beat", params, bodyMap, HttpMethod.PUT);
return JacksonUtils.toObj(result);
}
可以看到,这个过程其实也是个发送HTTP请求的过程,和官方API中提到的发送实例心跳是一样的,那么结合上述的客户端自动注册原理和上述逻辑的分析,我们知道,客户端心跳响应是BeatTask这个线程类实现的,客户端在发起服务注册的期间/时候,会通过buildBeatInfo创建心跳实例BeatInstance,然后通过addBeatInfo方法添加第一个心跳任务放到任务队列中,心跳任务的实质就是往Nacos服务发送一个心跳检测的HTTP请求,而每个心跳任务在结束之前都会将下一个心跳任务放到队列中,从而实现类似于循环的定时任务的效果,告诉Nacos我还活着
结合上述的自动注册,整个流程就是如下
/*
这里可以提前剧透一下,你在看1.14版本和高版本的registerService方法中,在if代码块中,两者的逻辑是不同的
*/
// 1.4.1版本中
if (instance.isEphemeral()) {
// 构建BeatInfo对象
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 添加BeatInfo对象
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// 高版本中
if (instance.isEphemeral()) {
throw new UnsupportedOperationException("Do not support register ephemeral instances by HTTP, please use gRPC replaced.");
}
/*
你会发现上述两个if块中的代码确实是差距有点大,其实从Ephemeral的意思以及高版本中的抛出的异常信息来看,就可以知道原因
Nacos 2.0 及以上版本,不再支持通过 HTTP 协议来注册临时实例(ephemeral instance)了。
那么功能是如何实现的呢?
1. 架构演进:从 HTTP 到 gRPC
Nacos 2.0 引入了全新的双向通信架构,核心是基于 gRPC 的长连接。这个连接一旦建立,就实现了客户端和服务器之间的双向流(Bi-directional streaming)。
2. 功能替代:心跳被“连接状态”取代
在新的架构下:
注册请求:虽然你调用的 registerInstance 方法看起来没变,但在底层,客户端会选择使用 gRPC 协议(而不是 HTTP)将注册信息发送给服务器。
健康检查:不再需要客户端主动发送心跳。那个建立的 gRPC 长连接本身就是一个健康状态的象征。只要这个连接是健康的,服务器就认为客户端是健康的。如果连接断开(比如客户端宕机、网络故障),服务器会立刻感知到,并迅速(秒级)将对应的服务实例标记为不健康或删除。
这就是那部分“缺失”的心跳代码的真正去向——它被 gRPC 长连接的连接状态管理机制所取代。 这种方式的优势非常明显:
实时性更高:服务器能立刻感知客户端下线,而不需要等待 15 或 30 秒的心跳超时。
压力更小:减少了大量重复的 HTTP 心跳请求,降低了服务器和网络的负载。
功能更强大:为服务端主动推送(如配置变更、服务列表变化)提供了通道。
3. 为何还保留 HTTP 注册的代码?
会注意到高版本代码中仍然有 serverProxy.registerService 的逻辑。这是为了兼容持久化实例(persistent instance)。
临时实例(Ephemeral):默认类型,生命周期与客户端绑定,客户端下线则实例自动注销。必须使用 gRPC 协议。
持久化实例(Persistent):生命周期与客户端解耦,即使客户端下线,实例也会保留在服务器上,其健康检查由 Server 端主动发起(类似 Nacos 对 MySQL 等数据源的健康检查)。这种实例仍然可以使用 HTTP 协议注册。
*/
文章标题:Nacos源码学习计划-Day02-客户端自动注册和客户端心跳检测原理
文章链接:https://zealsinger.xyz/?post=27
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自ZealSinger !
如果觉得文章对您有用,请随意打赏。
您的支持是我们继续创作的动力!

微信扫一扫

支付宝扫一扫