在上篇文章中我们介绍了EurekaServerContext Bean的注入,我们接下来看下PeerAwareInstanceRegistry类,这个类主要是用作服务实例对象的注册,在EurekaServerAutoConfiguration类代码的定义中它首先通过客户端获取所有的application对象,这些对象是已经注册到服务端的实例对象,这里就不多做介绍了,可以自己去看实现类,这里我们主要了解下InstanceRegistry代码的实现,进入到InstanceRegistry类,可以看到有如下方法
1.我们首先看下register()方法,它有2个不同参数的方法,从参数名称上理解分别是实例对象(InstanceInfo),有效时间(leaseDuration),是否需要复制到其他节点(isReplication),在这个方法中它都回去调用一个事件发布的方法进行消息事件的发布,我们直接通过2个参数的register()方法去查看父类的方法,该父类是PeerAwareInstanceRegistryImpl类
public void register(final InstanceInfo info, final boolean isReplication) { int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } super.register(info, leaseDuration, isReplication); replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication); }在该register()方法中它又调用了父类AbstractInstanceRegistry类的register()方法,以及replicateToPeers()方法,在调用父类的注册方法前,它提供了默认的leaseDuration(90s)或通过配置获取到的leaseDuration值,在AbstractInstanceRegistry类的register()方法中,总的来说就是将新增的InstanceInfo对象存入到一个ConcurrentHashMap对象的register变量中,当然还在一些自定义队列对象中添加了该InstanceInfo对象,接下来我们看看注册完后的replicateToPeers()方法
private void replicateToPeers(Action action, String appName, String id, InstanceInfo info /* optional */, InstanceStatus newStatus /* optional */, boolean isReplication) { Stopwatch tracer = action.getTimer().start(); try { if (isReplication) { numberOfReplicationsLastMin.increment(); } // If it is a replication already, do not replicate again as this will create a poison replication if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) { return; } for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) { // If the url represents this host, do not replicate to yourself. if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) { continue; } replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node); } } finally { tracer.stop(); } }通过上面的代码知道如果没有服务端节点或是其他节点同步过来的服务节点数据就不进行后面的数据处理,直接返回,如果上面条件都不满足则循环向除了当前节点的其他节点进行注册,通过上面的代码我们就明白了eureka是怎么实现服务中心数据的同步以及防止实例注册的循环传播。
2.下面我们看下cancel()方法
public boolean cancel(final String appName, final String id, final boolean isReplication) { if (super.cancel(appName, id, isReplication)) { replicateToPeers(Action.Cancel, appName, id, null, null, isReplication); synchronized (lock) { if (this.expectedNumberOfClientsSendingRenews > 0) { // Since the client wants to cancel it, reduce the number of clients to send renews this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1; updateRenewsPerMinThreshold(); } } return true; } return false; }通过上面的代码了解到,它会去调用AbstractInstanceRegistry类的cancel()方法,在该方法中,它去完成了从ConcurrentHashMap对象的register变量中移除当前需要服务端实例,它也去调用了register()方法中提到的replicateToPeers()方法,只不过最终去调用了各个服务器实例的cancel方法实现。最后去定义了服务实例需要发送的个数以及每分钟刷新的实例个数。
3.在看下renew()方法
public boolean renew(final String appName, final String id, final boolean isReplication) { if (super.renew(appName, id, isReplication)) { replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication); return true; } return false; }从其实现的代码上看,知道它是一个心跳检测方法,首先它也去进行了服务器实例的判断,有该实例才进行心跳的检测,它也调用了replicateToPeers()方法,只不过最终去调用了各个服务器实例的heartbeat方法实现。
4.看下这个internalCancel()方法,这个方法从实现来说只是删除了内部的服务器实例,并没有像cancel()方法那样最终调用远程的去删除,这种可能是在当前服务器下线的时候实现的,删除自己保存的其他服务实例即可。
5.openForTraffic()方法从名字上看可以理解为正常运行什么之类的,其实只看这个还真不知道是做什么用的,我们还是去看看它的实现,它首先去调用了一个更新每分钟刷新个数的公式,然后判断了isAws进行对服务器节点的hearbeat操作,在将ApplicationInfoManager里面的实例对象状态更新为UP,最后在调用了父类AbstractInstanceRegistry类的postInit()方法,在该方法中应用了一个EvictionTask类,我们主要看下在这个类的run()方法中调用的evict()方法,它首先进行了如下判断
if (!isLeaseExpirationEnabled()) { ...... return; }
这个判断是说是否开启了过期时间,如果开启过期时间则进行后面的判断,在isLeaseExpirationEnabled()方法中,我们可以看到
public boolean isLeaseExpirationEnabled() { if (!isSelfPreservationModeEnabled()) { // The self preservation mode is disabled, hence allowing the instances to expire. return true; } return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold; }首先它判断是否开启了自我保护机制,就是在eureka管理页面上看到的那句红色的字,如果开启了自我保护机制,则不在进行对超时的客户端实例对象进行删除操作,如果没有开启自我保护机制则判断每分钟产生的特定实例数量大于0并且上一分钟产生的心跳数量每分钟产生的特定实例数量则没有过期
List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>(); for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) { Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue(); if (leaseMap != null) { for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) { Lease<InstanceInfo> lease = leaseEntry.getValue(); if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) { expiredLeases.add(lease); } } } }
它去遍历了在ConcurrentHashMap对象的register变量中的所有注册实例,对满足已经不存在或当前系统时间大于上次更新时间的实例对象添加到expeiredLease列表中
int registrySize = (int) getLocalRegistrySize(); int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold()); int evictionLimit = registrySize - registrySizeThreshold; int toEvict = Math.min(expiredLeases.size(), evictionLimit); if (toEvict > 0) { logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit); Random random = new Random(System.currentTimeMillis()); for (int i = 0; i < toEvict; i++) { // Pick a random item (Knuth shuffle algorithm) int next = i + random.nextInt(expiredLeases.size() - i); Collections.swap(expiredLeases, i, next); Lease<InstanceInfo> lease = expiredLeases.get(i); String appName = lease.getHolder().getAppName(); String id = lease.getHolder().getId(); EXPIRED.increment(); ...... internalCancel(appName, id, false); } }
然后判断服务器实例总数减去了服务器实例*更新阈值百分比与expeiredLease个数的最小值,如果最新值大于0,则随机的获取删除的实例,调用internalCancel()方法进行删除操作。
通过上面的源码分析我们了解到了服务中心的数据同步以及防止数据的传播性,以及在是否开启自我保护机制的时候如何进行过期实例对象的删除。
对于剩下的EurekaServerBootstrap bean的实现中的contextInitialized()方法,在其中initEurekaEnvironment()方法主要是初始化一些默认值的添加,而在initEurekaServerContext()方法中,它基本上都是在调用上面介绍的PeerAwareInstanceRegistry类,实现openForTraffic()方法以及通过eurekaClient.getApplications()去其他服务器实例中获取已经注册的客户端对象,然后注册到当前的服务器实例中。
在这里还要说明下在官方文档中提到了Why Is It so Slow to Register a Service?通过该文档介绍说是因为默认的一个实例向服务注册发送心跳的默认时间是30秒,只有当实例、服务器和客户端的本地缓存中都有相同的元数据时,客户端才能发送服务,因此可能需要3次心跳来验证,我们可以通过配置参数eureka.instance.leaseRenewalIntervalInSecond来改变心跳的发送时间间隔,但是在生产环境,最好还是不要改变默认的30秒时间间隔,因为在服务器中内部计算对租约时间进行了假设。
到此我们就分析完了所有的eureka主要代码。
相关推荐
Spring Cloud Eureka源码分析
为什么要看源码: 1、提升技术功底:学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提 升自己的技术功底 2、深度掌握技术框架:源码看多了,对于一个新技术或框架的掌握速度会有...
负载均衡器源码分析 33 负载均衡器重试机制 33 服务保护机制SpringCloud Hystrix 33 微服务高可用技术 33 服务降级、熔断、限流概念 34 服务学崩效应 34 服务降级 34 服务熔断 35 服务隔离 35 服务限流 36 ...
spring cloud组件源码分析,包括Eureka,feign,gateway,ribbon,Hystrix组件
本文将从SpringCloud源码角度出发,让大家能够了解到相关组件内部的运行机制,从而更好的回馈开发的流程和配置上,为用户提供更好的方案。SpringCloud提供了微服务架构中的众多组件,例如API网关、注册中心、负载...
资源名字:基于Springcloud+mysql的分布式架构网上商城设计与实现(源码+设计文档+部署说明+视频演示).zip 资源内容:项目全套源码+完整文档 源码说明: 全部项目源码都是经过测试校正后百分百成功运行。 基于...
基于SpingBoot+SpringCloud+Maven+Eureka+Vue的分布式架构网上商城系统源码+数据已获导师指导。 本项目是一套基于SpringCloud的分布式架构网上商城系统,主要针对计算机相关专业的正在做毕设的学生和需要项目实战...
面试必考之HashMap源码分析与实现 ,微服务架构之Spring Cloud Eureka 场景分析与实战,高性能必学之Mysql主从架构实践 ,架构师不得不知道的Spring事物不能回滚的深层次原因 ,分库分表之后分布式下如何保证ID全局...
视频讲解知识内容包括:HashMap源码分析与实现、JVM底层奥秘ClassLoader源码分析与案例讲解、大型网站数据库瓶颈之数据库分库分表方案实践、Spring Cloud Eureka场景分析与实战、分库分表之后分布式下如何保证ID全局...
前文 SpringCloud 简介 SpringCloud 版本选型 SpringCloud 工程构建 SpringCloud —— Eureka 注册中心 SpringCloud —— Eureka 集群 ...文章目录前文Ribbon 负载均衡原理Ribbon 源码分析RoundRo
Spring全家桶源码分析 Tomcat架构原理 Web请求处理原理 数据访问层框架原理 架构与设计思维模式 程序中的数学 数据分析 机器智能算法剖析与应用 云原生 自动化DevOps 流量治理 链路监控 弹性扩容 分布式存储Redis6.0...
应用Spring Cloud Eureka作为服务注册中心 应用Spring Cloud Zuul作为网关分发请求 应用MyBatis-Plus作为持久层框架 使用Ribbon实现了负载均衡技术,自定义均衡算法 拆分Spring Security成单独微服务作为权限验证...
java版商城源码下载 关于作者 前腾讯、前阿里员工,从事Java后台工作; 对Docker和Kubernetes充满热爱...Eureka源码分析专题 spring-cloud-alibaba实战 ; ; ; ; ; spring-cloud-kubernetes特辑 dubbo实战特辑 Docke
2017卧底面试题答案解析.txt...微服务架构之Spring Cloud Eureka 场景分析与实战 高性能必学之Mysql主从架构实践 架构师不得不知道的Spring事物不能回滚的深层次原因 大型公司面试必答之数据结构与算法精讲 ... 等
java版商城源码下载 关于作者 ...前腾讯、前阿里员工,从事Java后台工作; 对Docker和Kubernetes充满热爱;...Eureka源码分析专题 spring-cloud-alibaba实战 ; ; ; ; ; spring-cloud-kubernetes特辑 Docker 基础知
java版商城源码下载 关于作者 ...前腾讯、前阿里员工,从事Java后台工作; 对Docker和Kubernetes充满热爱;...Eureka源码分析专题 spring-cloud-alibaba实战 ; ; ; ; ; spring-cloud-kubernetes特
【原始笔记】专注于Java... SpringCloud(Eureka,Ribbon,Hystrix,Zuul,Config,Feign ...) 四郎 Tomcat 西塔 运河 联合会 卡夫卡 纳科斯 动物园管理员 我的猫 ...... 微信搜: 公众号:原始笔记 联系我:艰苦奋斗
10.微服务架构之Spring Cloud Eureka 场景分析与实战 11.高性能必学之Mysql主从架构实践 13.RPC底层通讯原理之Netty线程模型源码分析 14.分库分表之后分布式下如何保证ID全局唯一性 10道腾讯的Java面试题.txt Dubbo...
│ │ 13.RPC底层通讯原理之Netty线程模型源码分析.wmv │ │ │ ├─14.分库分表之后分布式下如何保证ID全局唯一性 │ │ 14.分库分表之后分布式下如何保证ID全局唯一性.mp4 │ │ │ └─15.大型公司面试必答之...