一、自动装配

  1、根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-starter-netflix-eureka-server的spring.factories,查看spring.factories如下:

  2、进入EurekaServer的自动装配类EurekaServerAutoConfiguration:

  3、@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)也就是说当容器中有EurekaServerMarkerConfig uration.Marker.class时,该配置类才起作用。接下来看下启动类EurekaApplication,该启动类上不光有@SpringBootApplication自动装配,还有@EnableEurekaServer,开启EurekaServer。

二、EurekaServer

  1、@EnableEurekaServer注解开启EurekaServer

  1.1 点进去@EnableEurekaServer点进去,发现其@Import(EurekaServerMarkerConfiguration.class),导入了EurekaServer MarkerConfiguration配置类

  1.2 EurekaServerMarkerConfiguration配置类,该配置类导入了EurekaServerMarkerConfiguration.Marker.class。如下:

  2、EurekaServerAutoConfiguration配置类

  当容器中有EurekaServerMarkerConfiguration.Marker.class,就可以激活该配置类,接下来详细看下该配置类为我们做了什么。

  2.1 @Import(EurekaServerInitializerConfiguration.class)

  

  EurekaServerInitializerConfiguration配置类实现了SmartLifecycle,我们知道实现了SmartLifecycle接口的,会在Ioc容器中所有Bean初始化完成后,根据isAutoStartup()方法返回true来执行该配置类的start()

  ① 进入EurekaServerInitializerConfiguration.start()方法:

public void start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//EurekaServerAutoConfiguration->@Bean EurekaServerBootstrap
eurekaServerBootstrap.contextInitialized(
EurekaServerInitializerConfiguration.this.servletContext);
log.info("Started Eureka Server");
//发布EurekaRegistryAvailableEvent事件
publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
//设置运行状态为true
EurekaServerInitializerConfiguration.this.running = true;
//发布EurekaServerStartedEvent事件
publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
}
catch (Exception ex) {
// Help!
log.error("Could not initialize Eureka servlet context", ex);
}
}
}).start();
}

  ② 进入EurekaServerBootstrap.contextInitialized(ServletContext context)方法:

public void contextInitialized(ServletContext context) {
try {
//初始化EurekaServer的运行环境
initEurekaEnvironment();
//初始化EurekaServer的上下文
initEurekaServerContext(); context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
}
catch (Throwable e) {
log.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}

  ③ 进入EurekaServerBootstrap.initEurekaServerContext()方法:

protected void initEurekaServerContext() throws Exception {
//省略非核心代码
//从集群其他节点复制注册
int registryCount = this.registry.syncUp();
/**
* 1、修改状态为UP
* 2、调用父类的postInit 开启一个剔除定时任务,每隔60执行一次,从当前服务清单中把超时(默认90秒)没有续约剔
*/
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}

  ④ PeerAwareInstanceRegistryImpl.syncUp()方法:

  该方法中的eurekaClient.getApplications()是通过http调用,获取集群中的其他节点的所有服务实例。然后遍历获取到的apps,根据isRegisterable(instance)判断是否可注册,如果可以注册就调用register(instance, instance.getLeaseInfo().getDurationInSecs(), true)进行注册,注册实质就是往AbstractInstanceRegistry的属性private final ConcurrentHashMap<String, Map<String, Lease <InstanceInfo>>> registry = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();中加入服务实例信息。

  ⑤ PeerAwareInstanceRegistryImpl.openForTraffic()方法:

  该方法核心第一步:applicationInfoManager.setInstanceStatus(InstanceStatus.UP)修改状态为UP,第二步:调用super.postInit();开启一个剔除定时任务,每隔60执行一次,从当前服务清单中把超时(默认90秒)没有续约剔除。

  ⑥ 进入AbstractInstanceRegistry.postInit()方法:

protected void postInit() {
renewsLastMin.start();
if (evictionTaskRef.get() != null) {
evictionTaskRef.get().cancel();
}
//设置剔除任务EvictionTask
evictionTaskRef.set(new AbstractInstanceRegistry.EvictionTask());
//每隔60s执行一次EvictionTask的run方法
evictionTimer.schedule(evictionTaskRef.get(),
serverConfig.getEvictionIntervalTimerInMs(),
serverConfig.getEvictionIntervalTimerInMs());
}

  ⑦ EvictionTask的run方法:

  执行AbstractInstanceRegistry.evict(),剔除逻辑:主要的功能是将注册表registry,其实就是一个ConcurrentHashMap的所有注册实例遍历下,看哪些是过期的,过期了就加入到expiredLeases中,然后遍历expiredLeases,执行internalCancel方法把实例状态修改成DELETED状态,这样客户端就拿不到。

  2.2 导入了一些Bean

  ① EurekaServerConfig:初始化eurekaServer配置;

  ② EurekaController:初始化dashboard的相关接口,用户获取eurekaServer的相关信息;

  ③ PeerAwareInstanceRegistry:初始化集群注册表;

  ④ PeerEurekaNodes:初始化集群节点;

  ⑤ EurekaServerContext:基于eurekaServer配置,注册表,集群节点,以及服务实例初始化eurekaServer上下文;

  ⑥ EurekaServerBootstrap:初始化eureka启动类;

  ⑦ FilterRegistrationBean:往Filter注册表里面注册一个Jsrsey过滤器;

  其中EurekaServerContext的默认实现DefaultEurekaServerContext在初始化的时候会调用initialize()方法,流程图如下:

  3、Eureka的Jersey服务

  3.1 服务注册接口

  ApplicationResource.addInstance()方法,核心逻辑就是调用PeerAwareInstanceRegistryImpl.register(final InstanceInfo info, final boolean isReplication)方法如下:

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();
}
//往注册表中注册实例信息,然后执行invalidateCache(),把读写缓存readWriteCacheMap失效掉
super.register(info, leaseDuration, isReplication);
//复制到集群中的其他节 发起http调用,调用集群中的其他节点的注册服务实例接口
replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

  3.2 获取全量实例信息接口

  ApplicationsResource.getContainers()方法如下:

