【Eureka】如何实现注册,续约,剔除,服务发现

news2024/9/25 23:14:49

文章目录

  • 前言
  • 服务注册
  • 服务续约
  • 服务剔除(服务端去剔除过期服务)被动下线
  • 服务下线(主动下线)client发起的
  • 服务发现
  • 集群同步信息
  • Work下载

前言

Eureka是SpringCloud的具体实现之一,提供了服务注册,发现,续约,撤销等功能。并且我们使用Eureka的时候会将其分为客户端client和服务端server。服务端用于作为注册中心,而客户端则作为服务提供者和调用者。
我们引入了Netflix的Eureka-client这个jar包之后,其中包含了DicoveryClient这个类,其提供了与eureka-server进行交互的方法,如register(注册),renew(续约),cancel(注销)等方法。

服务注册

首先是服务注册方法。
这个类中的注册方法首先调用 AbstractJerseyEurekaHttpClient 类的register方法:Jersey是一个Restful请求服务的框架
在这里插入图片描述
下面是Jersey这个类中的register方法,他会从我们的配置文件中拿到我们设定的注册中心的URL地址,然后向这个地址发送一个post请求(因为post请求在Restful风格中代表的是添加),并且携带自己的info信息,这个info信息就是该client的配置文件中定义的信息。
在这里插入图片描述
通过HTTP请求向server发送完毕一个注册消息之后,就会构建一个响应结果。
在这里插入图片描述
当 eureka 启动的时候,会向我们指定的 serviceUrl 发送请求,把自己节点的数据以 post
请求的方式,数据以 json 形式发送过去。
当返回的状态码为 204 的时候,表示注册成功。

上面是客户端client的注册流程,那么发送注册请求之后,就需要server服务器端去保存这个注册信息了。

在ApplicationResource这个类中,有一个addInstance方法,用于将我们的注册的实例添加到注册中心去.
在这里插入图片描述
这个代码的前面是一堆的if else判断,用于对注册的实例信息进行验证.
在这里插入图片描述
然后进入这个register方法
在这里插入图片描述
可以发现注册信息过来了
在这里插入图片描述
在这里做了两个功能:
1、调用handleRegistration,在方法中使用publishEvent发布了监听事件 。Spring支持事件驱动,可以监听者模式进行事件的监听,这里广播给所有监听者,收到一个服务注册的请求。
2、调用父类PeerAwareInstanceRegistryImpl的register方法:

① 拿到微服务的过期时间,并进行更新
② 将服务注册交给父类完成
③ 完成集群信息同步(这个会在后面说明)
调用父类AbstractInstanceRegistry的register方法,在这开始真正开始做服务注册:
在这里插入图片描述
之后这里开始调用父类AbstractInstanceRegistry的register方法,在这开始真正开始做服务注册:
在这里插入图片描述
先说一下在这个类中定义的Eureka-server的服务注册列表的结构:
在这里插入图片描述
ConcurrentHashMap<String, Map<String, Lease>> registry;
ConcurrentHashMap中外层的String表示服务名称;spring.application.name
Map中的String表示服务节点的id (也就是实例的instanceid);
Lease是一个心跳续约的对象,InstanceInfo表示实例信息。
重要的类:
--------------------------------------client发送注册请求----------------------------
DiscoveryClient 里面的 register()方法完后注册的总体构造
AbstractJerseyEurekaHttpClient 里面的 register()方法具体发送注册请求(post)
-------------------------------------------下面是server中的处理-----------------
InstanceRegistry 里面 register()方法接受客户端的注册请求
PeerAwareInstanceRegistryImpl 里面调用父类的 register()方法实现注册
AbstractInstanceRegistry 里面的 register()方法完成具体的注册保留数据到 map 集合
保存服务实例数据的集合:
第一个 key 是应用名称(全大写) spring.application.name
Value 中的 key 是应用的实例 id
eureka.instance.instance-id
在这里插入图片描述
Value 中的 value 是 具体的服务节点信息 也就是Lease里面存放的是你设定的具体实例信息

