Eureka处理流程

1、Eureka Server服务端会做什么

1、服务注册
Client服务提供者可以向Server注册服务,并且内部有二层缓存机制来维护整个注册表,注册表是Eureka Client的服务提供者注册进来的。
2、提供注册表
服务消费者用来获取注册表
3、同步状态
通过注册、心跳机制和 Eureka Server同步当前客户端的状态,这里就包括服务提供者和服务消费者。

2、Eureka Server的问题

问题:
1、Eureka Server的自我保护机制是怎么实现,怎么做到15分钟内,服务心跳失败比例高于85%。

2、自我保护机制触发后,有哪些功能会被开启

1、不再从注册列表中移除因为长时间没收到心跳而应该过期的服务,冷却时间是多久?
2、仍然能够接受新服务的注册和查询注册表请求,但是不会被同步到其它节点上
3、当网络稳定时,当前实例新的注册信息会被同步到其它节点中,怎么判断网络恢复

集群问题:

Eureka Server集群中,Eureka Server节点A和Eureka Server节点B是怎么通过P2P的方式完成服务注册表的同步?

Eureka Server集群中,同一个区域的Eureka Client,怎么做到优先和同区域内的Eureka Server进行通信的?

Eureka Client服务提供者,向Eureka Server注册,如果某个节点失败,自动切换到其他节点,是怎么做到的?

Eureka Server什么时候会自动退出自我保护模式?

二、源码概述

1、EurekaServer启动

@EnableEurekaServer->import(EurekaServerMarkerConfiguration)->注册Bean(EurekaServerMarkerConfiguration.Marker)->Marker激活了EurekaServerAutoConfiguration这个配置类

2、EurekaServerAutoConfiguration主要包含以下内容

1、创建Bean:【EurekaServerConfigBean】是一个配置类,EurekaServer的所有配置项都是EurekaServerConfigBean这个类里面。
2、创建Bean:【EurekaController】也就是我们通过url,可以访问EurekaServer后台。
3、创建Bean:【PeerAwareInstanceRegistry】处理注册表的类,这个类也会发布事件,发布了注册事件和取消事件(默认没有监听者需要自己实现)
4、创建Bean:【PeerEurekaNodes】初始化了集群节点集合
5、创建Bean:【EurekaServerContext】专业名称叫【EurekaServer上下文】,这个Bean的生成是基于上面创建的Bean: eureka server配置,注册表,集群节点集合来生成。而EurekaServerContext的作用就是初始化eurekaServer上下文,里面会做很多事情。
6、创建Bean:【EurekaServerBootstrap】Eureka Server的启动类
7、创建Bean:【FilterRegistrationBean】主要是对Jersey过滤器的包装,那么这个过滤器干嘛用的 ?

到此EurekaServerAutoConfiguration的创建Bean的任务完成了,但是EurekaServerAutoConfiguration里面还有一@Import(EurekaServerInitializerConfiguration)注解

3、EurekaServerInitializerConfiguration

EurekaServerInitializerConfiguration里面有个start方法,里面会拿到上面注册Bean:【EurekaServerBootstrap启动类】,来启动Eureak。
然后在通过生成的【EurekaServer上下文】开始初始化,初始化的时候会调用registry.syncUp方法,从相邻的eureka节点复制注册表,通过http调用相邻节点获取所有服务实例。

在通过上面的【PeerAwareInstanceRegistry】把实例注册到本地,这里的实例是指EurekaClient的服务提供者,同时PeerAwareInstanceRegistry里面还有一个【Timer】,这个是定时任务,清理30s没有续约的任务、服务剔除超过90s没过来续约的服务。

原文地址:跳转

三、下面通过代码原理说下实现逻辑

1、服务注册
2、服务续约
3、服务剔除
4、服务下线
5、服务发现
6、集群信息同步

1、服务注册

流程:服务提供者,请求EurekaServer端某个节点注册服务

EurekaClient端

Bean =EurekaAutoServiceRegistration】
通过com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#register向EurekaServer注册服务。

EurekaServer端

