本文主要基于 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 微服务实战》 https://union-click.jd.com/jdc?d=505Twi
周立 -- 《Spring Cloud 与 Docker 微服务架构实战》 https://union-click.jd.com/jdc?d=k3sAaK
两书齐买, 京东包邮.
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 https://github.com/Netflix/archaius 实现的配置文件的上下文, 从而读取合适的配置文件. 大多数情况下, 只需要设置 EUREKA_ENVIRONMENT 即可, 不同的服务器环境 ( 例如, PROD / TEST 等) 读取不同的配置文件. 实现原理, 在《Eureka 源码解析 -- Eureka-Client 初始化(一) 之 EurekaInstanceConfig》「2.4 PropertiesInstanceConfig」有详细解析.
感兴趣的也可以阅读:《Netflix Archaius 官方文档 -- Deployment context》.
2.2 初始化 Eureka-Server 上下文
EurekaBootStrap 的 #initEurekaServerContext() 方法的实现代码相对较多, 已经将代码切块 + 中文注册, 点击 链接, 对照下面每个小结阅读理解.
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;
- }
- }
大多数情况下用不到.
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 集群同步》 http://www.iocoder.cn/Eureka/server-cluster/?self 详细解析.
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 集群同步》 http://www.iocoder.cn/Eureka/server-cluster/?self 详细解析.
2.2.11 注册监控
- // Register all monitoring statistics.
- EurekaMonitors.registerAllStats();
配合 Netflix Servo https://github.com/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 https://github.com/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》 http://www.iocoder.cn/Eureka/rate-limiter/?self 详细解析.
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 的交互.
搞起!
胖友, 分享一波朋友圈可好!?
来源: https://juejin.im/entry/5c858907f265da2dbf5f3d34