SpringCloud源码阅读1-EurekaServer源码的秘密(上)

简介: SpringCloud源码阅读1-EurekaServer源码的秘密(上)

题外话:


Spring Cloud Netflix 作为springcloud 我们常用的一个项目,其子项目Eureka,zuul,Rebbion是我熟悉的。但是Spring Cloud Netflix 被宣布进入了维护模式, 意思不再添加新特性了,这对于我们来说很不友好了。 大家纷纷寻找相应的替代工具。(具体可以网上搜索)

但这不影响我们学习一些组件的框架思想。我对注册发现,负载均衡这块比较感兴趣。所以在此记录下自己的阅读心得。

版本说明:Finchley.SR1


1.组件的配置:


1.1 启用Eureka注册中心

当我们在springboot的启动类上加上@EnableEurekaServer,一个基本的注册中心就可以生效了。

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
}
}

@EnableEurekaServer仅仅是引入EurekaServerMarkerConfiguration类。 Marker的英文意思是标记的意思,spring相关框架中有很多类似xxxMarkerxxx这样的注解.其实他们的意思就是一个开关。会在其他地方进行开关的判断,有对应xxxMarkerxxx类就表示打开,没有表示关闭。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}

EurekaServerMarkerConfiguration开关打开的是哪个类呢??

org.springframework.cloud.netflix.eureka.server项目spring.factories资源文件中自动注入类EurekaServerAutoConfiguration,此类在自动注入的过程中,会判断开关是否打开来决定是否自动注入相关类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
    InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
  .....
}

由此看出EurekaServerMarkerConfiguration开关打开的EurekaServerAutoConfiguration


1.2 组件的配置。

下面我们看看EurekaServerAutoConfiguration配置了什么东西。 (1.先看注解上相关配置

@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
    InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
  ...
}
  • 引入EurekaServerInitializerConfiguration类,此类继承了SmartLifecycle接口,所以会在spring启动完毕时回调此类的start()方法
  • EurekaDashboardProperties 表示Euerka面板相关配置属性。例如:是否打开面板;面板的访问路径
  • InstanceRegistryProperties 表示实例注册相关配置属性。例如:每分钟最大的续约数量,默认打开的通信数量 等
  • 加载/eureka/server.properties的配置属性。