EurekaServer收到请求,请求进到ApplicationResource#addInstance方法,里面调用【Bean=PeerAwareInstanceRegistry】#register方法,
里面先发布一个Event事件,但是Spring没有监听这个事件,这个是留给我们自己拓展用的,在register里面还有一个很重要的事做,就是注册: 
1、首先是设置服务的过期时间90s
2、调用父类完成服务注册,
3、在完成集群信息同步,同步给其他节点。重点看调用【父类完成注册】,先从注册表的集合中获取服务注册信息:
1、如果注册表存在,那么说明冲突了,就判断哪2个节点的活跃时间比较靠前,保留节点时间最新的节点
2、如果不存在就新建,将EurekaClient的服务提供者封装成InstanceInfo对象, InstanceInfo存放了注册信息,最后操作时间,注册时间,过期时间,
剔除时间等信息,再把这个InstanceInfo对象存到注册表中去。至此一个服务注册的流程就完成了注意:注册表是一个Map<String, Map<String, Lease<InstanceInfo>>>对象
1、最外层的key是AppName,注册进来的服务的服务名 , value = Map<String, Lease<InstanceInfo>> value表示这个服务名对应多个实例节点
2Map<String, Lease<InstanceInfo>>这个Map,key是ip+端口,value是Lease<InstanceInfo>对象,也就是这个实例的更多信息,比如过期时间,最近活跃时间等等。

2、服务续约:

服务续约由Eureka-client端主动发起请求Eureka服务端,间隔时间30s,Eureka服务端收到请求,刷新节点的活跃时间。因为Eureka服务端,有定时任务,就是基于这个活跃时间来考虑是否剔除服务。

Eureka-client端

请求Eureka服务端,由DiscoveryClient#renew方法完成,主要是发送http请求,每隔30秒进行一次续约,
里面调用AbstractJerseyEurekaHttpClient#sendHeartBeat方法

Eureka-server端

Eureka-server端服务续约的调用链与服务注册基本相同
InstanceRegistry#renew() -> PeerAwareInstanceRegistry#renew()-> AbstractInstanceRegistry # renew() 主要逻辑还是AbstractInstanceRegistry的renew方法renew的方法逻辑操作非常简单,它的本质就是修改服务的【最后更新时间】。将最后更新时间改为:【系统当前时间】+【服务的过期时间】

3、服务剔除

服务主要是Eureka服务端,通过定时任务检测注册节点的【活跃时间】,如果超过90s就会剔除。

Eureka-server端

Eureka-server发现有的实例没有续约超过一定时间,则将该服务从注册列表剔除,该项工作由一个定时任务完成的。由下面方法完成
AbstractInstanceRegistry # postInit()定时任务前面说了在【EurekaServer上下文】初始化的时候,添加了一个Timer定时器,定时器关联的任务是EvictionTask的run方法,在执行任务中
调用剔除方法 evict(), 主要是拿到注册表的所有实例挨个遍历,判断【系统当前时间 > 最后更新时间+过期时间+预留时间】,
并且新建实例列表expiredLeases,用来存放过期的实例。当该条件成立时,认为服务过期(在Eureka中过期时间默认定义为3个心跳的时间,一个心跳是30秒,因此过期时间是90秒)。
将该过期实例放入上面创建的expiredLeases列表中。注意这里仅仅是将实例放入List中,并没有实际剔除。因为要判断是否超过阈值了,如果超过就从里面取随机数,随机剔除实例ID,注意expiredLeases里面存的是多个服务的实例,不是某一个服务的所有实例。下线的时候从里面取随机数,所以有可能某个服务的所有实例全部被剔除都有可能。在实际剔除任务前,需要提一下eureka的自我保护机制:
当1分钟内,心跳失败的服务大于一定比例时,会触发自我保护机制。这个值在Eureka中被定义为85%,一旦触发自我保护机制,
Eureka会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。1分钟是怎么统计数量的?哪些节点保留? 哪些节点删除?答:使用了随机算法进行剔除,
举个例子,假如当前共有100个服务,那么剔除阈值为85%,也就是最多剔除15个,如果list中有60个服务,那么就会从60个服务里面取15个。有可能一个服务的所有节点全部被剔除。剔除的节点被放到一个queue里面,这个里面存的是最近剔除的节点,在集群同步,或者拉取注册表的时候,要用到。

关于自我保护