首先,注册表根据微服务的名称或取Map,如果不存在就新建,使用putIfAbsent。
然后,从gMap(gMap就是该服务的实例列表)获取一次服务实例,判断这个微服务的节点是否存在,第一次注册的情况下一般是不存在的。
在这里插入图片描述
当然,也有可能会发生注册信息冲突时,这时Eureka会根据最后活跃时间来判断到底覆盖哪一个:
这段代码中,Eureka拿到存在节点的最后活跃时间,和当前注册节点的发起注册时间,进行对比。当存在的节点的最后活跃时间大于当前注册节点的时间,就说明之前存在的节点更活跃,就替换当前节点。
这里有一个思想,就是如果Eureka缓存的老节点更活跃,就说明它能够使用,而新来的服务我并不知道是否能用,那么Eureka就保守的使用了可用的老节点,从这一点也保证了可用性。
也就是已经存在的节点发送消息的时间与现在的时间更加接近,就说明已经存在的节点更加活跃.
在这里插入图片描述
之后在拿到服务实例后对其进行封装:
在这里插入图片描述
Lease是一个心跳续约的包装类,里面存放了注册信息,最后操作时间,注册时间,过期时间,剔除时间等信息。在这里把注册实例及过期时间放到这个心跳续约对象中,再把心跳续约对象放到gmap注册表中去。之后进行改变服务状态,系统数据统计,至此一个服务注册的流程就完成了。
注册完成后,查看一下registry中的服务实例,发现我们启动的Eureka-client都已经放在里面了:
在我们的eureka-server中,只要我们写完配置文件之后,就可以使用@EnableEurekaServer注解来开启注册中心服务。
查看该注解的实现方法,发现为空白注解,使用@Import:
@Import(EurekaServerMarkerConfiguration.class)
在这里插入图片描述
而这个类的作用就是向spring容器中注入Marker对象
在这里插入图片描述
此时继续查看Eureka-server的自动配置类,发现其要求必须先注入Marker这个类
所以@EnableEurekaServer就相当于一个开关,起到标识的作用。
在这里插入图片描述

服务续约

服务续约由Eureka-client端主动发起,由之前介绍过的DiscoveryClient类中的renew方法完成,主要内容仍然是发送http请求:
在这里插入图片描述
每隔30秒进行一次续约,调用AbstractJerseyEurekaHttpClient的sendHeartBeat方法:
发送心跳其实就是进行服务的续约了
在这里插入图片描述
在eureka-server端
在Eureka-server端,服务续约的调用链与服务注册基本相同:
InstanceRegistry # renew() ->PeerAwareInstanceRegistry # renew()->AbstractInstanceRegistry # renew()
在这里插入图片描述
在这里插入图片描述
主要看一下AbstractInstanceRegistry 的renew方法:
先从注册表获取该服务的实例列表(gMap),再从gMap中通过实例的id 获取具体的 要续约的实例。之后根据服务实例的InstanceStatus判断是否处于宕机状态,以及是否和之前状态相同。如果一切状态正常,最终调用Lease中的renew方法:
在这里插入图片描述
可以看出,其实服务续约的操作非常简单,它的本质就是修改服务的最后的更新时间。将最后更新时间改为系统当前时间加上服务的过期时间。续约的本质就是修改了服务节点的最后更新时间

值得提一下的是,lastUpdateTimestamp这个变量是被volatile关键字修饰的。
volitaile是用来保证可见性的。那么要被谁可见呢,提前说一下,这里要被服务剔除中执行的定时任务可见
因为剔除某个服务的话就是根据时间来判定的,因此要求这个时间是实时可见的,要求是最新的.所以这里用volatile
duration:代表注册中心最长的忍耐时间:
并不是 30s 没有续约就里面剔除,而是 30 +duration(默认是 90s) 期间内没有续约,才剔 除服务
Volatile 标识的变量是具有可见性的,当一条线程修改了我的剔除时间,其他线程就可以立 马看到(应用场景:一写多读),后面在剔除里面有一个定时任务,去检查超时从而判断某一 个服务是否应该被剔除
在这里插入图片描述

服务剔除(服务端去剔除过期服务)被动下线