public Response getContainers(@PathParam("version") String version,
@HeaderParam(HEADER_ACCEPT) String acceptHeader,
@HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
@HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
@Context UriInfo uriInfo,
@Nullable @QueryParam("regions") String regionsStr) { //省略......
//1、构建缓存key
Key cacheKey = new Key(Key.EntityType.Application,
ResponseCacheImpl.ALL_APPS,
keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
);
Response response;
if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
//省略......
} else {
//2、根据缓存key从缓存中获取,首先从只读缓存中取为null,再去读写缓存中取,然后设置到只读缓存中
response = Response.ok(responseCache.get(cacheKey))
.build();
}
return response;
}

  3.3 获取增量实例信息接口

  ApplicationsResource.getContainerDifferential()方法,逻辑同获取全量实例信息接口一样,不同在于构建缓存key的时候传入的ALL_APPS_DELTA,而获取全量实例信息接口传入的是ALL_APPS。

  3.4 心跳接口

  InstanceResource.renewLease()方法,核心逻辑就是调用PeerAwareInstanceRegistryImpl.renew(final String appName, final String id, final boolean isReplication)方法进行续约,其实就是设置实例的lastUpdateTimestamp为当前时间+duration

public void renew() {
//设置lastUpdateTimestamp为当前时间+duration
lastUpdateTimestamp = System.currentTimeMillis() + duration;
}

  3.5 服务下线接口

  InstanceResource.cancelLease()方法,核心就是调用PeerAwareInstanceRegistryImpl.cancel(final String appName, final String id,final boolean isReplication)方法进行服务下线,其实就是把实例的状态设置成DELETE,然后执行invalidateCache(),把读写缓存readWriteCacheMap失效掉

三、Eureka服务端流程图

  自此Eureka服务端源码解析完成,接下来将对Eureka客户端源码进行解析。Eureka应用详见:Spring Cloud系列(二):Eureka应用详解

Spring Cloud系列(三):Eureka源码解析之服务端的更多相关文章

  1. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  2. ThreadPoolExecutor系列三——ThreadPoolExecutor 源码解析

    ThreadPoolExecutor 源码解析 本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.htm ...

  3. Spring Cloud系列(四):Eureka源码解析之客户端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...

  4. Eureka源码解析系列文章汇总

    先看一张图 0 这个图是Eureka官方提供的架构图,整张图基本上把整个Eureka的核心功能给列出来了,当你要阅读Eureka的源码时可以参考着这个图和下方这些文章 EurekaServer Eur ...

  5. Mybatis 系列6-结合源码解析节点配置:objectFactory、databaseIdProvider、plugins、mappers

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  6. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  7. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  8. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  9. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

随机推荐

  1. Unimrcp通过Vendor传递随路数据

    摘要 项目中需要在MRCPV2的识别消息中,传递一些随路数据.

  2. Docker启动失败

    报错信息: Job for docker.service failed because the control process exited with error code. See "sy ...

  3. centOS7 设置mysql数据库外网可以访问

    1.查看目前防火墙是否对外开发了3306端口 firewall-cmd --list-all 2.查看3306端口是否开放 firewall-cmd --query-port=3306/tcp no ...

  4. Zookeeper原生客户端

    1.1.1.1. 客户端基本操作 package cn.enjoy.javaapi; import org.apache.zookeeper.*; import java.io.IOException ...

  5. SpringMVC-乱码问题

    乱码问题 目录 乱码问题 1. 使用原生filter解决 1. 前端jsp 2. 编写controller 3. 编写过滤器 4. 注册过滤器 2. 使用SpringMVC提供的过滤器实现 1. 使用 ...

  6. python基础:用yagmail模块发邮件

    一.yagmail模块安装 网上下载的yagmail模块安装以后,在使用过程中,如果附件有中文,会显示乱码. pip install yagmail-0.10.212-py2.py3-none-any ...

  7. numpy函数笔记(持续更新)

    numpy函数笔记 np.isin用法 np.isin(a,b) 用于判定a中的元素在b中是否出现过,如果出现过返回True,否则返回False,最终结果为一个形状和a一模一样的数组.(注意:这里的a ...

  8. python中使用cookie进行模拟登录

    背景:使用cookie模拟登录豆瓣->我的豆瓣网页 [准备工作] 1.通过Fiddler抓取“我的豆瓣”url: 2.通过Fiddler抓取“我的豆瓣”cookie值. import urlli ...

  9. JavaScript中关于获取浏览器可视窗口的几个兼容性写法的理解

    1.浏览器可视窗口的概述: 浏览器可视区域不是网页的body的大小.可视区指的是浏览器减去上面菜单栏.工具栏,下面状态栏和任务栏,右边滚动条(如果有的话)后的中间网页内容的单页面积大小.而body大小 ...

  10. 《图解HTTP》学习笔记之入门

    学习HTTP之前,很必要了解一下关于Web网络基础. TCP/IP协议族 1.应用层:应用层决定了向用户提供应用 服务时的通信活动,比如FTP(文件传输协议).DNS(域名系统).HTTP(超文本传输 ...