首先是阈值是85%,比如100个,阈值是85%,那么一次最多剔除15个,当定时任务进来,发现100个里面有10个失效,那么10小于【最大阈值】,那就剔除10个,如果20个失效,20个大于15,所以最多剔除15个,这15个怎么选?
通过for循环15次,每次生成一个随机数,这个随机数是从20里面取,1、自我保护时期不能进行服务剔除操作
2、过期操作是分批进行
3、服务剔除是随机逐个剔除,均匀分布在所有应用中,其实也不算均匀,是随机抽
4、服务剔除是一个定时任务,默认60秒一次问题:自我保护时期不能进行服务剔除操作:这个是怎么做到的?首先是定义了2个变量,一个是【期望续约数】,一个是【前一分钟实际的续约数】。 
这个【期望续约数】是通过公式算出来了,比如20个实例,正常情况下1分钟的话会续约40次,
那么期望的续约数应该是40*85%=34个,而如果实际契约数超过这个数量,比如35,
那么EurekaServer认为,服务恢复正常了,应该关闭自我保护机制。
注意:期望续约数是一个动态值,每次会重行计算的。比如服务下线或者上线,期望的数量是会加1或者减1的。问题:实际续约数是怎么算出来的? 
因为剔除的定时任务是1分钟一次,所以有个定时任务专门设置【前一分钟实际续约数量】,MeasuredRate也是60秒一次,他里面定义了2个变量,一个是【一分钟内的续约】数,一个是【上一分钟的续约数】,服务每次注册就会加1,服务下线就减1,当定时任务跑的时候,就会把一分钟的续约数赋值给【上一分钟的续约数】,然后再把【一分钟内的续约】置0

自我保护机制,详细解读:跳转
代码流程:跳转

4、服务下线

当eureka-client关闭时,不会立刻关闭,需要先发请求给eureka-server服务端,告知自己要下线了。

Eureka-client端:

Eureka客户端请求EurekaServer服务端,通过DiscoveryClient#shutdown方法调用EurekaServer服务端

Eureka-server端

收到请求,进到AbstractInstanceRegistry#cancel方法, 最终还是调用了和服务剔除中一样的方法,remove掉了注册表中的实例

5、服务发现

是指EurekaClient 消费者,通过Http调用EurekaServer服务端接口,获取注册表信息

Eureka-client端:

DiscoveryClient#getInstances方法,可以根据服务id获取服务实例列表。那么这里就有一个问题了,我们还没有去调用微服务,那么服务列表是什么时候被拉取或缓存到本地的服务列表的呢?

EurekaDiscoveryClient # getInstances() -> DiscoveryClient # getInstancesByVipAddress() -> DiscoveryClient #getInstancesByVipAddress2() ->Applications # getInstancesByVirtualHostName() 这里居然不是走的http,是读的本地缓存。Applications中的getInstancesByVirtualHostName方法里面,有一个virtualHostNameAppMap的Map集合中已经保存了当前所有注册到eureka的服务列表。
private final Map<String, VipIndexSupport> virtualHostNameAppMap;   
也就是说,在我们没有手动去调用服务的时候,该集合里面已经有值了,说明在Eureka-server项目启动后,会自动去拉取服务,并将拉取的服务缓存起来。那么追根溯源,来查找一下服务的发现究竟是什么时候完成的。回到DiscoveryClient这个类,
在它的构造方法中定义了任务调度线程池cacheRefreshExecutor,定义完成后,调用initScheduledTask方法,
通过fetchRegistry方法来拉取,不过分2种情况【增量拉取】还是【全量拉取】【全量拉取】:当缓存为null,或里面的数据为空,或强制时,进行全量拉取,执行getAndStoreFullRegistry方法
【增量拉取】: 只拉取修改的。执行getAndUpdateDelta方法,虽然这里是拉增量,但是如果没拉到数据,还是会拉全量的数据,然后就是更新操作,更新也有类型,是delete还是Modify,added,这里有个细节校验,就是拿hashCode和缓存的HashCode对比是否一致,如果一致,说明数据没有变动,如果不一致,那就说明本地和远程数据不一样,需要重新再拉一次,

对服务发现过程进行一下重点总结:

1、服务列表的拉取并不是在服务调用的时候才拉取,而是在项目启动的时候就有定时任务去拉取了,这点在DiscoveryClient的构造方法中能够体现;
2、服务的实例并不是实时的Eureka-server中的数据,而是一个本地缓存的数据;
3、缓存更新根据实际需求分为全量拉取与增量拉取。

6、集群信息同步

Eureka-server端:

集群信息同步发生在Eureka-server之间,之前提到在PeerAwareInstanceRegistryImpl类中,在执行register方法注册微服务实例完成后,
执行了集群信息同步方法replicateToPeers首先,遍历集群节点,用以给各个集群信息节点进行信息同步。最终发送http请求,请求各个EureakServer节点。
调用EurekaServerApplicationResource类里面的addInstance,注意EurekaClient注册的时候,也是调的这个方法,
单独注册时isReplication的值为false,集群同步时为true

Eureka三级缓存

服务端的缓存机制

服务端采用三级缓存(registry,readWriteCacheMap,readOnlyCacheMap)来存储注册表信息。

三级缓存的目的是为了将注册服务和获取服务区分开,避免了高并发的同时对一个缓存的读写操作,有效避免读写冲突。保证性能。

