上期我们大概得了解了Eurake向Spring上下文中注册EurekaServiceRegistry和EurekaRegistration的详细过程,其中总调度类EurekaAutoServiceRegistration还专门采用lifeCycle的方式实现。昨天的分析中我们指出EurekaServiceRegistry是分析的重点,因此今天我们就重点突破一下这块的具体逻辑。

public void register(EurekaRegistration reg) {this.maybeInitializeClient(reg);if (log.isInfoEnabled()) {//打印服务的状态,UP表示正常启动log.info("Registering application " + reg.getInstanceConfig().getAppname() + " with eureka with status " + reg.getInstanceConfig().getInitialStatus());}//设置启动状态reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());if (reg.getHealthCheckHandler() != null) {reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler());}
}

我们原来以为EurekaServiceRegistry有大量的动作,但是发现这个类中什么也没有。等于与一个壳子,重点全是EurekaRegistration既然如此我们就重新分析EurekaRegistration吧。

发现这里有两个Eurake的客户端,但是cloudEurekaClient并没有怎么用唉,好像这里的eurekaClient才是真的老大。现在这里注入进来了,我们看看它是如何初始化的。这块我们直接DiscoveryClient的实现。

在DiscoveryClient的构造方法中,有几个线程相关的,分别是定时任务处理器、心跳线程池、缓存线程池。我们做过SpringCloud的同志都知道服务注册是通过定时任务去拉取服务信息,通过心态检测是否有服务宕机的。除此之外如果注册中心宕机了也会采用缓存。

private void initScheduledTasks() {int renewalIntervalInSecs;int expBackOffBound;//判断是否拉取配置,默认为trueif (this.clientConfig.shouldFetchRegistry()) {//多久拿一次,默认为30秒renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds();//超时容许时间的倍数,和获取心跳或者服务信息失败的时间差的扩容有关系 默认10秒expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();//这里采用了代理模式,都是runnable接口哦,比较绕//启动刷新缓存的线程,也就是获取服务信息,全部打偶放到了定时任务中。this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);}
//判断是否向eureka注册 默认为trueif (this.clientConfig.shouldRegisterWithEureka()) {//多久获取一次服务信息 默认30秒renewalIntervalInSecs = this.instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();//超时容许时间的倍数,和获取心跳或者服务信息失败的时间差的扩容有关系 默认10秒expBackOffBound = this.clientConfig.getHeartbeatExecutorExponentialBackOffBound();logger.info("Starting heartbeat executor: renew interval is: " + renewalIntervalInSecs);//启动心跳线程this.scheduler.schedule(new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);//定义一个监听器,用来看获取定时任务还在吗this.statusChangeListener = new StatusChangeListener() {public String getId() {return "statusChangeListener";}public void notify(StatusChangeEvent statusChangeEvent) {if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);} else {DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);}DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();}};if (this.clientConfig.shouldOnDemandUpdateStatusChange()) {this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);}//启动监听this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());} else {logger.info("Not registering with Eureka server per configuration");}}

上述代码只要用来获取服务的注册和发现。也对上述功能的线程进行了监听。上边的代理模式,我们打开看看TimedSupervisorTask

public void run() {Future future = null;try {//这里task是注册刷新的任务 future = this.executor.submit(this.task);//获取活动线程的个数this.threadPoolLevelGauge.set((long)this.executor.getActiveCount());//使用future任务类型,这里获取拿到的服务信息future.get(this.timeoutMillis, TimeUnit.MILLISECONDS);this.delay.set(this.timeoutMillis);this.threadPoolLevelGauge.set((long)this.executor.getActiveCount());} catch (TimeoutException var12) {//如果超时了,这里还会将当前的时间间隔扩大一倍logger.error("task supervisor timed out", var12);this.timeoutCounter.increment();long currentDelay = this.delay.get();long newDelay = Math.min(this.maxDelay, currentDelay * 2L);this.delay.compareAndSet(currentDelay, newDelay);} catch (RejectedExecutionException var13) {if (!this.executor.isShutdown() && !this.scheduler.isShutdown()) {logger.error("task supervisor rejected the task", var13);} else {logger.warn("task supervisor shutting down, reject the task", var13);}this.rejectedCounter.increment();} catch (Throwable var14) {//如果异常服务处理,就直接中断了if (!this.executor.isShutdown() && !this.scheduler.isShutdown()) {logger.error("task supervisor threw an exception", var14);} else {logger.warn("task supervisor shutting down, can't accept the task");}this.throwableCounter.increment();} finally {if (future != null) {future.cancel(true);}if (!this.scheduler.isShutdown()) {//如果没有启动的话就重新创建一个this.scheduler.schedule(this, this.delay.get(), TimeUnit.MILLISECONDS);}}
}
    //刷新的任务    @VisibleForTestingvoid refreshRegistry() {try {boolean isFetchingRemoteRegionRegistries = this.isFetchingRemoteRegionRegistries();boolean remoteRegionsModified = false;//先会判断这个值是否为空,如果为空话就从这里获取配置String latestRemoteRegions = this.clientConfig.fetchRegistryForRemoteRegions();if (null != latestRemoteRegions) {String currentRemoteRegions = (String)this.remoteRegionsToFetch.get();if (!latestRemoteRegions.equals(currentRemoteRegions)) {synchronized(this.instanceRegionChecker.getAzToRegionMapper()) {if (this.remoteRegionsToFetch.compareAndSet(currentRemoteRegions, latestRemoteRegions)) {//设置最新的服务地址,用来获取服务配置String[] remoteRegions = latestRemoteRegions.split(",");this.remoteRegionsRef.set(remoteRegions);this.instanceRegionChecker.getAzToRegionMapper().setRegionsToFetch(remoteRegions);remoteRegionsModified = true;} else {logger.info("Remote regions to fetch modified concurrently, ignoring change from {} to {}", currentRemoteRegions, latestRemoteRegions);}}} else {this.instanceRegionChecker.getAzToRegionMapper().refreshMapping();}}//查看从eureka服务上获取配置是否成功, 真正的获取服务信息boolean success = this.fetchRegistry(remoteRegionsModified);if (success) {this.registrySize = ((Applications)this.localRegionApps.get()).size();this.lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();}if (logger.isDebugEnabled()) {StringBuilder allAppsHashCodes = new StringBuilder();allAppsHashCodes.append("Local region apps hashcode: ");allAppsHashCodes.append(((Applications)this.localRegionApps.get()).getAppsHashCode());allAppsHashCodes.append(", is fetching remote regions? ");allAppsHashCodes.append(isFetchingRemoteRegionRegistries);Iterator var11 = this.remoteRegionVsApps.entrySet().iterator();while(var11.hasNext()) {Entry<String, Applications> entry = (Entry)var11.next();allAppsHashCodes.append(", Remote region: ");allAppsHashCodes.append((String)entry.getKey());allAppsHashCodes.append(" , apps hashcode: ");allAppsHashCodes.append(((Applications)entry.getValue()).getAppsHashCode());}logger.debug("Completed cache refresh task for discovery. All Apps hash code is {} ", allAppsHashCodes.toString());}} catch (Throwable var9) {logger.error("Cannot fetch registry from server", var9);}}

在从Eureka服务器上获取服务所有信息的时候代码是这样写的

    private void getAndStoreFullRegistry() throws Throwable {long currentUpdateGeneration = this.fetchRegistryGeneration.get();logger.info("Getting all instance registry info from the eureka server");Applications apps = null;//这块就是从拉取服务所有信息的EurekaHttpResponse<Applications> httpResponse = this.clientConfig.getRegistryRefreshSingleVipAddress() == null ?this.eurekaTransport.queryClient.getApplications((String[])this.remoteRegionsRef.get()) : this.eurekaTransport.queryClient.getVip(this.clientConfig.getRegistryRefreshSingleVipAddress(), (String[])this.remoteRegionsRef.get());if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {//处理成实体类型apps = (Applications)httpResponse.getEntity();}logger.info("The response status is {}", httpResponse.getStatusCode());if (apps == null) {logger.error("The application is null for some reason. Not storing this information");} else if (this.fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1L)) {this.localRegionApps.set(this.filterAndShuffle(apps));logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());} else {logger.warn("Not updating applications as another thread is updating it already");}}

看到这里,我也蒙了。本来一串也走下来了,但是让我疑惑的是我的serviceurl在哪里设置进去的?找了一圈remoteRegionsRef也没找到。fetch-remote-regions-registry这个配置是找到了,当我把这两个都配置上的时候就报错了。但是用Idea看的时候确实没有serviceUrl的踪迹。所以我是觉得是反编译的问题?有知道的朋友可以给我留言哦!谢谢

SpringCloud源码学习(二) 面试官问我Eurake服务注册的实现细节?相关推荐

  1. Golang源码学习(二)----Go源码学习基础

    ### 本文源码版本为 GO 1.17.8 Windows/amd64: ### 可能参与对比的版本:GO 1.16.2 Linux/amd64一.Golang的编译器究竟是如何工作的? (学习源码有 ...

  2. SocketServer源码学习(二)

    SocketServer 中非常重要的两个基类就是:BaseServer 和 BaseRequestHandler 在SocketServer 中也提供了对TCP以及UDP的高级封装,这次我们主要通过 ...

  3. 使用base标签后图片无法加载_Spring 源码学习(二)-默认标签解析

    `Spring` 解析默认标签~ 从上一篇笔记可以看出,在容器注册 bean 信息的时候,做了很多解析操作,而 xml 文件中包含了很多标签.属性,例如 bean . import 标签, meta ...

  4. ROS源码学习 二、线程池

    2021SC@SDUSC 目录 1.写在前面 2.ROS线程池概述 3.ROS线程池模型 4.ROS线程池源码详解 5.总结 1.写在前面 ROS作为一个操作系统,其职责是协调具有不同功能的node之 ...

  5. yara 源码学习(二) 规则编译部分

    yara规则的详细信息请参考: https://yara.readthedocs.io/en/stable/writingrules.html 根据官方文档,yara规则长这个样子: [1]:yara ...

  6. Box2d源码学习二内存管理之SOA的实现

    本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8258166 SOA,全称small object al ...

  7. java集合类程序代码_java集合类源码学习二

    我们查看Collection接口的hierarchy时候,可以看到AbstractCollection这样一个抽象类,它实现了Collection接口的部分方法,Collection集合系列的各个集合 ...

  8. java function获取参数_「Java容器」ArrayList源码,大厂面试必问

    ArrayList简介 ArrayList核心源码 ArrayList源码分析 System.arraycopy()和Arrays.copyOf()方法 两者联系与区别 ArrayList核心扩容技术 ...

  9. Thrift源码学习二——Server层

    Thrift 提供了如图五种模式:TSimpleServer.TNonblockingServer.THsHaServer.TThreadPoolServer.TThreadSelectorServe ...

最新文章

  1. C#操作Excel导入导出
  2. python-15:装饰函数之一
  3. 狗屎一样的代码如何重构?
  4. Elasticsearch笔记(七):聚合查询
  5. 美国科学院公布新一批院士名单,中科院曹晓风及五名华裔教授当选
  6. 【java】反射+poi 导出excel
  7. 原创]Windows Gdi入门初级应用(VC SDK)
  8. IT项目中的6类知识转移
  9. 计算机网络流媒体播放,流媒体播放方式包含以下哪几种方式
  10. 【转】计算机人工智能技术纵览---入门部分
  11. BFS POJ 3278 Catch That Cow
  12. ipa逆向分析class-dump的安装和使用
  13. 阿里云历时13年,站上世界现代计算架构之巅
  14. XEQ玻尿酸敏感肌可以用吗?效果怎么样?
  15. 企业知识、经验如何传承?知识管理系统告诉你
  16. apple开发者账号区别
  17. 深度学习目标检测方法
  18. 聊聊从平面设计师转为UI设计师的经历
  19. 程序员英语再渣也要会的单词
  20. 你有你的计划,世界另有计划这本书 万维钢

热门文章

  1. 大头贴边框html,拍大头贴.html · NFUNM104/API_Graduation - Gitee.com
  2. 【css】h5cs3新特性
  3. 如何在一台电脑上同时安装Windows和Linux双操作系统
  4. iMindMap教你如何熟悉自考驾照
  5. ios推送证书过期处理
  6. 人工智能数学基础--概率与统计14:连续随机变量的指数分布、威布尔分布和均匀分布
  7. Redis集群搭建加Springboot配置
  8. 钟汉良日记:莫慌,收获和播种不在一个季节
  9. SpringMVC对HTTP报文体的处理
  10. linux命令 final,shell 命令大全先贴一下FinalShell程序的官方网