本文主要基于 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. 彩蛋
RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
您对于源码的疑问每条留言都将得到认真回复甚至不知道如何读源码也可以请教噢
新的源码解析文章实时收到通知每周更新一篇左右
认真的源码交流微信群
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 容器 ( 例如 TomcatJetty ) 启动时, 调用
#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 初始化(一) 之 EurekaInstanceConfig2.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 启动 (一) 之 ServerConfig2.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 初始化 (三) 之 EurekaClient3. 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
- );
- }
应用实例信息的注册表类关系图如下:
本文不展开分享, 在 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;
- // .... 省略方法
- }
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);
本文不展开分享, 在 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 实现监控信息采集实现代码如下:
- // 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 配置如下:
- jersey
- com.sun.jersey.spi.container.servlet.ServletContainer
- com.sun.jersey.config.property.WebPageContentRegex
- /(flex|images|js|CSS|jsp)/.*
- com.sun.jersey.config.property.packages
- com.sun.jersey;com.netflix
- com.sun.jersey.spi.container.ContainerRequestFilters
- com.sun.jersey.api.container.filter.GZIPContentEncodingFilter
- com.sun.jersey.spi.container.ContainerResponseFilters
- com.sun.jersey.api.container.filter.GZIPContentEncodingFilter
- jersey
- /*
666. 彩蛋
啦啦啦, Eureka-Server 启动完成!
准备工作已经完成, 可以开始更加有趣的注册续约取消注册过期等等 Eureka-Client 与 Eureka-Server 的交互
搞起!
胖友, 分享一波朋友圈可好!?
来源: http://www.suo.im/6woQX