一级缓存 = ConcurrentHashMap<Key,Value> registry 服务一开始注册进来的地方
二级缓存 = Loading<Key,Value> readWriteCacheMap,本质上是guava的缓存,包含失效机制,保存服务信息的对外输出数据结构。
三级缓存 = ConcurrentHashMap<Key,Value> readOnlyCacheMap 本质上是HashMap,无过期时间,保存服务信息的对外输出数据结构。

设置缓存
(1)、客户端将服务信息注册在一级缓存registry中。(每30s一次心跳续约)
(2)、一级缓存registry收到注册信息后,先清空二级缓存readWriteCacheMap中的注册信息,然后在同步新数据给readWriteCacheMap二级缓存。
(3)、二级缓存按照30s一次的频率给三级缓存readOnlyCacheMap同步数据缓存获取
(4)、其他的客户端连接注册中心Server 30s一次的频率从三级缓存readOnlyCacheMap中获取,如果readOnlyCacheMap中获取不到,则直接去一级缓存registry中获取。缓存更新
(5)、一级缓存中默认每隔60s检查服务续期,如果90秒内服务还没有续期,则删除注册信息。同时同步给二级三级缓存。
(6)、服务下线时,一级缓存registry中的注册信息删除,同时删除二级缓存的数据。30s后二级同步三级缓存时发现二级缓存已失效,则删除三级缓存的注册表信息。则会期间会有时间的延迟。
(7)、二级缓存的默认有效期是180s(3min),3min后数据会失效,然后二级缓存数据清空。

三级缓存的弊端:

三级缓存的问题很明显,就是服务下线之后,不能及时通知到三级缓存中,注册信息的获取者(客户端)拿到的注册信息不是实时的。(当让客户端的获取也不是实时的,要间隔30s才会去主动获取)

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

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

相关文章

海康多相机同步取流保存图片

话不多说&#xff0c;直接上代码。代码里包含了多窗口显示图像和保存图片。 #"rtsp://admin:123456qq192.168.10.192/stream1" # rtsp://admin:Admin123192.168.100.103:554/Streaming/Channels/101 #rtsp://admin:Admin123192.168.100.103:554/cam/realmonitor?ch…

redis缓存击穿,redisson分布式锁,redis逻辑过期

什么是缓存击穿&#xff1a; 缓存击穿是指在高并发环境下&#xff0c;某个热点数据的缓存过期&#xff0c;导致大量请求同时访问后端存储系统&#xff0c;引起系统性能下降和后端存储压力过大的现象。 解决方案&#xff1a; 1. redisson分布式锁 本质上是缓存重建的过程中&…

「Verilog学习笔记」四选一多路器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 通过波形示意图我们可以发现&#xff0c;当sel为0&#xff0c;1&#xff0c;2时&#xff0c;输出mux_out分别为d3&#xff0c;d2&#xff0c;d1&#xff0c;那么sel3…

PHP中文转拼音实现

pinyin.php 床前明月光&#xff0c;疑是地上霜。 举头望明月&#xff0c;低头思故乡 <?php /*** PHP 汉字转拼音 [包含20902个基本汉字5059生僻字]* author 楼教主(cik520qq.com)* version v1.2* note 请开启 mb_string 扩展*/var_dump(pinyin(床前明月光&#xff0c;疑是…

【Redis】的简介和安装配置(Linux和windows)及操作命令

目录 一、概述 1.介绍 2.特点 二、安配 1. 安装 2. 配置 3. 主机连接 1.Linux连接 2.windows连接 三、命令 1. 字符串(String) 2. 哈希(Hash) 3. 列表&#xff08;List&#xff09; 4. 集合&#xff08;Set&#xff09; 一、概述 1.介绍 Redis是一个开源的、基…

零代码复现-TCGA联合GEO免疫基因结合代谢基因生信套路(二)

零代码复现-TCGA联合GEO免疫基因结合代谢基因生信套路&#xff08;二&#xff09;-关键基因集的获取和生存数据准备 前面的分析中&#xff0c;下载TCGA和GEO的数据&#xff0c;并进行简单的处理&#xff0c;接下来就是相关基因集的获取和整理&#xff0c;为后期聚类和降维做准…

【PyQt学习篇 · ⑥】:QWidget - 事件

文章目录 事件消息显示和关闭事件移动事件调整大小事件鼠标事件进入和离开事件鼠标按下和释放事件鼠标双击事件鼠标按下移动事件 键盘事件焦点事件拖拽事件绘制事件改变事件右键菜单输入法 事件转发机制案例一案例二案例三 事件消息 显示和关闭事件 showEvent(QShowEvent)方法…

