【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版本的源码 讲解,后续在继续。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/380418.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

19.x86游戏实战-创建MFC动态链接库

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

HarmonyOS鸿蒙- 跳转系统应用能力

一、通过弹窗点击设置跳转系统应用能力 1、 自定义弹窗效果图 2、 自定义弹窗代码 import { common, Want } from kit.AbilityKit; import { BusinessError } from kit.BasicServicesKit;export function alertDialog() {AlertDialog.show({title: ,message: 当前功能依赖定位…

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

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

C++基础语法:STL之容器(4)--序列容器中的list(一)

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 序列容器的学习.以<C Prime Plus> 6th Edition(以下称"本书")内容理解 本书中容器内容不多只有几页.最好是有数据结构方面的知识积累,如果没有在学的同时补上. 序列容器回顾:序列容器内元素按严格…

css前端面试题

1.什么是css盒子模型&#xff1f; 盒子模型包含了元素内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&#xff09;、外边距&#xff08;margin&#xff09;几个要素。 标准盒子模型和IE盒子模型的区别在于其对元素的w…

食家巷香豆烤馍:传统美味,唇齿留香

你是否在寻找一种能唤醒童年记忆的美食&#xff1f;是否又在渴望一种既能充饥又能享受的美味&#xff1f;那么&#xff0c;食家巷的香豆烤馍&#xff0c;一定能满足你的味蕾。 香豆烤馍&#xff0c;以优质的原料、精致的制作和独特的口味&#xff0c;让食家巷香豆烤馍在众…

[React 进阶系列] useSyncExternalStore hook

[React 进阶系列] useSyncExternalStore hook 前情提要&#xff0c;包括 yup 的实现在这里&#xff1a;yup 基础使用以及 jest 测试 简单的提一下&#xff0c;需要实现的功能是&#xff1a; yup schema 需要访问外部的 storage外部的 storage 是可变的React 内部也需要访问同…

数据库第二次作业

1.建立数据库 2.插入数据 3.完成查询 &#xff08;1&#xff09;、显示所有职工的基本信息。 &#xff08;2&#xff09;、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 &#xff08;3&#xff09;、求出所有职工的人数。 &#xff08;4&#xff09;、列…

iPhone手机上备忘录怎么设置字数显示

在日常生活和工作中&#xff0c;我经常会使用iPhone的备忘录功能来记录一些重要的想法、待办事项或临时笔记。备忘录的便捷性让我可以随时捕捉灵感&#xff0c;但有时候&#xff0c;我也会苦恼于不知道自己记录了多少内容&#xff0c;尤其是在需要控制字数的时候。 想象一下&a…

NAS新品“翻车”后,绿联科技要上市了

在消费电子市场回暖的东风中&#xff0c;又一消费电子知名企业登陆A股。 近日&#xff0c;深圳市绿联科技股份有限公司&#xff08;下称“绿联科技”&#xff09;开启申购&#xff0c;将在创业板上市。本次上市&#xff0c;绿联科技的发行价为21.21元/股&#xff0c;发行数量为…

鸿蒙OpenHarmony Native API【HiLog】

HiLog Overview Description: HiLog模块实现日志打印功能。 开发者可以通过使用这些接口实现日志相关功能&#xff0c;输出日志时可以指定日志类型、所属业务领域、日志TAG标识、日志级别等。 syscap SystemCapability.HiviewDFX.HiLog Since: 8 Summary Files File …

几种基本数据结构

目录 前言 线性结构 链式结构 单链表 双链表​编辑 树形结构 前言 在我们编写程序时&#xff0c;经常会出现需要存储数据的情况&#xff0c;而数据的存储是有讲究的&#xff0c;数据不是在我们的内存中胡乱存储&#xff0c;为了保证数据在进行修改和查找时更加方便&#…

高德api获取天气(详细步骤)

1.登录高德开放平台&#xff0c;点击创建新应用&#xff0c;输入应用名称&#xff0c;选择应用类型&#xff0c;然后点击创建 2.点击添加key&#xff0c;按照以下步骤&#xff1a; 3.然后提交后点开就能看到你的key 4.以下就是示例代码&#xff1a; <!-- 高德获取天气坐标…

扩展PyTorch视觉模型

扩展PyTorch视觉模型 目录 扩展PyTorch视觉模型 一、概述 二、扩展基本视觉模型的原因 1. 性能提升 2. 功能扩展 3. 资源管理 三、扩展PyTorch视觉模型的方法 1.修改现有架构 2.应用模型集成技术 3.量化和压缩模型 四、高级技巧与实践 1.自定义训练循环 2.深度模型…

Visio绘制的CBAM结构图,无水印,可修改,能导出高清图片,可用于论文写作

Visio绘制的CBAM网络结构图,可导出高清图片&#xff0c;可修改&#xff0c;无水印。 方便用于小论文写作&#xff0c;方便用于毕业设计。 Visio版本为2021版&#xff0c;可用更高版本打开。 下载地址&#xff1a;cbam 图片展示&#xff1a;

超算网络体系架构-资源层-平台层-服务层-应用层

目录 超算网络体系架构 我国超算基础设施 超算互联网相关标准研制方面 技术架构 资源层 基础资源 芯片多样 体系异构 高效存储 高速互连 资源池化 可隔离 可计量 互联网络 高带宽 低时延 高安全 平台层 算力接入 资源管理 算力调度 用户管理 交易管理 模…

CVE-2024-24549 Apache Tomcat - Denial of Service

https://lists.apache.org/thread/4c50rmomhbbsdgfjsgwlb51xdwfjdcvg Apache Tomcat输入验证错误漏洞&#xff0c;HTTP/2请求的输入验证不正确&#xff0c;会导致拒绝服务&#xff0c;可以借助该漏洞攻击服务器。 https://mvnrepository.com/artifact/org.apache.tomcat.embed/…

【云原生】Prometheus整合Alertmanager告警规则使用详解

目录 一、前言 二、Altermanager概述 2.1 什么是Altermanager 2.2 Altermanager使用场景 三、Altermanager架构与原理 3.1 Altermanager使用步骤 3.2 Altermanager工作机制 3.3 Altermanager在Prometheus中的位置 四、Altermanager部署与接入Prometheus 4.1 Altermana…

nodejs安装+踩坑报错解决

下载Node.js安装包 官网下载地址&#xff1a;http://nodejs.cn/download/&#xff0c;根据自己电脑选择32位还是64位&#xff0c; 下载地址 选择合适的版本下载 X86是32位的&#xff0c;X64是64位的&#xff0c;我们一般是下载win版X64的msi文件的是点击可以直接启动安装程序的…

深入Redis集群部署:从安装配置到测试验证的完整指南

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…