深入了解EurekaClient的注册过程
来源:互联网 发布:越穷越爱国 知乎 编辑:程序博客网 时间:2024/06/06 01:39
深入了解EurekaClient的注册过程
目录
1.注册方法–register()
2.插曲:意外的收获–服务续租
3.实现Runnable接口重写run()
4.向注册中心更新状态的flag:isDirty
5.初始化定时任务 initScheduledTasks()
6.总结:EurekaClient register 流程
Eureka 的注册机制
jar: eureka-client-1.6.2.jar
package: com.netflix.discovery
class: EurekaClient 该接口继承了LookupService
@ImplementedBy(DiscoveryClient.class)public interface EurekaClient extends LookupService { ... }
通过EurekaClient接口上的注解@ImplementedBy(DiscoveryClient.class)
我们知道这个接口的默认实现类是DiscoveryClient,这个类中定义了一些客户端的操作方法,本篇仅是看看客户端注册的流程,所以我们将目标放在register()这个方法:
注册方法- - -register()
/*** Register with the eureka service by making the appropriate REST call.*/ boolean register() throws Throwable { logger.info(PREFIX + appPathIdentifier + ": registering service..."); EurekaHttpResponse<Void> httpResponse; try { httpResponse = eurekaTransport.registrationClient.register(instanceInfo); } catch (Exception e) { logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e); throw e; } if (logger.isInfoEnabled()) { logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode()); } return httpResponse.getStatusCode() == 204; }
不难看出,这里调用的register方法仅是将客户端的一些信息使用http请求发送到注册中心,顺藤摸瓜,使用ctrl+alt+h
搜索一哈register()的调用方发现有两个地方调用了register(),分别是renew(),run(),其中renew()是Eureka的心跳定时任务中的run()方法调用的,作用是向注册中心发送心跳,表明这个服务还活着,是客户端实现服务续租功能时调用的方法
插曲:意外的收获–服务续租
/** * Renew with the eureka service by making the appropriate REST call */ boolean renew() { EurekaHttpResponse<InstanceInfo> httpResponse; try { httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null); logger.debug("{} - Heartbeat status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode()); if (httpResponse.getStatusCode() == 404) { REREGISTER_COUNTER.increment(); logger.info("{} - Re-registering apps/{}", PREFIX + appPathIdentifier, instanceInfo.getAppName()); return register(); } return httpResponse.getStatusCode() == 200; } catch (Throwable e) { logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e); return false; } }
那么这个renew()方法并不是我们找的,将目标锁定run():
实现Runnable接口重写run()
public void run() { try { discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } } catch (Throwable t) { logger.warn("There was a problem with the instance info replicator", t); } finally { Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } }
粗略看run(),一个try-catch-finally块里面套这个if语句,而调用register(),就放在if语句中,那么我们就有必要知道这条语句执行的条件,dirtyTimestamp!=null
,顺藤摸瓜,看看isDirtyWithTime()。
向注册中心更新状态的flag:isDirty
/*** @return the lastDirtyTimestamp if is dirty, null otherwise.*/ public synchronized Long isDirtyWithTime() { if (isInstanceInfoDirty) { return lastDirtyTimestamp; } else { return null; } }
一个同步方法,简单的if语句,于是将目标变成isInstanceInfoDirty什么时候为true,查找当前类中,发现了一个setIsDirty()方法:
/** * Sets the dirty flag so that the instance information can be carried to * the discovery server on the next heartbeat. */ public synchronized void setIsDirty() { isInstanceInfoDirty = true; lastDirtyTimestamp = System.currentTimeMillis(); }
又用到了源码三键 ctrl+alt+h
找到了规律:
这几个地方要么是初始化的时候设置instanceinfo,要么是刷新instanceinfo的时候,而isDirty的定义就是是否跟原来的instanceinfo一样,就像上面方法中的文档所说,这是一个是否进行重新注册,发送心跳的标志。于是乎这里的逻辑都理通了。
下面回到run(),不难发现try块中第一件事情就是refreshInstanceInfo(),也就是检查之前的配置信息和现在的是否相同,为后面的register()立flag。
顺藤摸瓜 ctrl+alt+h
找到
初始化定时任务 initScheduledTasks()
/** * Initializes all scheduled tasks. */ private void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { ... } if (clientConfig.shouldRegisterWithEureka()) { ... } }
我们发现了老朋友,这不就是咱们最最最开始设置的两兄弟吗,还记得YAML中的设置吗,当我们将微服务作为EurekaClient时,我们并没有设置这两个值,它们默认为TRUE,看来我们找对了,于是看看这两个if块中都有什么:
if (clientConfig.shouldFetchRegistry()) { // registry cache refresh timer int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); }
设置了一个定时任务,用来定时刷新注册中心的服务列表
if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs); // Heartbeat timer scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); // InstanceInfo replicator instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); }
这里做了几件事情:
1.设置发送心跳的定时任务
2.设置状态改变的监听者,当instance状态改变时更新向注册中心更新信息
最后这个初始化的方法是在DiscoveryClient( … ){ .. }的构造方法中调用的,到此EurekaClient的注册就说完了,我们来捋一捋
总结:EurekaClient register 流程
项目启动》构造DiscoveryClient对象》调用initScheduledTasks()》初始化定时任务》执行注册、获取服务列表
细心的小伙伴已经发现了,这里并没有直接调用线程的start()方法,而是设置了一个延迟,
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
这个值默认为40秒,所以服务启动的时候并不是直接向服务中心进行注册的,而是延迟40秒才发送请求。
- 深入了解EurekaClient的注册过程
- EurekaClient获取注册到Eureka上的微服务信息
- 深入了解CLR的加载过程
- 深入了解CLR的加载过程
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 深入了解android平台的jni---注册native函数
- 【重点】深入了解android平台的jni---注册native函数
- 深入了解软件测试过程
- 【checkpoint】深入了解checkpoint过程
- 深入了解存储过程的编写经验和优化措施
- 从信令上深入了解鉴权和加密的过程
- 深入了解DHCP的4步租约过程
- Kafka_Shell命令
- postgreSQL数据类型字符串和数值相互转换
- 【机器学习】交叉验证(cross-validation)
- Linux入门基础教程
- Java number
- 深入了解EurekaClient的注册过程
- 软件开发时设计时要遵循的原则
- 一个递归抛出异常的java程序
- 报org.hibernate.MappingException: Unknown entity:
- Spring framework(2):资源访问&资源加载
- mock记录
- 剑指offer-旋转数组
- 【android逆向笔记】(五)android软件安全与逆向第一个实例
- Tensorflow环境搭建&基本概念