一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情
图解+源码讲解 Nacos 客户端下线流程
千里之行,始于足下 —— 老子 Nacos 源码分析系列相关文章
- 从零开始看 Nacos 源码环境搭建
- 图解+源码讲解 Nacos 客户端发起注册流程
- 图解+源码讲解 Nacos 服务端处理注册请求逻辑
- 图解+源码讲解 Nacos 客户端下线流程
- 图解+源码讲解 Nacos 服务端处理下线请求
- 图解+源码讲解 Nacos 客户端发起心跳请求
- 图解+源码讲解 Nacos 服务端处理心跳请求
- 图解+源码讲解 Nacos 服务端处理配置获取请求
核心思想
其实要想客户端下线的话,就是将心跳任务停止,不发送心跳给服务端,那么服务端自然而然的就知道客户端已经不好用了,已经死掉了
源码入口
这个 destroy方法,在客户端毁掉之前调用了 stop方法 ,那我们就看看这个 stop 方法
服务停止 stop 方法
一看这个就是实例下线的方法,deregister
public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
// 实例下线方法
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
// 关闭一些方法,比如心跳任务、命名空间代理等
this.serviceRegistry.close();
}
}
实例下线方法 deregister
protected void deregister() {
this.serviceRegistry.deregister(getRegistration());
}
其实调用的就是 NacosServiceRegistry#deregister方法进行服务下线
@Override
public void deregister(Registration registration) {
log.info("De-registering from Nacos Server now...");
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No dom to de-register for nacos client...");
return;
}
// 获取命名空降
NamingService namingService = namingService();
// 获取注册中心服务的ID
String serviceId = registration.getServiceId();
// 获取组,默认就是 default
String group = nacosDiscoveryProperties.getGroup();
// 通过 namingService 进行客户端实例下线
try {
namingService.deregisterInstance(serviceId, group, registration.getHost(),
registration.getPort(), nacosDiscoveryProperties.getClusterName());
}
catch (Exception e) {
log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",
registration.toString(), e);
}
log.info("De-registration finished.");
}
客户端实例下线 deregisterInstance
调用的就是 namingService的 deregisterInstance方法
@Override
public void deregisterInstance(String serviceName, String groupName, String ip,
int port, String clusterName)
throws NacosException {
// 构建一个实例信息
Instance instance = new Instance();
instance.setIp(ip);
instance.setPort(port);
instance.setClusterName(clusterName);
// 进行服务下线
deregisterInstance(serviceName, groupName, instance);
}
@Override
public void deregisterInstance(String serviceName, String groupName, Instance instance)
throws NacosException {
// 是否是虚拟节点
if (instance.isEphemeral()) {
// 在心跳执行器中移除心跳信息
beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName),
instance.getIp(), instance.getPort());
}
// 下线实例
serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName),
instance);
}
移除 beatReactor 中心跳信息
将 beatInfo 的 stop 状态设置成true,那么就不会发送心跳了,因为我们分析发送心跳的时候会进行当前心跳信息状态的判断,如果是true那么就不会发送了
图中就是发送心跳的判断
public void removeBeatInfo(String serviceName, String ip, int port) {
NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.",
serviceName, ip, port);
// 将心跳信息从 dom2Beat Map集合中移除
BeatInfo beatInfo = dom2Beat.remove(buildKey(serviceName, ip, port));
if (beatInfo == null) {
return;
}
// 设置心跳信息停止,在发起心跳的时候会进行判断beatInfo是否是stop的
beatInfo.setStopped(true);
// 指标监听
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
请求服务端下线服务实例
serverProxy#deregisterService通过这个方法进行服务实例下线
public static String webContext = "/nacos";
public static String nacosUrlBase = webContext + "/v1/ns";
public static String nacosUrlInstance = nacosUrlBase + "/instance";
public void deregisterService(String serviceName, Instance instance)
throws NacosException {
NAMING_LOGGER
.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}",
namespaceId, serviceName,instance);
// 构建访问信息
final Map<String, String> params = new HashMap<String, String>(8);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
// 请求服务端进行服务下线,DELETE 方法
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.DELETE);
}
所以访问路径是:/nacos/v1/ns/instance
最终关闭 一些后台资源
关闭一些没有用的资源服务,比如心跳线程池等
@Override
public void close() {
try {
// nacos 服务管理下线
nacosServiceManager.nacosServiceShutDown();
}
catch (NacosException e) {
log.error("Nacos namingService shutDown failed", e);
}
}
public void nacosServiceShutDown() throws NacosException {
// 通过命名空间服务关闭一些资源服务
this.namingService.shutDown();
namingService = null;
namingMaintainService = null;
}
// 关闭一些相关的资源服务
@Override
public void shutDown() throws NacosException {
beatReactor.shutdown();
hostReactor.shutdown();
serverProxy.shutdown();
}
小结
就是将客户端的心跳服务关闭,之后访问服务端进行客户端端下线请求,最终再关闭一些资源服务
其他系列源码分析
feign 源码分析系列相关文章
- 图解+源码讲解 Feign 如何将客户端注入到容器中
- 图解+源码讲解动态代理获取 FeignClient 代理对象
- 图解+源码讲解代理对象 ReflectiveFeign 分析
- 图解+源码讲解 Feign 如何选取指定服务
- 图解+源码讲解 Feign 请求的流程
ribbon 源码分析系列相关文章 - Ribbon 原理初探
- 图解+源码讲解 Ribbon 如何获取注册中心的实例
- 图解+源码讲解 Ribbon 服务列表更新
- 图解+源码讲解 Ribbon 服务选择原理
- 图解+源码讲解 Ribbon 如何发起网络请求
eureka 源码分析系列相关文章
- eureka-server 项目结构分析
- 图解+源码讲解 Eureka Server 启动流程分析
- 图解+源码讲解 Eureka Client 启动流程分析
- 图解+源码讲解 Eureka Server 注册表缓存逻辑
- 图解+源码讲解 Eureka Client 拉取注册表流程
- 图解+源码讲解 Eureka Client 服务注册流程
- 图解+源码讲解 Eureka Client 心跳机制流程
- 图解+源码讲解 Eureka Client 下线流程分析
- 图解+源码讲解 Eureka Server 服务剔除逻辑
- 图解+源码讲解 Eureka Server 集群注册表同步机制