您的位置:首页 > Web前端 > BootStrap

注册中心 Eureka 源码解析 —— Eureka-Server 启动(二)之 EurekaBootStrap

2018-03-28 00:00 991 查看
本文主要基于 Eureka 1.8.X 版本1. 概述

2. EurekaBootStrap

2.1 初始化 Eureka-Server 配置环境

2.2 初始化 Eureka-Server 上下文

3. Filter

3.1 StatusFilter

3.2 ServerRequestAuthFilter

3.3 RateLimitingFilter

3.4 GzipEncodingEnforcingFilter

3.5 ServletContainer

666. 彩蛋

友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】搞基嗨皮。
友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】搞基嗨皮。
友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】搞基嗨皮。

1. 概述

本文接《Eureka 源码解析 —— Eureka-Server 启动(一)之 EurekaServerConfig》,主要分享 Eureka-Server 启动的过程的第二部分 —— EurekaBootStrap。考虑到整个初始化的过程中涉及的代码特别多,拆分成两两篇文章:ServerConfig

【本文】EurekaBootStrap

推荐 Spring Cloud 书籍:请支持正版。下载盗版,等于主动编写低级 BUG 。

程序猿DD —— 《Spring Cloud微服务实战》

周立 —— 《Spring Cloud与Docker微服务架构实战》

两书齐买,京东包邮。

推荐 Spring Cloud 视频:Java 微服务实践 - Spring Boot

Java 微服务实践 - Spring Cloud

Java 微服务实践 - Spring Boot / 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");
  }
}
设置基于 Netflix Archaius 实现的配置文件的上下文,从而读取合适的配置文件。大多数情况下,只需要设置 
EUREKA_ENVIRONMENT
 即可,不同的服务器环境( 例如,
PROD
 / 
TEST
 等) 读取不同的配置文件。实现原理,在《Eureka 源码解析 —— Eureka-Client 初始化(一)之 EurekaInstanceConfig》「2.4 PropertiesInstanceConfig」有详细解析。

感兴趣的也可以阅读:《Netflix Archaius 官方文档 —— Deployment context》。

2.2 初始化 Eureka-Server 上下文

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

2.2.1 创建 Eureka-Server 配置

EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
在 《Eureka 源码解析 —— Eureka-Server 启动(一)之 ServerConfig》「2.3 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;
}
[/code]
}
大多数情况下用不到

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
 );
}
应用实例信息的注册表类关系图如下:


本文不展开分享,在《Eureka 源码解析 —— 注册表 InstanceRegistry 类关系》详细解析。

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

PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
     registry,
     eurekaServerConfig,
     eurekaClient.getEurekaClientConfig(),
     serverCodecs,
     applicationInfoManager
);
com.netflix.eureka.cluster.PeerEurekaNodes
,Eureka-Server 集群节点集合,在《Eureka 源码解析 —— Eureka-Server 集群同步》详细解析。

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;

// .... 省略方法
[/code]
}

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;
}
[/code]
}

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);
本文不展开分享,在 《Eureka 源码解析 —— Eureka-Server 集群同步》详细解析。

2.2.11 注册监控

// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
配合 Netflix Servo 实现监控信息采集。

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 实现监控信息采集。实现代码如下:
```Java
// 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>

666. 彩蛋


知识星球啦啦啦,Eureka-Server 启动完成!准备工作已经完成,可以开始更加有趣的注册、续约、取消注册、过期等等 Eureka-Client 与 Eureka-Server 的交互。搞起!胖友,分享一波朋友圈可好!?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