当Eureka-server发现有的实例没有续约超过一定时间,则将该服务从注册列表剔除,该项工作由一个定时任务完成的。
在AbstractInstanceRegistry 中有一个 postInit()方法。这个方法设定了一个EvictionTask任务,这个任务用于定时执行剔除操作
在这里插入图片描述
之后来看evict方法的实现
这个方法首先创建了一个用于存放已经过期的实例的List集合,之后开始吧每一个spring。application。name下面的每一个注册中心的拥有的服务进行遍历,并且把所有已经过期的服务放入到这个List集合,知道遍历完毕所有的服务之后开始进行处理。
在这里插入图片描述
其中这个方法
evictionTimestamp:剔除时间,当剔除节点的时候,将系统当前时间赋值给这个
evictionTimestampadditionalLeaseMs:集群同步产生的预留时间,这个时间是程序中传过来的
在这里插入图片描述
系统当前时间 >最后更新时间 + 过期时间 + 预留时间
当该条件成立时,认为服务过期。在Eureka中过期时间默认定义为3个心跳的时间,一个心跳是30秒,因此过期时间是90秒。
当以上两个条件之一成立时,判断该实例过期,将该过期实例放入上面创建的列表中。注意这里仅仅是将实例放入List中,并没有实际剔除。
在实际剔除任务前,需要提一下eureka的自我保护机制:
当15分钟内,心跳失败的服务大于一定比例时,会触发自我保护机制。
在这里插入图片描述
这个值在Eureka中被定义为85%,一旦触发自我保护机制,Eureka会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。
在这里插入图片描述
参数意义:
registrySizeThreshold:根据阈值计算可以被剔除的服务数量最大值evictionLimit:剔除后剩余最小数量
expiredLeases.size():剔除列表的数量
上面的代码中根据自我保护机制进行了判断,使用Min函数计算两者的最小值,剔除较小数量的服务实例。
举个例子,假如当前共有100个服务,那么剔除阈值为85,如果list中有60个服务,那么就会剔除该60个服务。但是如果list中有95个服务,那么只会剔除其中的85个服务,在这种情况下,又会产生一个问题,eureka-server该如何判断去剔除哪些服务,保留哪些服务呢?
其实是根据随机算法来进行删除的,首先得到一个随机数,然后交换一下剔除集合中的数据,然后得到要删除的数据的编号,然后根据这个编号得到这个服务对应的应用名称appname和服务id。
然后调用intervalCancel进行定时的服务删除。
在这里插入图片描述
然后删除任务的方法就是从gmap中删除这个id对应的服务
在这里插入图片描述
服务剔除方法每隔60s执行一次,当然我们也可以在配置文件中自定义时间
在这里插入图片描述

服务下线(主动下线)client发起的

要下线的时候会调用shutdown方法,然后shutdown方法调用了unregister方法
在这里插入图片描述

调用AbstractJerseyEurekaHttpClient 的cancel方法:
发送http请求告诉eureka-server自己下线。
在这里插入图片描述
之后这个请求就会被server服务端收到,然后服务端调用cancel方法进行服务取消注册
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里最后又调用了internalCancel这个方法后根据id去gmap中删除对应的实例
在这里插入图片描述

服务发现

服务发现也就是一个服务去发现另一个服务,或者客户端去发现另一个服务的过程
在这里插入图片描述
可以通过DicoveryClient的getInstances方法去得到当前serviceId对应的服务的信息。
在这里插入图片描述
getInstancesByVipAddress这个方法用于进行服务发现。
首先判断本地是否有这个服务,如果本地有,那么就从本地直接拿。
如果没有,那么就从远程拿。
最后applications.getInstancesByVirtualHostName才是真正的获取服务列表
在这里插入图片描述
在这里插入图片描述
这里通过get方法真正的拿到了服务实例的集合
夸张的地方就在这里,因为明明我们还没有调用完毕getInstances获取实例的方法,但是这个map集合中就已经有了所有实例的数据了,这说明一定是在我们还没有调用getInstances试图去获取注册的实例之前,eureka就已经获取到了所有的实例了,并且把他们缓存在自己的信息中了。
也就是在我们还没有做服务发现之前,集合里面已经有值了,说明项目启动的时候就已经去server端里面拉取了所有服务并且进行了缓存。(这里的eureka是我是用客户端发送的,为下图comsumer,eureka-server表示的是注册中心,也就是说consumer这个客户端在我还没有进行服务发现的时候他就已经把server中的所有的服务都已经拉取到本地进行缓存了)
在这里插入图片描述
这个构造函数中有一个任务调度线程池
在这里插入图片描述
在 CacheRefreshThread()中
在这里插入图片描述
fetchRegistry()方法中判断决定是全量拉取还是增量拉取
这个方法就是用来拉取服务的
在这里插入图片描述
在这里插入图片描述
如果有新增服务,那么就进行增量拉取
如果服务列表为null,那么就直接进行全量拉取
在这里插入图片描述
第一个箭头指向的是向eureka-server拉取服务
第二个箭头是指把服务放入到本地服务列表中

下面是增量拉取
12表示先拉取服务列表,如果服务列表为null,那么直接进行一次全量拉取
不为空则进行增量拉取,然后进行一次一致性hash算法,判断此时的服务列表是否与eureka-server中的服务是一样的,如果一样,那么成功,否则在进行一次拉取。
在这里插入图片描述
DiscoveryClient 类里面的构造方法执行线程初始化调用
CacheRefreshThread 类里面的 run 方法执行服务列表的拉取(方便后期做服务发现)
fetchRegistry()方法去判断全量拉取还是增量拉取
全量拉取发生在:当服务列表为 null 的情况 当项目刚启动就全量拉取
增量拉取发生:当列表不为 null ,只拉取 eureka-server 的修改的数据(注册新的服务, 上线服务)
eureka 客户端会把服务列表缓存到本地 为了提高性能
但是有脏读问题,当你启动一个新的应用的时候 不会被老的应用快速发现,毕竟eureka相比于zookeeper,放弃的就是一致性,选择的是高可用,也就是AP组合。

