当前位置: 首页 > news >正文

【Nacos】Nacos服务注册与发现 心跳检测机制源码解析

在前两篇文章,介绍了springboot的自动配置原理,而nacos的服务注册就依赖自动配置原理。

Nacos

在这里插入图片描述

Nacos核心功能点

服务注册 :Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。

服务心跳: 在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。

服务健康检查: Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它 的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复 发送心跳则会重新注册)

服务发现: 服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清 单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存

服务同步: Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。

客户端 服务注册&心跳发送

在客户端中,也就是开发的应用,包含引入有 nacos-discovery 而路径下包含有一个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,\

其中 NacosServiceRegistryAutoConfiguration 中引入了三个类 NacosServiceRegistryNacosRegistrationNacosAutoServiceRegistration 其中 NacosAutoServiceRegistration 继承了 AbstractAutoServiceRegistration

	public void onApplicationEvent(WebServerInitializedEvent event) {bind(event);}// 启动this.start();//注册服务register();

组装实例信息,ip 端口 服务权重 集群名字 源信息 以及是否

	private Instance getNacosInstanceFromRegistration(Registration registration) {Instance instance = new Instance();instance.setIp(registration.getHost());instance.setPort(registration.getPort());instance.setWeight(nacosDiscoveryProperties.getWeight());instance.setClusterName(nacosDiscoveryProperties.getClusterName());instance.setEnabled(nacosDiscoveryProperties.isInstanceEnabled());instance.setMetadata(registration.getMetadata());instance.setEphemeral(nacosDiscoveryProperties.isEphemeral());return instance;}
	// 注册实例namingService.registerInstance(serviceId, group, instance);// 服务名 组名 实例public 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);}// 实际就是调用 instance serverProxy.registerService(groupedServiceName, groupName, instance);}// 心跳发送线程executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);

发送心跳线程

