写点什么

Eureka 部分机制记录

用户头像
PCMD
关注
发布于: 2021 年 02 月 21 日
Eureka 部分机制记录

Eureka 部分机制记录

背景


eureka 分布式服务,注册中心,满足 CAP 中的 AP。通过一些技术细节内幕,保证了其可用性和分区容错性。最近也是在看 eureka 相关源码,针对其中的部分,在这里做一些记录。针对以下的一些点
(1)eureka server启动:注册中心(2)eureka client启动:服务实例(3)服务注册:map数据结构(4)eureka server集群:注册表的同步,多级队列的任务批处理机制(5)全量拉取注册表:多级缓存机制(6)增量拉取注册表:一致性hash比对机制(7)心跳机制:服务续约,renew(8)服务下线:cancel(9)服务故障:expiration,eviction(10)自我保护:自动识别eureka server出现网络故障了
复制代码


Eureka 部分机制的实现原理


Eureka 作为 spring-cloud 所选中的注册中心,其特性和突出的点,不可赘述,下边,将会针对其中的一些点,做一些记录。


先来一张整体的流程和架构图



非全面机制细节呈现图,主要是一个大致的流程运行图。


从图中,可以看到,一个 service (eurka-client)启动,会向 eureka-server 进行服务注册,将自己作为 provider ,注册到 eureka-server 中,并定时的,从 server 端进行 续约 (心跳机制)eureka 中叫做 renewLease,且定时的从 server 端拉取 注册表内容,用来保证自己本地维护的注册表信息完全和一致。


###服务注册


eureka 服务注册比较简单,也没有太多的内容在里边,主要是 client 初始化的时候,调度 registry 通过 jersey 进行 http 的请求,然后 server 端,进行接收之后,将元信息(metadata) 保存到 server 端的内存 map 之中,并且通 status 控制台,可以看元数据的信息。下边是个简单的服务注册流程



client 注册启动过程


作为一个 eureka-client ,从整体运行图上,我们也可以看到,会进行,一些心跳的续约,还有注册表的拉取更新,下边,从 client 的启动整体启动过程,做一下梳理


Eureka client ,具体的实现在 DiscoveryClient 这个类中,具体的 代码细节,可在源码中,自行查阅,以下是一张 client 注册启动的流程图


可以看到,在注册启动的时候,初始化了 3 个线程池任务用来进行之前说的,续约,心跳,注册表拉取功能.



刚才也说了,client 会定时的调度,会拉取刷新注册表,在 client 中,会存在 2 中拉取机制


  • 增量同步

  • 全量同步


下边是关于 client 每 30s 一次的更新流程,在 server 端,会有一个队里,保存最近 3 分钟之内增量变更的注册表


client 端通过 serve 端的返回,然后进行和 本地进行对比,进行增删改操作,最后通过本地的最新的全量注册表,计算 hash 值,在和服务端返回的 全量注册表的 hash 值,做一致性 hash 校验,如果不一致,在进行一次全量的拉取,保证其一致性



server 端的 3 层缓存机制



server 端启动与集群部署


先看下 server 端的工程目录架构,可以看到,eureka-server 的项目中,除了 resources 中的一些配置之外 只有 web.xml 这样一个工程目录



具体的启动逻辑,就在 web.xml 的配置之中 ,listener core 工程中的 EurekaBootStrap


  <!-- web 应用初始化 ,一些初始化代码
eureka server 的初始化
-->
<listener>
<listener-class>com.netflix.eureka.EurekaBootStrap</listener-class>
</listener>
复制代码



启动大致流程,如下图所示, EurekaBootStrap 实现了 ServletContextListener 接口,在 contextInitialized 实现方法中,实现了 server 短的环境,配置初始化,和启动。
```java public void contextInitialized(ServletContextEvent event) { try { initEurekaEnvironment(); initEurekaServerContext();
ServletContext sc = event.getServletContext(); sc.setAttribute(EurekaServerContext.class.getName(), serverContext); } catch (Throwable e) { logger.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } }
复制代码



为了保证服务的高可用,eureka-server 端肯定是需要通过集群部署的。在初始的整体架构图中,可以看到,当 server 启动的时候,也会作为 eureka-client 向另外的 server 进行注册,并同步拉取注册表,这样,就保证了不同 server 之间的信息同步。


下边将针对服务状态维护,展开一些深入的探讨。


服务状态维护


服务下线


关于 service 的一些实例信息,是维护在 server 端的,一旦当有服务关机了,势必是要通知到 eureka-server,进行服务的下线,然后从注册表中摘除,并且清除缓存,最终完成了服务的线下,具体流程,如下图



从这张图上,也可以看到,在 server 端,会维护一个 recentlyChangedQueue,顾名思义 在这个队列中,会维护最近 3 分钟之内的变更记录,然后由一个定时任务,维护最近 3 分钟的变更,清理之前的变更记录。这个任务,在 构架实例表的时候,就已经构建了,后台运行。并且也会清理缓存,使其失效,这边会有一个 readWriteCacheMap 这样的一个读写缓存,稍后会针对这个换错,做下详细说明。


server 端自动故障感知 &服务实例摘除 &网络故障,自我保护机制


除了一些正常的关机之外,比如当业务 service 宕机了,或者 oom 等异常,导致服务没有自发的进行 shutdown 操作,下线服务,这样,服务依旧会存在 server 端的注册表中,那么这时候,就需要有机制,来保证故障自动感知,并将故障服务,从服务清单中,进行摘除,最后,在由 client 端的定期的拉取,做到同步感知。一些细节流程如下图所示



在这个流程中,有几个关键因素:


  • 续约心跳(client)

  • 心跳时间(server 端)

  • 补偿时间(防止由于 server 端 GC 之类的 stw ,导致的时间延长)

  • 上次的心跳次数


并且内,为了防止由于一些续约心跳上报时候的服务网络故障,导致的上报异常,如果开启了自我保护,那么会在服务摘除的时候,进行自我保护机制的判断,在里边会预留一定的 buffer,防止因异常而全部摘除


并且在服务摘除的时候,也会进行一定比例的随机摘除,非一次性全部摘除。


eueka server 之间状态同步任务批处理机制


eureka-server 是集群部署的,我们也知道,client 进行注册,续约,下线,都只会随机的同步到一台 server 机器上,必不可少的就是 server 端的状态互相同步


查看源码。也可以知道,每一次的注册,下线,或者服务剔除,都会向其他 peeerNode 进行数据同步。在同步过程中,eureka 采取了 3 重队列,然后通过批处理的方式,一次性提交请求。当服务多的时候,注册或者下线,不会有太多的请求,按时心跳续约,这是很频繁的,势必是要采取批量请求的方式,进行服务之间的状态同步


具体的同步流程,如下图所示



具体的逻辑代码实现在这个地方 eureka-core 中



难免疏漏,不足之处,烦请指出,欢迎支持沟通交流

发布于: 2021 年 02 月 21 日阅读数: 41
用户头像

PCMD

关注

我看青山多妩媚 2020.02.07 加入

还未添加个人简介

评论

发布
暂无评论
Eureka 部分机制记录