集群同步信息

集群的信息同步发生在eureka-server服务端之间。
在执行register方法注册微服务实例完成后,执行了集群信息同步方法replicateToPeers
在这里插入图片描述
在这里插入图片描述
首先,判断是否有节点,然后遍历集群节点,用以给各个集群信息节点进行信息同步。
然后,调用replicateInstanceActionsToPeers方法,在该方法中根据具体的操作类型Action,选择分支,最终调用PeerEurekaNode的register方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个后面调用了一个post请求
在这里插入图片描述
之后post请求发送完毕之后,进行判断
在这里插入图片描述
在这里插入图片描述
最后如果是集群模式,那么就会把集群同步的表示isReplication设定为true,说明注册的信息是来自于集群同步。
在注册过程中运行到addInstance方法时,单独注册时isReplication的值为false,集群同步时为true。通过该值,能够避免集群间出现死循环,进行循环同步的问题。

Work下载

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

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

相关文章

[ Linux Audio 篇 ] Type-C 转 3.5mm音频接口介绍

简介 常见的Type-C 转3.5mm 线有两种&#xff1a; 模拟Type-C转3.5mm音频线数字Type-C转3.5mm 音频线&#xff0c;也就是带DAC芯片的转换线 当使用Type-C转换3.5mm音频接口时&#xff0c;使用到的是这里面的SBU1、D-、D、CC四个针脚&#xff0c;手机会通过这四个针脚输出模拟…

信贷--------

定义 信贷&#xff1a;一切以实现承诺为条件的价值运动方式&#xff0c;如贷款、担保、承诺、赊欠等 信贷业务&#xff1a;本外币贷款、贴现、透支、押汇&#xff08;表内信贷&#xff09;&#xff1b;票据承兑、信用证、保函、贷款承诺、信贷证明等&#xff08;表外信贷&…

卷积神经网络硬件加速——INT8数据精度加速

卷积神经网络硬件加速——INT8数据精度加速 上一专题已介绍了一种通用的卷积神经网络硬件加速方法——Supertile&#xff0c;本文将介绍一种特殊的硬件加速方案&#xff0c;一种INT8数据精度下的双倍算力提升方案。 目前大部分卷积神经网络模型的数据类型都是32-bits单精度浮点…

android开发笔记002

ListView控件 <ListViewandroid:id"id/main_iv"android:layout_width"match_parent"android:layout_height"match_parent"android:layout_below"id/main_top_layout"android:padding"10dp"android:divider"null&qu…

TCP三次握手详解

三次握手是 TCP 连接的建立过程。在握手之前&#xff0c;主动打开连接的客户端结束 CLOSE 阶段&#xff0c;被动打开的服务器也结束 CLOSE 阶段&#xff0c;并进入 LISTEN 阶段。随后进入三次握手阶段&#xff1a; ① 首先客户端向服务器发送一个 SYN 包&#xff0c;并等待服务…

c++11 标准模板(STL)(std::deque)(二)

构造函数 std::deque<T,Allocator>::deque deque(); (1) explicit deque( const Allocator& alloc ); (2)explicit deque( size_type count, const T& value T(), const Allocator& alloc Allocator());(3)(C11 前) …

网络编程 完成端口模型

1.概念以及重叠IO存在的问题 2.完成端口代码详解 整体流程 使用到的新函数 1.CreateIoCompletionPort函数 该函数创建输入/输出 (I/O) 完成端口并将其与指定的文件句柄相关联&#xff0c;或创建尚未与文件句柄关联的 I/O 完成端口&#xff0c;以便稍后关联&#xff0c;即创建…

金融业务的数据存储选型

为什么用关系型数据库&#xff1f;最常见的理由是别人在用&#xff0c;所以我也得用&#xff0c;但是这个并不是理由&#xff0c;而是借口。 1 数据分类 选择数据存储类型前&#xff0c;先分析数据特点&#xff0c;才能针对性选择存储方案。 通常按数据与数据之间关系的复杂…

SSM2---spring

Spring spring环境搭建 创建一个空白模块&#xff0c;目录结构如下 在pom.xml文件中引入相关依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/…

基于SSM(Spring+SpringMVC+Mybatis)实现的个人博客系统,含数据库文件及详细说明

