Eureka 源码解析 —— Eureka-Server 启动(二)之 EurekaBootStrap

摘要: 原创出处 http://www.iocoder.cn/Eureka/eureka-server-init-second/ 「芋道源码」欢迎转载,保留摘要,谢谢!

本文主要基于 Eureka 1.8.X 版本

  • 1. 概述
  • 2. EurekaBootStrap

  • 3. Filter
  • 1. 概述

    本文接《Eureka 源码解析 —— Eureka-Server 启动(一)之 EurekaServerConfig》,主要分享 Eureka-Server 启动的过程的第二部分 —— EurekaBootStrap

    考虑到整个初始化的过程中涉及的代码特别多,拆分成两两篇文章:

    推荐 Spring Cloud 书籍

    2. EurekaBootStrap

    com.netflix.eureka.EurekaBootStrap,Eureka-Server 启动入口

    EurekaBootStrap 实现了 javax.servlet.ServletContextListener 接口,在 Servlet 容器( 例如 Tomcat、Jetty )启动时,调用 #contextInitialized() 方法,初始化 Eureka-Server,实现代码如下:

           
           
    public class EurekaBootStrap implements ServletContextListener {
    // 省略无关变量和方法
    @Override
    public void contextInitialized(ServletContextEvent event) {
    try {
    // 初始化 Eureka-Server 配置环境
    initEurekaEnvironment();
    // 初始化 Eureka-Server 上下文
    initEurekaServerContext();
    ServletContext sc = event.getServletContext();
    sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
    } catch (Throwable e) {
    logger.error( "Cannot bootstrap eureka server :", e);
    throw new RuntimeException( "Cannot bootstrap eureka server :", e);
    }
    }
    }

    • 调用 #initEurekaEnvironment() 方法,初始化 Eureka-Server 配置环境。
    • 调用 #initEurekaServerContext() 方法,初始化 Eureka-Server 上下文。

    2.1 初始化 Eureka-Server 配置环境

           
           
    // EurekaBootStrap.java
    /
    * 部署环境 - 测服
    /
    private static final String TEST = "test";
    private static final String ARCHAIUS_DEPLOYMENT_ENVIRONMENT = "archaius.deployment.environment";
    private static final String EUREKA_ENVIRONMENT = "eureka.environment";
    /
    部署数据中心 - CLOUD
    /
    private static final String CLOUD = "cloud";
    /*
    * 部署数据中心 - 默认
    */
    private static final String DEFAULT = "default";
    private static final String ARCHAIUS_DEPLOYMENT_DATACENTER = "archaius.deployment.datacenter";
    private static final String EUREKA_DATACENTER = "eureka.datacenter";
    protected void initEurekaEnvironment() throws Exception {
    logger.info( "Setting the eureka configuration..");
    // 设置配置文件的数据中心
    String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);
    if (dataCenter == null) {
    logger.info( "Eureka data center value eureka.datacenter is not set, defaulting to default");
    ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
    } else {
    ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
    }
    // 设置配置文件的环境
    String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT);
    if (environment == null) {
    ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
    logger.info( "Eureka environment value eureka.environment is not set, defaulting to test");
    }
    }

    2.2 初始化 Eureka-Server 上下文

    EurekaBootStrap 的 #initEurekaServerContext() 方法的实现代码相对较多,已经将代码切块 + 中文注册,点击 EurekaBootStrap 链接,对照下面每个小结阅读理解。


    2.2.1 创建 Eureka-Server 配置

           
           
    EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

    2.2.2 Eureka-Server 请求和响应的数据兼容

           
           
    // For backward compatibility
    JsonXStream.getInstance().registerConverter( new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
    XmlXStream.getInstance().registerConverter( new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);

    • 目前 Eureka-Server 提供 V2 版本 API ,如上代码主要对 V1 版本 API 做兼容。可以选择跳过。

    2.2.3 创建 Eureka-Server 请求和响应编解码器

           
           
    logger.info( "Initializing the eureka client...");
    logger.info(eurekaServerConfig.getJsonCodecName());
    ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);

    2.2.4 创建 Eureka-Client

           
           
    ApplicationInfoManager applicationInfoManager;
    if (eurekaClient == null) {
    EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
    ? new CloudInstanceConfig()
    : new MyDataCenterInstanceConfig();
    applicationInfoManager = new ApplicationInfoManager(
    instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
    EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
    eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
    } else {
    applicationInfoManager = eurekaClient.getApplicationInfoManager();
    }

    • Eureka-Server 内嵌 Eureka-Client,用于和 Eureka-Server 集群里其他节点通信交互。
    • Eureka-Client 的初始化过程,在《Eureka 源码解析 —— Eureka-Client 初始化(三)之 EurekaClient》「3. DiscoveryClient」有详细解析。
    • Eureka-Client 也可以通过 EurekaBootStrap 构造方法传递,实现代码如下:

               
               
      public class EurekaBootStrap implements ServletContextListener {
      private EurekaClient eurekaClient;
      public EurekaBootStrap(EurekaClient eurekaClient) {
      this.eurekaClient = eurekaClient;
      }
      }
      • 大多数情况下用不到


    2.2.5 创建应用实例信息的注册表

           
           
    PeerAwareInstanceRegistry registry;
    if (isAws(applicationInfoManager.getInfo())) { // AWS 相关,跳过
    registry = new AwsInstanceRegistry(
    eurekaServerConfig,
    eurekaClient.getEurekaClientConfig(),
    serverCodecs,
    eurekaClient
    );
    awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager);
    awsBinder.start();
    } else {
    registry = new PeerAwareInstanceRegistryImpl(
    eurekaServerConfig,
    eurekaClient.getEurekaClientConfig(),
    serverCodecs,
    eurekaClient
    );
    }

    2.2.6 创建 Eureka-Server 集群节点集合

           
           
    PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
    registry,
    eurekaServerConfig,
    eurekaClient.getEurekaClientConfig(),
    serverCodecs,
    applicationInfoManager
    );

    2.2.7 创建 Eureka-Server 上下文

           
           
    serverContext = new DefaultEurekaServerContext(
    eurekaServerConfig,
    serverCodecs,
    registry,
    peerEurekaNodes,
    applicationInfoManager
    );

    • com.netflix.eureka.EurekaServerContext,Eureka-Server 上下文接口,提供Eureka-Server 内部各组件对象的初始化关闭获取等方法。
    • com.netflix.eureka.EurekaServerContext.DefaultEurekaServerContext,Eureka-Server 上下文实现类,实现代码如下:

               
               
      public class DefaultEurekaServerContext implements EurekaServerContext {
      /**
      * Eureka-Server 配置
      */
      private final EurekaServerConfig serverConfig;
      /**
      * Eureka-Server 请求和响应编解码器
      */
      private final ServerCodecs serverCodecs;
      /**
      * 应用实例信息的注册表
      */
      private final PeerAwareInstanceRegistry registry;
      /**
      * Eureka-Server 集群节点集合
      */
      private final PeerEurekaNodes peerEurekaNodes;
      /**
      * 应用实例信息管理器
      */
      private final ApplicationInfoManager applicationInfoManager;
      // .... 省略方法
      }

    2.2.8 初始化 EurekaServerContextHolder

           
           
    EurekaServerContextHolder.initialize(serverContext);

    • com.netflix.eureka.EurekaServerContextHolder,Eureka-Server 上下文持有者。通过它,可以很方便的获取到 Eureka-Server 上下文,实现代码如下:

               
               
      public class EurekaServerContextHolder {
      /**
      * 持有者
      */
      private static EurekaServerContextHolder holder;
      /**
      * Eureka-Server 上下文
      */
      private final EurekaServerContext serverContext;
      private EurekaServerContextHolder(EurekaServerContext serverContext) {
      this.serverContext = serverContext;
      }
      public EurekaServerContext getServerContext() {
      return this.serverContext;
      }
      /**
      * 初始化
      *
      * @param serverContext Eureka-Server 上下文
      */
      public static synchronized void initialize(EurekaServerContext serverContext) {
      holder = new EurekaServerContextHolder(serverContext);
      }
      public static EurekaServerContextHolder getInstance() {
      return holder;
      }
      }

    2.2.9 初始化 Eureka-Server 上下文

           
           
    serverContext.initialize();
    logger.info( "Initialized server context");

    • 调用 ServerContext#initialize() 方法,初始化 Eureka-Server 上下文,实现代码如下:

               
               
      // DefaultEurekaServerContext.java
      @Override
      public void initialize() throws Exception {
      logger.info( "Initializing ...");
      // 启动 Eureka-Server 集群节点集合(复制)
      peerEurekaNodes.start();
      // 初始化 应用实例信息的注册表
      registry.init(peerEurekaNodes);
      logger.info( "Initialized");
      }

    2.2.10 从其他 Eureka-Server 拉取注册信息

           
           
    // Copy registry from neighboring eureka node
    int registryCount = registry.syncUp();
    registry.openForTraffic(applicationInfoManager, registryCount);

    2.2.11 注册监控

           
           
    // Register all monitoring statistics.
    EurekaMonitors.registerAllStats();

    3. Filter

    Eureka-Server 过滤器( javax.servlet.Filter ) 顺序如下:

    • StatusFilter
    • ServerRequestAuthFilter
    • RateLimitingFilter
    • GzipEncodingEnforcingFilter
    • ServletContainer

    3.1 StatusFilter

    com.netflix.eureka.StatusFilter,Eureka-Server 状态过滤器。当 Eureka-Server 未处于开启( InstanceStatus.UP )状态,返回 HTTP 状态码 307 重定向,实现代码如下:


           
           
    // StatusFilter.java
    private static final int SC_TEMPORARY_REDIRECT = 307;
    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
    InstanceInfo myInfo = ApplicationInfoManager.getInstance().getInfo();
    InstanceStatus status = myInfo.getStatus();
    if (status != InstanceStatus.UP && response instanceof HttpServletResponse) {
    HttpServletResponse httpRespone = (HttpServletResponse) response;
    httpRespone.sendError(SC_TEMPORARY_REDIRECT,
    "Current node is currently not ready to serve requests -- current status: "
    + status + " - try another DS node: ");
    }
    chain.doFilter(request, response);
    }

    3.2 ServerRequestAuthFilter

    com.netflix.eureka.ServerRequestAuthFilter,Eureka-Server 请求认证过滤器。Eureka-Server 未实现认证。目前打印访问的客户端名和版本号,配合 Netflix Servo 实现监控信息采集。实现代码如下:


    
      
      
            
            
    // ServerRequestAuthFilter.java
    protected void logAuth(ServletRequest request) {
    if (serverConfig.shouldLogIdentityHeaders()) {
    if (request instanceof HttpServletRequest) {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String clientName = getHeader(httpRequest, AbstractEurekaIdentity.AUTH_NAME_HEADER_KEY);
    String clientVersion = getHeader(httpRequest, AbstractEurekaIdentity.AUTH_VERSION_HEADER_KEY);
    DynamicCounter.increment(MonitorConfig.builder(NAME_PREFIX + clientName + "-" + clientVersion).build());
    }
    }
    }

    3.3 RateLimitingFilter

    com.netflix.eureka.RateLimitingFilter,请求限流过滤器。在《Eureka 源码解析 —— 基于令牌桶算法的 RateLimiter》详细解析。


    3.4 GzipEncodingEnforcingFilter

    com.netflix.eureka.GzipEncodingEnforcingFilter,GZIP 编码过滤器。


    3.5 ServletContainer

    com.sun.jersey.spi.container.servlet.ServletContainer,Jersey MVC 请求过滤器。

    • Jersey MVC 模式如下图:

      FROM 《Jersey框架的MVC》

    • com.netflix.eureka.resources 包里,有所有的 Eureka-Server Jersey Resource ( Controller )。

    • 过滤器在 web.xml 配置如下:

               
               
      <filter>
      <filter-name>jersey </filter-name>
      <filter-class>com.sun.jersey.spi.container.servlet.ServletContainer </filter-class>
      <init-param>
      <param-name>com.sun.jersey.config.property.WebPageContentRegex </param-name>
      <param-value>/(flex|images|js|css|jsp)/.* </param-value>
      </init-param>
      <init-param>
      <param-name>com.sun.jersey.config.property.packages </param-name>
      <param-value>com.sun.jersey;com.netflix </param-value>
      </init-param>
      <!-- GZIP content encoding/decoding -->
      <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerRequestFilters </param-name>
      <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter </param-value>
      </init-param>
      <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerResponseFilters </param-name>
      <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter </param-value>
      </init-param>
      </filter>
      <filter-mapping>
      <filter-name>jersey </filter-name>
      <url-pattern>/* </url-pattern>
      </filter-mapping>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值