(2.再看类内部相关配置(代码比较长,这里只讲内容,建议打开源码看)寻找类中的Bean

  • HasFeatures 注册HasFeatures表示Eureka特征,
  • EurekaServerConfigBean配置类,表示EurekaServer的配置信息。通过@ConfigurationProperties(“eureka.server”)映射我们的配置文件中的eureka.server.xxxx格式的配置信息(此类很重要啊,我们想修改EurekaServer的配置信息,可以配置eureka.server.xxxx覆盖此类中的默认配置)
  • EurekaController: 面板的访问配置默认是“/”
  • 注册编码器(ServerCodecs)CloudServerCodecs
  • PeerAwareInstanceRegistry:对等节点同步器。 多个节点下复制相关。 与注册中心高可用有关的组件。此处注册的是 InstanceRegistry(注意PeerAwareInstanceRegistry实现了AbstractInstanceRegistry,这里准确的说是 对等节点+当前节点同步器
  • PeerEurekaNodes: Eureka-Server 集群节点的集合。存储了集群下各个节点信息。也是与高可用有关。
  • EurekaServerContext : 上下文。默认注册的DefaultEurekaServerContext
  • EurekaServerBootstrap: EurekaServer启动器。EurekaServerBootstrap
  • FilterRegistrationBean: 注册 Jersey filter过滤器。这里有必要讲一下。Eureka也是servlet应用。不过他是通过Jersey 框架来提供接口的。Jersey 框架是一个类Springmvc的web框架。我们项目中大多都是使用springmvc来处理。所以注册 Jersey filter过滤器,把/eureka开头的请求都交给Jersey 框架去解析。容器是com.sun.jersey.spi.container.servlet.ServletContainer
  • ApplicationResource: 暴漏com.netflix.discovery","com.netflix.eureka"包路径下的接口。通常我们再springmvc中通过Controller概念来表示接口,Jersey框架下用ApplicationResource的概念来表示接口。暴露的接口其实就是eureka各个应用通信的接口。(下面再说这些接口)

EurekaServerAutoConfiguration基本上就做了这些工作。我们来归类总结下

针对当前Eureka实例的相关组件:

  • EurekaDashboardProperties:面板属性
  • EurekaController: 面板的访问的处理器。
  • InstanceRegistryProperties:实例注册相关属性
  • (EurekaServerConfig)EurekaServerConfigBean:当前ErekekaServer相关配置
  • EurekaServerContext : 当前Eureka 注册中心上下文
  • 请求相关组件:注册/eureka路径的相关接口,注册拦截/eureka的拦截器,注册com.sun.jersey.spi.container.servlet.ServletContainer容器来处理对应的请求

两个针对集群下相关组件:

  • PeerAwareInstanceRegistry:用于集群下的节点相关复制信息用
  • PeerEurekaNodes:集群下的所有节点信息

两个针对启动相关类:

  • EurekaServerInitializerConfiguration: 对接spring,再spring启动完成后,调用
  • EurekaServerBootstrap:启动器,用于启动当前Eureak实例的上下文

至此:我们也可以大致了解了一个EurekaServer大致长什么样子了。



2.EurekaServerContext初始化:


EurekaServerContext作为上下文,应该是核心所在。上文讲过注册DefaultEurekaServerContext。此类中有@Inject,@PostConstruct, @PreDestroy注解的方法,重点来看看。

@Inject
public DefaultEurekaServerContext(EurekaServerConfig serverConfig,
                               ServerCodecs serverCodecs,
                               PeerAwareInstanceRegistry registry,
                               PeerEurekaNodes peerEurekaNodes,
                               ApplicationInfoManager applicationInfoManager) {
        this.serverConfig = serverConfig;
        this.serverCodecs = serverCodecs;
        this.registry = registry;
        this.peerEurekaNodes = peerEurekaNodes;
        this.applicationInfoManager = applicationInfoManager;
}


2.1 @Inject注解的构造方法

@Inject注解的方法,参数由IOC容器注入。serverConfig ,serverCodecs ,registry ,peerEurekaNodes我们已经认识了。ApplicationInfoManager 是用来管理应用信息的,也就是实例注册信息,由ApplicationInfoManager统一管理。


2.2 @PostConstruct注解的initialize()方法

@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次,被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行.

@PostConstruct
@Override
public void initialize() {
        logger.info("Initializing ...");
        peerEurekaNodes.start();
        try {
            registry.init(peerEurekaNodes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        logger.info("Initialized");
}

这个方法很简明,主要有两个重要的的点:

  • peerEurekaNodes.start();
  • registry.init(peerEurekaNodes);


2.2.1 peerEurekaNodes.start()

PeerEurekaNodes: 用于管理PeerEurekaNode节点集合。 peerEurekaNodes.start();

public void start() {
    //创建一个单线程定时任务线程池:线程的名称叫做Eureka-PeerNodesUpdater
        taskExecutor = Executors.newSingleThreadScheduledExecutor(
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
                        thread.setDaemon(true);
                        return thread;
                    }
                }
        );
        try {
          // 解析Eureka Server URL,并更新PeerEurekaNodes列表
            updatePeerEurekaNodes(resolvePeerUrls());
            //创建任务
            //任务内容为:解析Eureka Server URL,并更新PeerEurekaNodes列表
            Runnable peersUpdateTask = new Runnable() {
                @Override
                public void run() {
                    try {
                        updatePeerEurekaNodes(resolvePeerUrls());
                    } catch (Throwable e) {
                        logger.error("Cannot update the replica Nodes", e);
                    }
                }
            };
            //交给线程池执行,执行间隔10min
            taskExecutor.scheduleWithFixedDelay(
                    peersUpdateTask,
                    serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
                    serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
                    TimeUnit.MILLISECONDS
            );
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        for (PeerEurekaNode node : peerEurekaNodes) {
            logger.info("Replica node URL:  {}", node.getServiceUrl());
        }
}


resolvePeerUrls():

解析配置的对等体URL。就是在配置文件中配置的多个Eureka注册中心的URL.


updatePeerEurekaNodes:
protected void updatePeerEurekaNodes(List<String> newPeerUrls) {
        //计算需要移除的url= 原来-新配置。
        Set<String> toShutdown = new HashSet<>(peerEurekaNodeUrls);
        toShutdown.removeAll(newPeerUrls);
        //计算需要增加的url= 新配置-原来的。
        Set<String> toAdd = new HashSet<>(newPeerUrls);
        toAdd.removeAll(peerEurekaNodeUrls);
        //没有变化就不更新
        if (toShutdown.isEmpty() && toAdd.isEmpty()) { // No change
            return;
        }
        List<PeerEurekaNode> newNodeList = new ArrayList<>(peerEurekaNodes);
         // 删除需要移除url对应的节点。
        if (!toShutdown.isEmpty()) {
            int i = 0;
            while (i < newNodeList.size()) {
                PeerEurekaNode eurekaNode = newNodeList.get(i);
                if (toShutdown.contains(eurekaNode.getServiceUrl())) {
                    newNodeList.remove(i);
                    eurekaNode.shutDown();
                } else {
                    i++;
                }
            }
        }
        // 添加需要增加的url对应的节点
        if (!toAdd.isEmpty()) {
            logger.info("Adding new peer nodes {}", toAdd);
            for (String peerUrl : toAdd) {
                newNodeList.add(createPeerEurekaNode(peerUrl));
            }
        }
        //更新节点列表
        this.peerEurekaNodes = newNodeList;
        //更新节点url列表
        this.peerEurekaNodeUrls = new HashSet<>(newPeerUrls);
    }

总结:start()方法,其实就是完成新配置的eureka集群信息的初始化更新工作。


2.2.2 registry.init(peerEurekaNodes)

对等节点同步器的初始化。

public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
        //统计最近X秒内的来自对等节点复制的续约数量(默认1秒)
        this.numberOfReplicationsLastMin.start();
        this.peerEurekaNodes = peerEurekaNodes;
        //初始化返回结果缓存
        initializedResponseCache();
        //更新续约阀值
        scheduleRenewalThresholdUpdateTask();
        //初始化远程区域注册 相关信息
        initRemoteRegionRegistry();
        ...
}


numberOfReplicationsLastMin.start():

启动一个定时任务,任务名称为Eureka-MeasureRateTimer,每1秒统计从对等节点复制的续约数,将当前的桶的统计数据放到lastBucket,当前桶置为0

this.numberOfReplicationsLastMin = new MeasuredRate(1000 * 60 * 1);
--
this.timer = new Timer("Eureka-MeasureRateTimer", true);
---
timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        // Zero out the current bucket.
                        lastBucket.set(currentBucket.getAndSet(0));
                    } catch (Throwable e) {
                        logger.error("Cannot reset the Measured Rate", e);
                    }
                }
}, sampleInterval, sampleInterval);

