目录
三. Eureka Server服务注册接⼝(接受客户端注册服务)
四. Eureka Server服务续约接⼝(接受客户端续约)
一. Eureka Server启动过程
⼊⼝:SpringCloud充分利⽤了SpringBoot的⾃动装配的特点
- 观察eureka-server的jar包,发现在META-INF下⾯有配置⽂件spring.factories
springboot应⽤启动时会加载EurekaServerAutoConfifiguration⾃动配置类
- EurekaServerAutoConfifiguration类
观察类头分析
三个关注点
----------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------
(一)关注点1
![](https://img-blog.csdnimg.cn/3e5d33a78cf745959f387c41b8d2a749.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/5c3923479e404be7981d85f34f470673.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
----------------------------------------------------------------------------------------------------------------------------------------
(二)关注点2
2)上图中的 2 、 关注EurekaServerAutoConfifiguration
回到主配置类中EurekaServerAutoConfifiguration类中找到:
进入到DefaultEurekaServerContext 这个类中,继续,找到initialize这个方法
回到主配置类DefaultEurekaServerContext 中 关注如下两个Bean:
----------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------
(三)关注点3
3) 上图中3)关注EurekaServerInitializerConfifiguration
![](https://img-blog.csdnimg.cn/188d02328d8843fca9977a62aa98e7ad.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
重点关注initEurekaServerContext()
研究⼀下上图中的syncUp⽅法,进入实现类PeerAwareInstanceRegistryImpl :
![](https://img-blog.csdnimg.cn/ccd44f90210b4cd8822de9e8d0b5692d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/24bc975774f44dcd93887acb5fa220a9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/90186f520c5c4a5badb1cf2eab62aedc.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
二. Eureka Server服务接⼝暴露策略
![](https://img-blog.csdnimg.cn/102f11b0979244a1a5dc9499062b9abb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
注⼊的Jersey细节
点击查看EUREKA_PACKAGES,看看扫描的包路径:
对外提供的接⼝服务,在Jersey中叫做资源
三. Eureka Server服务注册接⼝(接受客户端注册服务)
![](https://img-blog.csdnimg.cn/2a66cf5242894abe891f10f1dcf287ce.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
/**
* Registers a new instance with a given duration.
*
* @see com.netflix.eureka.lease.LeaseManager#register(java.lang.Object, int, boolean)
*/
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
read.lock(); //读锁
// registry是保存所有应⽤实例信息的
// Map:ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
// 从registry中获取当前appName的所有实例信息
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);//注册统计+1
// 如果当前appName实例信息为空,新建Map
if (gMap == null) {
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) {
gMap = gNewMap;
}
}
// 获取实例的Lease租约信息
Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
// Retain the last dirty timestamp without overwriting it, if there is already a lease
// 如果已经有租约,则保留最后⼀个脏时间戳⽽不覆盖它
// (⽐较当前请求实例租约 和 已有租约 的LastDirtyTimestamp,选择靠
// 后的)
if (existingLease != null && (existingLease.getHolder() != null)) {
Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
// this is a > instead of a >= because if the timestamps are equal, we still take the remote transmitted
// InstanceInfo instead of the server local copy.
if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" +
" than the one that is being registered {}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant");
registrant = existingLease.getHolder();
}
} else {
// The lease does not exist and hence it is a new registration
// 如果之前不存在实例的租约,说明是新实例注册
// expectedNumberOfRenewsPerMin期待的每分钟续约数+2(因为30s⼀个)
// 并更新numberOfRenewsPerMinThreshold每分钟续约阀值(85%)
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to register it, increase the number of clients sending renews
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
updateRenewsPerMinThreshold();
}
}
logger.debug("No previous lease information found; it is new registration");
}
Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);
if (existingLease != null) {
lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
}
//当前实例信息放到维护注册信息的Map
gMap.put(registrant.getId(), lease);
// 同步维护最近注册队列
synchronized (recentRegisteredQueue) {
recentRegisteredQueue.add(new Pair<Long, String>(
System.currentTimeMillis(),
registrant.getAppName() + "(" + registrant.getId() + ")"));
}
// This is where the initial state transfer of overridden status happens
// 如果当前实例已经维护了OverriddenStatus,将其也放到此
// EurekaServer的overriddenInstanceStatusMap中
if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {
logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the "
+ "overrides", registrant.getOverriddenStatus(), registrant.getId());
if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {
logger.info("Not found overridden id {} and hence adding it", registrant.getId());
overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());
}
}
InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());
if (overriddenStatusFromMap != null) {
logger.info("Storing overridden status {} from map", overriddenStatusFromMap);
registrant.setOverriddenStatus(overriddenStatusFromMap);
}
// Set the status based on the overridden status rules
// 根据overridden status规则,设置状态
InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);
registrant.setStatusWithoutDirty(overriddenInstanceStatus);
// If the lease is registered with UP status, set lease service up timestamp
// 如果租约以UP状态注册,设置租赁服务时间戳
if (InstanceStatus.UP.equals(registrant.getStatus())) {
lease.serviceUp();
}
//ActionType为ADD
registrant.setActionType(ActionType.ADDED);
//维护recentlyChangedQueue
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
//更新最后更新时间
registrant.setLastUpdatedTimestamp();
// 使当前应⽤的ResponseCache失效
invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
logger.info("Registered instance {}/{} with status {} (replication={})",
registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);
} finally {
read.unlock();//读锁
}
}
再查看上图的replicateToPeers
![](https://img-blog.csdnimg.cn/5f8a538d2ee84355b733020132fc8147.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
/**
* Replicates all eureka actions to peer eureka nodes except for replication
* traffic to this node.
*
*/
private void replicateToPeers(Action action, String appName, String id,
InstanceInfo info /* optional */,
InstanceStatus newStatus /* optional */, boolean isReplication) {
Stopwatch tracer = action.getTimer().start();
try {
// 如果是复制操作(针对当前节点,false)
if (isReplication) {
numberOfReplicationsLastMin.increment();
}
// If it is a replication already, do not replicate again as this will create a poison replication
// 如果它已经是复制,请不要再次复制,直接return
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;
}
// 复制Instance实例操作到某个node节点
replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
}
} finally {
tracer.stop();
}
}
继续查看PeerAwareInstanceRegistryImpl#replicateInstanceActionsToPeers
四. Eureka Server服务续约接⼝(接受客户端续约)
![](https://img-blog.csdnimg.cn/1505251fca0c46a3be558114d07ee220.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
com.netflflix.eureka.registry.PeerAwareInstanceRegistryImpl#renew
上图继续调用replicateToPeers方法,在该方法中又调用replicateInstanceActionsToPeers() 复制Instance实例操作到其它节点。
renew()⽅法中—>leaseToRenew.renew()—>对最后更新时间戳进⾏更新
五. Eureka Client注册服务
![](https://img-blog.csdnimg.cn/1b7bc046a3bd40ad8ce2fe6a8b102656.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZW50ZXJwYw==,size_20,color_FFFFFF,t_70,g_se,x_16)
引⼊jar就会被⾃动装配,分析EurekaClientAutoConfifiguration类头
如果不想作为客户端,可以设置eureka.client.enabled=false
回到主配置类EurekaClientAutoConfifiguration
(一)读取配置⽂件
(二)启动时从EurekaServer获取服务实例信息
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance,
@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
//If we use the proxy of the ApplicationInfoManager we could run into a problem
//when shutdown is called on the CloudEurekaClient where the ApplicationInfoManager bean is
//requested but wont be allowed because we are shutting down. To avoid this we use the
//object directly.
ApplicationInfoManager appManager;
if(AopUtils.isAopProxy(manager)) {
appManager = ProxyUtils.getTargetObject(manager);
} else {
appManager = manager;
}
CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager, config, this.optionalArgs,
this.context);
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
return cloudEurekaClient;
}
进入上图 CloudEurekaClient构造方法
观察⽗类DiscoveryClient()
在另外⼀个构造器中 :进入DiscoveryClient类的DiscoveryClient方法,并找到如下
进入fetchRegistry方法继续查看
(三)注册⾃⼰到EurekaServer
在DiscoveryClient类的DiscoveryClient方法,并找到如下
进入 DiscoveryClient#register方法
底层使⽤Jersey客户端进⾏远程请求。
(四)开启⼀些定时任务(⼼跳续约,刷新本地服务缓存列表)
在DiscoveryClient类的DiscoveryClient方法,并找到如下:
点击进入initScheduledTasks方法
点击进入CacheRefreshThread()方法
六. Eureka Client⼼跳续约
回到initScheduledTasks() 方法:
点击进入HeartbeatThread()方法
点击进入renew()方法
点击进入sendHeartBeat方法
DiscoveryClient类initScheduledTasks() 方法完整代码如下:
/**
* Initializes all scheduled tasks.
*/
private void initScheduledTasks() {
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");
}
}
七. Eureka Client下架服务
我们看com.netflflix.discovery.DiscoveryClient#shutdown