一、自动装配

  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()方法:

  1. public void start() {
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. try {
  6. //EurekaServerAutoConfiguration->@Bean EurekaServerBootstrap
  7. eurekaServerBootstrap.contextInitialized(
  8. EurekaServerInitializerConfiguration.this.servletContext);
  9. log.info("Started Eureka Server");
  10. //发布EurekaRegistryAvailableEvent事件
  11. publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
  12. //设置运行状态为true
  13. EurekaServerInitializerConfiguration.this.running = true;
  14. //发布EurekaServerStartedEvent事件
  15. publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
  16. }
  17. catch (Exception ex) {
  18. // Help!
  19. log.error("Could not initialize Eureka servlet context", ex);
  20. }
  21. }
  22. }).start();
  23. }

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

  1. public void contextInitialized(ServletContext context) {
  2. try {
  3. //初始化EurekaServer的运行环境
  4. initEurekaEnvironment();
  5. //初始化EurekaServer的上下文
  6. initEurekaServerContext();
  7.  
  8. context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
  9. }
  10. catch (Throwable e) {
  11. log.error("Cannot bootstrap eureka server :", e);
  12. throw new RuntimeException("Cannot bootstrap eureka server :", e);
  13. }
  14. }

  ③ 进入EurekaServerBootstrap.initEurekaServerContext()方法:

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

  ④ 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()方法:

  1. protected void postInit() {
  2. renewsLastMin.start();
  3. if (evictionTaskRef.get() != null) {
  4. evictionTaskRef.get().cancel();
  5. }
  6. //设置剔除任务EvictionTask
  7. evictionTaskRef.set(new AbstractInstanceRegistry.EvictionTask());
  8. //每隔60s执行一次EvictionTask的run方法
  9. evictionTimer.schedule(evictionTaskRef.get(),
  10. serverConfig.getEvictionIntervalTimerInMs(),
  11. serverConfig.getEvictionIntervalTimerInMs());
  12. }

  ⑦ 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)方法如下:

  1. public void register(final InstanceInfo info, final boolean isReplication) {
  2. int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
  3. if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
  4. leaseDuration = info.getLeaseInfo().getDurationInSecs();
  5. }
  6. //往注册表中注册实例信息,然后执行invalidateCache(),把读写缓存readWriteCacheMap失效掉
  7. super.register(info, leaseDuration, isReplication);
  8. //复制到集群中的其他节 发起http调用,调用集群中的其他节点的注册服务实例接口
  9. replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
  10. }

  3.2 获取全量实例信息接口

  ApplicationsResource.getContainers()方法如下:

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

  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

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

  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. Mysql常用的一些命令

    1.登录Mysql数据库, 打开终端命令输入窗口,输入命令:mysql -u <账户名,一般是root> -p <密码>. 2.显示所有的数据库: show databases ...

  2. Python爬虫实战 批量下载高清美女图片

    彼岸图网站里有大量的高清图片素材和壁纸,并且可以免费下载,读者也可以根据自己需要爬取其他类型图片,方法是类似的,本文通过python爬虫批量下载网站里的高清美女图片,熟悉python写爬虫的基本方法: ...

  3. Excel-Countif函数对重复值的处理之删除、统计和标记

    问题场景 删除重复值: 统计重复项出现的次数: 对比两列,找出重复项. 场景一 某列员工编号user_id相当于身份证,可以唯一代表这个人,需要删除重复项. 目标 将user_id重复的人员所在行都删 ...

  4. sdf文件可以通过database net4工具升级版本

    用database .net4工具打开数据库后,右键数据库->数据库工具->upgrade to->to 4.0 or to 3.5; 可以用来判断数据库版本及是否要升级.

  5. app转iap

    ios打包ipa的四种实用方法(.app转.ipa) http://blog.csdn.net/oiken/article/details/49535369 手动压缩改后缀方式 这种方式与4.1的方法 ...

  6. 【python开发】迈出第一步,这可能是我唯一一次的Python开发了

    好久没写博了,今天就瞎唠唠吧 背景: 组内有个测试平台,是基于Python2+tornado 框架写的,之前自己维护了一套系统的UIweb自动化代码,现在需要集成进去.这很可能是自己唯一一次基于pyt ...

  7. HDU - 5775-Bubble Sort(权值线段树)

    P is a permutation of the integers from 1 to N(index starting from 1). Here is the code of Bubble So ...

  8. Myabtis动态SQL,你真的会了吗?

    目录 前言 什么是动态SQL? 常用的标签 if choose.when.otherwise where foreach set sql include 总结 拓展一下 Mybatis中如何避免魔数? ...

  9. 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP

    洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...

  10. JVM学习第三天(JVM的执行子系统)之类加载机制补充

    昨晚没看完,今天继续 系统的类加载器 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间.这句话可以表达得更通俗一些: ...