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>
發佈了22 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章