所谓的客户端心跳,其实就是启动一个线程,然后定时给一个接口发送调用。

	class BeatTask implements Runnable {BeatInfo beatInfo;public BeatTask(BeatInfo beatInfo) {this.beatInfo = beatInfo;}@Overridepublic void run() {// 如果停止 直接返回if (beatInfo.isStopped()) {return;}// 获取下次时间long nextTime = beatInfo.getPeriod();// 实际就是调用服务端的一个心跳接口  /instance/beatJsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);// 如果结束,启动另外一个 开始下次的心跳线程发送executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);}}

在这里插入图片描述

nacos 服务注册

对于服务端来说,就是一个API接口

	public String register(HttpServletRequest request) throws Exception {// 准备服务实例final Instance instance = parseInstance(request);serviceManager.registerInstance(namespaceId, serviceName, instance);return "ok";}

注册实例其实就是三步、创建服务、获取服务、添加实例

    public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {createEmptyService(namespaceId, serviceName, instance.isEphemeral());Service service = getService(namespaceId, serviceName);addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);}

创建服务

    public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)throws NacosException {// 命名空间 服务名称Service service = getService(namespaceId, serviceName);if (service == null) {service = new Service();service.setName(serviceName);service.setNamespaceId(namespaceId);service.setGroupName(NamingUtils.getGroupName(serviceName));// now validate the service. if failed, exception will be thrownservice.setLastModifiedMillis(System.currentTimeMillis());service.recalculateChecksum();if (cluster != null) {cluster.setService(service);service.getClusterMap().put(cluster.getName(), cluster);}service.validate();// 这里是重点putServiceAndInit(service);if (!local) {addOrReplaceService(service);}}}private void putServiceAndInit(Service service) throws NacosException {// 添加服务putService(service);// 下面说心跳检测机制service.init();}

在这里插入图片描述
这里其实就是底层的注册表的数据结构了,这里使用双检查锁。分别是nameSpace、group、service、实例。

这里简单思考下,为什么要设计这么复杂。一个服务可能对应多个实例。没有问题。一个分组group 可能是在同一个公司有不同的组,比如订单、支付,每个组都有自己的服务。namespace则可以分为dev\test\prod 三个不同的组。

	// 注册表  如何提升private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();// 这里的机制其实就是 判断当前是否存在该nameSpaceId , 如果不存在的话,则创建一个CSLMpublic void putService(Service service) {if (!serviceMap.containsKey(service.getNamespaceId())) {// 添加锁 locksynchronized (putServiceLock) {if (!serviceMap.containsKey(service.getNamespaceId())) {serviceMap.put(service.getNamespaceId(), new ConcurrentSkipListMap<>());}}}// 将添加服务添加到nameSpaceId 添加服务serviceMap.get(service.getNamespaceId()).put(service.getName(), service);}
    // 添加实例public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)throws NacosException {// 构建一个keyString key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);Service service = getService(namespaceId, serviceName);synchronized (service) {List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);Instances instances = new Instances();instances.setInstanceList(instanceList);// 持久化服务consistencyService.put(key, instances);}}// 这里根据判断是否ephemeral 走保存内存还是磁盘持久化 private ConsistencyService mapConsistencyService(String key) {// AP CPreturn KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;}

这里其实选择的是 DistroConsistencyServiceImpl 另一个就是 RaftConsistencyServiceImpl 使用raft实现的数据持久化,这里先不介绍。

DistroConsistencyServiceImpl

    public void put(String key, Record value) throws NacosException {onPut(key, value);distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,globalConfig.getTaskDispatchPeriod() / 2);}// 这里调用put public void onPut(String key, Record value) {if (KeyBuilder.matchEphemeralInstanceListKey(key)) {Datum<Instances> datum = new Datum<>();datum.value = (Instances) value;datum.key = key;datum.timestamp.incrementAndGet();//将数据保存到内存中dataStore.put(key, datum);}if (!listeners.containsKey(key)) {return;}// 但是这里调用了一个任务notifier.addTask(key, DataOperation.CHANGE);}// 其实就是一个mapprivate Map<String, Datum> dataMap = new ConcurrentHashMap<>(1024);public void put(String key, Datum value) {dataMap.put(key, value);}@PostConstructpublic void init() {// 初始化 构造方法执行的时候 进行处理GlobalExecutor.submitDistroNotifyTask(notifier);}

从源码中可以看到,在类初始化的时候,创建一个任务异步进行执行。 其实就是将当前服务进行异步任务注册,可以提升性能。添加和获取任务。

源码精髓:很多开源框架为了提升操作性能会大量使用这种异步任务及内存队列操作,这些操作本省不需要写入立即返回成功,用这种方式可以提升操作性能很大帮助

   public class Notifier implements Runnable {// 保存服务private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024);// 阻塞队列 private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue<>(1024 * 1024);// 初始化的时候 添加一个任务到阻塞队列中public void addTask(String datumKey, DataOperation action) {if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {return;}if (action == DataOperation.CHANGE) {services.put(datumKey, StringUtils.EMPTY);}tasks.offer(Pair.with(datumKey, action));}@Overridepublic void run() {// 为什么异步设计 : 提升性能// 阻塞队列 ,会线程等待 wait// 并发、反射、网络、IOfor (; ; ) {// 异步处理Pair<String, DataOperation> pair = tasks.take();handle(pair);}}}

在这里插入图片描述

心跳检测机制

其实就是服务注册的时候 启动一个线程,然后检查所有实例的心跳检测,对于超过15s没有收到客户端心跳的实例会将它 的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复 发送心跳则会重新注册)

    /*** Init service.*/public void init() {// 心跳检查线程HealthCheckReactor.scheduleCheck(clientBeatCheckTask);for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {entry.getValue().setService(this);entry.getValue().init();}}// 初始化5S后执行,每5S执行一次public static void scheduleCheck(ClientBeatCheckTask task) {futureMap.putIfAbsent(task.taskKey(), GlobalExecutor.scheduleNamingHealth(task, 5000, 5000, TimeUnit.MILLISECONDS));}// public void run() {List<Instance> instances = service.allIPs(true);for (Instance instance : instances) {// 当前时间 减去 心跳超时时间if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {if (instance.isHealthy()) {// 健康状态instance.setHealthy(false);getPushService().serviceChanged(service);ApplicationUtils.publishEvent(new InstanceHeartbeatTimeoutEvent(this, instance));}}}}// then remove obsolete instances:for (Instance instance : instances) {if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {// delete instanceLoggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(),JacksonUtils.toJson(instance));deleteIp(instance);}}} }

至此,我们就基本上过了一遍,服务的注册 以及心跳检测机制,本篇主要是针对nacos1.4.1 的源码学习,关于后续的服务发现,以及2.X版本的源码 讲解,后续在继续。

在这里插入图片描述

相关文章:

【Nacos】Nacos服务注册与发现 心跳检测机制源码解析

在前两篇文章&#xff0c;介绍了springboot的自动配置原理&#xff0c;而nacos的服务注册就依赖自动配置原理。 Nacos Nacos核心功能点 服务注册 :Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务&#xff0c;提供自身的元数据&#xff0c;比如ip地址、端…...

python 66 个冷知识 0720

66个有趣的Python冷知识 一行反转列表 使用切片一行反转列表&#xff1a;reversed_list my_list[::-1] 统计文件单词数量 使用 collections.Counter 统计文件中每个单词的数量&#xff1a;from collections import Counter; with open(file.txt) as f: word_count Counter(f…...

利用PyTorch进行模型量化

利用PyTorch进行模型量化 目录 利用PyTorch进行模型量化 一、模型量化概述 1.为什么需要模型量化&#xff1f; 2.模型量化的挑战 二、使用PyTorch进行模型量化 1.PyTorch的量化优势 2.准备工作 3.选择要量化的模型 4.量化前的准备工作 三、PyTorch的量化工具包 1.介…...

Android 小白菜鸟从入门到精通教程

前言 Android一词最早出现于法国作家利尔亚当&#xff08;Auguste Villiers de l’Isle-Adam&#xff09;在1886年发表的科幻小说《未来的夏娃》&#xff08;L’ve future&#xff09;中。他将外表像人的机器起名为Android。从初学者的角度出发&#xff0c;通过通俗易懂的语言…...

php相关

php相关 ​ 借鉴了小迪安全以及各位大佬的博客&#xff0c;如果一切顺利&#xff0c;会不定期更新。 如果感觉不妥&#xff0c;可以私信删除。 默认有php基础。 文章目录 php相关1. php 缺陷函数1. 与2. MD53. intval()4. preg_match() 2. php特性1. php字符串解析特性2. 杂…...

uniapp上传功能用uni-file-picker实现

文章目录 html代码功能实现css样式代码 html代码 <uni-file-pickerselect"onFileSelected"cancel"onFilePickerCancel"limit"1"class"weightPage-upload-but"file-mediatype"image"></uni-file-picker><imag…...

【PPT笔记】1-3节 | 默认设置/快捷键/合并形状

文章目录 说明笔记1 默认设置1.1 OFFICE版本选择1.1.1 Office某某数字专属系列1.1.2 Office3651.1.3 产品信息怎么看 1.2 默认设置1.2.1 暗夜模式1.2.2 无限撤回1.2.3 自动保存&#xff08;Office2013版本及以上&#xff09;1.2.4 图片压缩1.2.5 字体嵌入1.2.6 多格式导出1.2.7…...

Qt中的高分辨率及缩放处理

写在前面 使用Qt开发界面客户端&#xff0c;需要考虑不同分辨率及缩放对UI界面的影响&#xff0c;否则会影响整体的交互使用。 问题 高分辨率/缩放设备上图片/图标模糊 若不考虑高分辨及缩放处理&#xff0c;在高分辨率/缩放设备上&#xff0c;软件中的图片、图标可能会出现…...

电机泵盖机器人打磨去毛刺,选德国进口高精度主轴

机器人打磨去毛刺该如何选择主轴呢&#xff1f;首先我们需要考虑的是工件的材质&#xff0c;电机泵盖通常使用铸铁、不锈钢、合金钢等金属材质&#xff0c;因此这类保持的硬度较高&#xff0c;一般会选择功率、扭矩较大的德国进口高精度主轴Kasite 4060 ER-S。 Kasite 4060 ER-…...

Android init.rc各阶段的定义和功能

Android开机优化系列文档-CSDN博客 Android 14 开机时间优化措施汇总-CSDN博客Android 14 开机时间优化措施-CSDN博客根据systrace报告优化系统时需要关注的指标和优化策略-CSDN博客Android系统上常见的性能优化工具-CSDN博客Android上如何使用perfetto分析systrace-CSDN博客A…...

.net dataexcel 脚本公式 函数源码

示例如: ScriptExec(""sum(1, 2, 3, 4)"") 结果等于10 using Feng.Excel.Builder; using Feng.Excel.Collections; using Feng.Excel.Interfaces; using Feng.Script.CBEexpress; using Feng.Script.Method; using System; using System.Collections.Gen…...

HarmonyOS ArkUi @CustomDialog 和promptAction.openCustomDialog踩坑以及如何选择

CustomDialog 内使用Link&#xff0c;如何正常使用 错误使用方式&#xff1a; 定义一个函数&#xff0c;在函数内使用弹窗&#xff0c;如下面代码showDialog&#xff1a; 这种使用方式&#xff0c;无法在自定义的CustomDialog内使用 Link&#xff0c;进行父子双向绑定&#x…...

Python面试题:详细讲解Python的多线程与多进程编程问题

在 Python 中&#xff0c;多线程和多进程编程是并发编程的两种主要方式&#xff0c;用于提高程序的执行效率和响应性。虽然它们都可以实现并发执行&#xff0c;但它们的工作原理和适用场景有所不同。以下是对 Python 多线程和多进程编程的详细讲解&#xff0c;包括它们的工作原…...

前端Canvas入门——用canvas写五子棋?

前言 五子棋的实现其实不难&#xff0c;因为本身就是一个很小的游戏。 至于画线什么的&#xff0c;其实很简单&#xff0c;都是lineTo()&#xff0c;moveTo()就行了。 难的在于——怎么让棋子落入到指定的格子上&#xff0c;怎么判断连子胜利。 当然啦&#xff0c;这部分是…...

[PaddlePaddle飞桨] PaddleDetection-通用目标检测-小模型部署

PaddleDetection的GitHub项目地址 推荐环境&#xff1a; PaddlePaddle > 2.3.2 OS 64位操作系统 Python 3(3.5.1/3.6/3.7/3.8/3.9/3.10)&#xff0c;64位版本 pip/pip3(9.0.1)&#xff0c;64位版本 CUDA > 10.2 cuDNN > 7.6pip下载指令&#xff1a; python -m pip i…...

Golang | Leetcode Golang题解之第239题滑动窗口最大值

题目&#xff1a; 题解&#xff1a; func maxSlidingWindow(nums []int, k int) []int {n : len(nums)prefixMax : make([]int, n)suffixMax : make([]int, n)for i, v : range nums {if i%k 0 {prefixMax[i] v} else {prefixMax[i] max(prefixMax[i-1], v)}}for i : n - 1…...

深度解析:在 React 中实现类似 Vue 的 KeepAlive 组件

在前端开发中&#xff0c;Vue 的 keep-alive 组件是一个非常强大的工具&#xff0c;它可以在组件切换时缓存组件的状态&#xff0c;避免重新渲染&#xff0c;从而提升性能。那么&#xff0c;如何在 React 中实现类似的功能呢&#xff1f;本文将带你深入探讨&#xff0c;并通过代…...

2024-7-20 IT新闻

目录 微软全球IT系统故障 中国量子计算产业峰会召开 其他IT相关动态 微软全球IT系统故障 后续处理&#xff1a; 微软和CrowdStrike均迅速响应&#xff0c;发布了相关声明并部署了修复程序。CrowdStrike撤销了有问题的软件更新&#xff0c;以帮助用户恢复系统正常运作。微软也…...

前端组件化开发:以Vue自定义底部操作栏组件为例

摘要 随着前端技术的不断演进&#xff0c;组件化开发逐渐成为提升前端开发效率和代码可维护性的关键手段。本文将通过介绍一款Vue自定义的底部操作栏组件&#xff0c;探讨前端组件化开发的重要性、实践过程及其带来的优势。 一、引言 随着Web应用的日益复杂&#xff0c;传统的…...

11.斑马纹列表 为没有文本的链接设置样式

斑马纹列表 创建一个背景色交替的条纹列表。 使用 :nth-child(odd) 或 :nth-child(even) 伪类选择器,根据元素在一组兄弟元素中的位置,对匹配的元素应用不同的 background-color。 💡 提示:你可以用它对其他 HTML 元素应用不同的样式,如 <div>、<tr>、<p&g…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...