注意:此统计器用于节点之间复制的统计。


相关文章
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
399 70
|
11月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
269 2
|
7月前
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
8月前
|
前端开发 Java Nacos
🛡️Spring Boot 3 整合 Spring Cloud Gateway 工程实践
本文介绍了如何使用Spring Cloud Alibaba 2023.0.0.0技术栈构建微服务网关,以应对微服务架构中流量治理与安全管控的复杂性。通过一个包含鉴权服务、文件服务和主服务的项目,详细讲解了网关的整合与功能开发。首先,通过统一路由配置,将所有请求集中到网关进行管理;其次,实现了限流防刷功能,防止恶意刷接口;最后,添加了登录鉴权机制,确保用户身份验证。整个过程结合Nacos注册中心,确保服务注册与配置管理的高效性。通过这些实践,帮助开发者更好地理解和应用微服务网关。
1252 0
🛡️Spring Boot 3 整合 Spring Cloud Gateway 工程实践
|
7月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
127 0
|
9月前
|
人工智能 安全 Java
AI 时代:从 Spring Cloud Alibaba 到 Spring AI Alibaba
本次分享由阿里云智能集团云原生微服务技术负责人李艳林主讲,主题为“AI时代:从Spring Cloud Alibaba到Spring AI Alibaba”。内容涵盖应用架构演进、AI agent框架发展趋势及Spring AI Alibaba的重磅发布。分享介绍了AI原生架构与传统架构的融合,强调了API优先、事件驱动和AI运维的重要性。同时,详细解析了Spring AI Alibaba的三层抽象设计,包括模型支持、工作流智能体编排及生产可用性构建能力,确保安全合规、高效部署与可观测性。最后,结合实际案例展示了如何利用私域数据优化AI应用,提升业务价值。
789 4
|
9月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
320 7
|
10月前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
532 5