关于项目 该博客是基于SSM实现的个人博客系统&#xff0c;适合初学SSM和个人博客制作的同学学习。 最新版本支持用户注册&#xff0c;包含用户和管理员两个角色 。 博主已写了一篇该项目的毕业论文和录制了2个小时的代码讲解可以供大家学习&#xff0c;需要的可以联系博主&…

RFID在模块管理中的应用

应用背景 模具是工业生产的基础工艺装备&#xff0c;被称为“工业之母”。作为国民经济的基础行业&#xff0c;模具涉及机械、汽车、轻工、电子、化工、冶金、建材等各个行业&#xff0c;应用范围十分广泛。模具资产管理采用传统的人工纸质记录的方式已经无法及时有效的进行&am…

还在用 XShell - 试试 IntelliJ IDEA 的 SSH

SSH 是很多人用得不多&#xff0c;但是又不得不用的工具。 如果你不是搞运维&#xff0c;没有必要搞个 CRT&#xff0c;XShell 也够用了&#xff0c;但是这 2 个都是收费软件&#xff0c;同时还不太便宜。 试试 IDEA 的 SSH 其实 IntelliJ IDEA 已经提供了 SSH 的功能。 如…

053-线程的状态改变及线程同步详细介绍

【上一讲】051-java线程的2种实现方法详解_CSDN专家-赖老师(软件之家)的博客-CSDN博客 线程可以处于以下四个状态之一1.新建(new):线程对象已经建立,但还没有启动,所以他不能运行。2.就绪(runnable):这种状态下,只要调度程序把时间片分配给线程就可以执行,也就是说…

10分钟带你了解什么是ArrayBuffer?

前言 很多时候我们前端开发是用不到 ArrayBuffer 的&#xff0c;但是用不到 ArrayBuffer 不代表我们不需要了解这个东西。本文就围绕 ArrayBuffer 来讲一下相关知识&#xff0c;大概需要10分钟左右就可以读完本文。 什么是ArrayBuffer&#xff1f; 描述 ArrayBuffer 对象用…

Paramiko库讲解

目录 基本概念 Paramiko组件架构 Key handing类 Transport类 SFTPClient类 SSHClient类—主要使用的类 Python编写完整例子 基本概念 Paramiko是Python实现SSHv2协议的模块&#xff0c;支持口令认证和公钥认证两种方式 通过Paramiko可以实现通过Python进行安全的远程命…

Html5网页播放器的同层播放功能

Html5网页播放器的同层播放功能&#xff1a; 在Android手机上使用H5播放视频时&#xff0c;大多数的国内浏览器厂商都会在视频播放时劫持<video>标签&#xff0c;使用浏览器自带的播放器播放视频&#xff0c;而且播放器会处于最高层级&#xff0c;视频上面无法显示其它h…

数影周报:字节跳动员工违规获取TikTok用户数据,阿里组织调整

本周看点&#xff1a;字节跳动员工违规获取TikTok用户数据&#xff1b;钉钉宣布用户数破6 亿&#xff1b;阿里组织调整&#xff1b;星尘数据完成 5000 万元 A 轮融资...... 数据安全那些事 字节跳动员工违规获取TikTok用户数据 字节跳动旗下热门应用TikTok日前曝出严重风波。字…

郭德纲落选,冯巩、赵炎上榜,国家非物质文化遗产传承人评选落幕

根据国家广电局26日消息&#xff0c;经过激烈的竞争&#xff0c;国家非物质文化遗产传承人评选工作&#xff0c;已经顺利落下帷幕。 在此次评选活动当中&#xff0c;评委会一致审议通过&#xff0c;著名相声演员冯巩和赵炎&#xff0c;被评为了非物质文化遗产传承人。而呼声很高…

Linux | 进程理解 | 进程的终止,等待与替换 | 环境变量的介绍与使用

文章目录进程终止进程终止的方法操作系统是怎么终止进程的&#xff1f;进程等待为何需要等待进程&#xff1f;怎么等待一个进程&#xff1f;非阻塞式等待进程替换什么是进程替换&#xff1f;为什么要进程替换&#xff1f;怎样替换一个进程&#xff1f;exec系列函数环境变量用命…

企业微信开发(一)常见问题收集及解决方案

持续收集企业微信开发中遇到疑难杂症&#xff0c;并给出相应的解决方案 一、好友上限&#xff08;84061&#xff09; 背景&#xff1a;达到添加好友数上限的员工&#xff0c;新增自动通过的好友&#xff0c;无法拉取到客户信息。 根因&#xff1a;企业微信业务限制 员工添加的…