小白如何制作电子画册?看这里,超多画册模板任你挑!

传统纸质版的画册&#xff0c;制作起来即费力又费时&#xff0c;花费还高&#xff0c;想要修改内容还得重新制作&#xff0c;特别麻烦。现在互联网发达&#xff0c;如今已经用上了H5的技术&#xff0c;小白也能快速制作一本翻页电子画册。 只需用FLBOOK&#xff0c;在线就可以制…

git reflog 恢复git reset --hard 回退的内容

首先使用 git reflog 查看处理的历史&#xff0c;历史是由新到旧排列的&#xff0c;找到回退前的commit的id&#xff0c;找的过程可以只关注HEAD的部分&#xff0c;HEAD括号中的值越大越旧&#xff0c;越小越新。 找到后执行以下命令 git reset --hard 你的commit_id 然后…

Linux的历史与环境

目录 Linux的背景介绍 Linux的时代背景-硅谷模式 计算机发展 UNIX发展历史 Linux诞生的偶然与必然 Linux开源 Linux发行版本 搭建Linux的环境 1.直接安装在物理机上 2.使用虚拟机软件 3.使用云服务器 &#xff08;1&#xff09;购买云服务器 &#xff08;2&#x…

OceanBase:03-集群部署

目录 一、集群规划 二、配置要求 三、部署前配置 1.配置 limits.conf 2.配置 sysctl.conf 3.关闭防火墙 4.关闭 SELinux 5.创建数据目录&#xff0c;修改文件所有者信息 6.设置无密码 SSH 登录 7.安装jdk 四、解压执行安装 五、集群部署 1.OBD命令行部署 2. OBD白…

2019年408真题复盘

紫色标记是认为有一定的思维难度或重点总结 红色标记是这次刷真题做错的 记录自己对题目的一些想法与联系&#xff0c;可能并不太关注题目本身。 分数用时 选择部分 80/8036min大题部分41/7094min总分121130min 摘自知乎老哥&#xff1a;“我做历年真题时&#xff0c;绝大部分…

图数据库Neo4j——SpringBoot使用Neo4j 简单增删改查 复杂查询初步

前言 图形数据库是专门用于存储图形数据的数据库&#xff0c;它使用图形模型来存储数据&#xff0c;并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。 Neo4j是用Java实现的开源NoSQL图数据库&#xff0c;本篇博客介绍如何在SpringBoot中使用Neo4j图数据库&…

python连接clickhouse (CK)

Author: tkhywang 2810248865qq.com Date: 2023-11-01 11:28:58 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-11-01 11:36:25 FilePath: \PythonProject02\Python读取clickhouse2 数据库数据.py Description: 这是默认设置,请设置customMade, 打开koroFileHead…

【Linux】:Linux开发工具之Linux编辑器vim的使用

&#x1f52b;1.Linux编辑器-vim使用 &#x1f4e4; vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以…

CentOS操作系统的特点

CentOS操作系统的特点如下&#xff1a; 免费开源&#xff1a;CentOS是一个免费开源的操作系统&#xff0c;完全免费&#xff0c;无需花费任何成本。 稳定性高&#xff1a;CentOS以其出色的稳定性和安全性而闻名。它是一个基于Red Hat Enterprise Linux&#xff08;RHEL&#x…

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取 由于一些工程的仿真文件不易产生&#xff0c;所以我们可以利用 quartus 软件自带的 SignalTap 工具对波形进行抓取 对各个信号进行分析处理&#xff0c;让电子器件与FPGA进行正常通讯工作&#xff0c;也验证所绘制的波形图是否一…

模拟实现.net中的Task机制:探索异步编程的奥秘

.net中使用Task可以方便地编写异步程序&#xff0c;为了更好地理解Task及其调度机制&#xff0c;接下来模拟Task的实现&#xff0c;目的是搞清楚&#xff1a; Task是什么Task是如何被调度的 基本的Task模拟实现 从最基本的Task用法开始 Task.Run(Action action)这个命令的作…

hiveSQL语法及练习题整理(mysql)

目录 hiveSQL练习题整理&#xff1a; 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 第十一题 第十二题 hivesql常用函数&#xff1a; hiveSQL常用操作语句&#xff08;mysql&#xff09; hiveSQL练习题整理&#xff1a; 第一题 我…

【C++】类和对象(中)之拷贝构造与运算符、操作符重载

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 我们继续学习默认成员函数&#xff0c;本篇文…