前言
SpringBoot与SpringCloud版本对应关系:https://start.spring.io/actuator/info
SpringCloud与Spring-Cloud-Alibaba版本对应关系:https://hub.fastgit.org/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
spring官网也能看到springboot与springcloud版本对应关系:https://spring.io/projects/spring-cloud-alibaba#learn
注册中心本质:存储每个客户端的注册信息,EurekaClient从EurekaServer同步获取服务注册列表,通过一定的规则选择一个服务进行调用。Eureka架构图如下:
-
服务提供者:是一个Eureka client,向 Eureka Server注册和更新自己的信息,同时能从Eureka Server注册表中获取到其他服务的信息。
-
服务注册中心:提供服务注册和发现的功能。每个Eureka Client向Eureka Server注册自己的信息,也可以通过Eureka Server获取到其他服务的信息达到发现和调用其他服务的目的。
-
服务消费者:是一个Eureka client,通过Eureka Server获取注册的其他服务信息,从而找到所需要的服务发起远程调用。
-
注册:服务提供者向Eureka Server端注册自身的元数据以供服务发现。
-
续约:通过发送心跳到Server以维持和更新注册表中服务实例元数据的有效性。在一定时长内,Server没有收到Client的心跳信息,将默认下线,会把服务实例信息从注册表中删除。
-
下线:服务提供方在关闭时候主动向Server注销服务实例元数据,这时服务提供方实例数据将从Server的注册表中删除。
-
获取注册表:服务消费者Client向Server请求注册表信息,用于服务发现,从而发起远程调用。
Eureka Server源码
EnableEurekaServer注解作用
- 通过 @EnableEurekaServer 激活EurekaServer
此类有一个重要作用:导入EurekaServerMarkerConfiguration配置类实例化了一个Marker的bean对象,此对象是实例化核心配置类的前提条件
//激活 EurekaServerAutoConfiguration 的开关 @Configuration(proxyBeanMethods = false) public class EurekaServerMarkerConfiguration { @Bean public Marker eurekaServerMarkerBean() { return new Marker(); } class Marker { } }自动装载核心配置类
SpringCloud对EurekaServer的封装使得发布一个EurekaServer无比简单,根据自动装载原则可以在spring-cloud-netflix-eureka-server-2.2.5.RELEASE.jar下的META-INF目录下找到 spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfigurationEurekaServerAutoConfiguration
@Configuration(proxyBeanMethods = false) @Import(EurekaServerInitializerConfiguration.class) @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) @EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) @PropertySource("classpath:/eureka/server.properties") public class EurekaServerAutoConfiguration implements WebMvcConfigurer { /** * List of packages containing Jersey resources required by the Eureka server. */ private static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery", "com.netflix.eureka" }; @Autowired private ApplicationInfoManager applicationInfoManager; @Autowired private EurekaServerConfig eurekaServerConfig; @Autowired private EurekaClientConfig eurekaClientConfig; @Autowired private EurekaClient eurekaClient; @Autowired private InstanceRegistryProperties instanceRegistryProperties; }现在我们展开来说这个Eureka服务端的自动配置类;
-
1、这个配置类实例化的前提条件是上下文中存在EurekaServerMarkerConfiguration.Marker 这个bean,解释了上面的问题。
-
2、通过@EnableConfigurationProperties({ EurekaDashboardProperties.class,InstanceRegistryProperties.class })导入了两个配置类:
- 1、EurekaDashboardProperties:配置 EurekaServer的管控台
- 2、InstanceRegistryProperties:配置期望续约数量和默认的通信数量
- 3、通过@Import({EurekaServerInitializerConfiguration.class})引入启动配置类
EurekaServerAutoConfiguration作为自动配置类,我们看看它主要配置了哪些东西。
EurekaServerConfig
- 初始化EurekaServer相关配置
EurekaController
- 服务治理少不了需要一个DashBoard来可视化监控,EurekaController基于springmvc提供DashBoard相关的功能。
PeerAwareInstanceRegistry
- 初始化集群注册,发现注册作为主要的核心功能。
PeerEurekaNodes
- 初始化集群节点集合
EurekaServerContext
- 基于eureka server配置注册表,集群节点集合以及服务实例初始化eureka server上下文。
EurekaServerBootstrap
- 初始化Spring Cloud包装的Eureka原生启动类
FilterRegistrationBean
- 初始化jerseyFilter
Jersey提供rpc调用
- Jersey是一个restful风格的基于http的rpc调用框架,eureka使用它来为客户端提供远程服务。
jerseyApplication 方法,在容器中存放了一个jerseyApplication对象,jerseyApplication()方法里的东西和Spring源码里扫描@Component逻辑类似,扫描@Path和@Provider标签,然后封装成beandefinition,封装到Application的set容器里。通过filter过滤器来过滤url进行映射到对象的Controller。
暴露的服务端接口
由于集成了Jersey,我们可以找到在EurekaServer的依赖包中的 eureka-core-1.9.8.jar ,可以看到一些列的XXXResource 这些类都是通过Jersey发布的供客户端调用的服务接口。
服务端接受客户端的注册 在ApplicationResource.addInstance()方法中可以看到 this.registry.register(info,"true".equals(isReplication));
@Singleton public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry { @Override public void register(final InstanceInfo info, final boolean isReplication) { int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } super.register(info, leaseDuration, isReplication); replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication); } }EurekaServerInitializerConfiguration
EurekaServerInitializerConfiguration实现了SmartLifecycle接口,重写了isAutoStartup方法并返回true,则会在Spring容器的finishRefresh()方法中调用EurekaServerInitializerConfiguration的start()方法,在所以在spring容器销毁的时候,就会分别调用它的stop方法。
@Configuration(proxyBeanMethods = false) public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered { private static final Log log = LogFactory .getLog(EurekaServerInitializerConfiguration.class); @Autowired private EurekaServerConfig eurekaServerConfig; private ServletContext servletContext; @Autowired private ApplicationContext applicationContext; @Autowired private EurekaServerBootstrap eurekaServerBootstrap; private boolean running; private int order = 1; @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } @Override public void start() { // 启动一个线程 new Thread(() -> { try { //初始化EurekaServer,同时启动Eureka Server eurekaServerBootstrap.contextInitialized( EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server"); // 发布EurekaServer的注册事件 publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig())); // 设置启动的状态为true EurekaServerInitializerConfiguration.this.running = true; // 发布Eureka Start 事件,另外还有其他事件,我们可以根据需要监听这些事件,然后做一些特定的业务需求 publish(new EurekaServerStartedEvent(getEurekaServerConfig())); } catch (Exception ex) { // Help! log.error("Could not initialize Eureka servlet context", ex); } }).start(); } private EurekaServerConfig getEurekaServerConfig() { return this.eurekaServerConfig; } private void publish(ApplicationEvent event) { this.applicationContext.publishEvent(event); } @Override public void stop() { this.running = false; eurekaServerBootstrap.contextDestroyed(this.servletContext); } @Override public boolean isAutoStartup() { return true; } }eurekaServerBootstrap是spring cloud定义的类,其代码完全拷贝了Eureka启动类的实现。 这里看到在Eureka启动后,会向外发送EurekaRegistryAvailableEvent 和 EurekaServerStartedEvent 事件,我们可以根据需要订阅这两个事件,做具体的处理操作。
接着我们一下 EurekaServerBootstrap 做了哪些具体的操作,从源码中可以看到方法 contextInitialized(ServletContext context) 进行了环境和上下文的初始化工作,下面我们分别看一下:
public class EurekaServerBootstrap { public void contextInitialized(ServletContext context) { try { //初始化执行环境 initEurekaEnvironment(); //初始化上下文 initEurekaServerContext(); context.setAttribute(EurekaServerContext.class.getName(), this.serverContext); } catch (Throwable e) { log.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } } }初始化执行环境——initEurekaEnvironment()
EurekaEnvironment无非就是设置了各种配置之类的东西
public class EurekaServerBootstrap { protected void initEurekaEnvironment() throws Exception { log.info("Setting the eureka configuration.."); //设置数据中心 String dataCenter = ConfigurationManager.getConfigInstance() .getString(EUREKA_DATACENTER); if (dataCenter == null) { log.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); } //设置 Eureka 环境 String environment = ConfigurationManager.getConfigInstance() .getString(EUREKA_ENVIRONMENT); if (environment == null) { ConfigurationManager.getConfigInstance() .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST); log.info( "Eureka environment value eureka.environment is not set, defaulting to test"); } else { ConfigurationManager.getConfigInstance() .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment); } } }初始化上下文——initEurekaServerContext()
public class EurekaServerBootstrap { protected void initEurekaServerContext() throws Exception { // 设置json与xml序列化工具 JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); if (isAws(this.applicationInfoManager.getInfo())) { this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig, this.eurekaClientConfig, this.registry, this.applicationInfoManager); this.awsBinder.start(); } EurekaServerContextHolder.initialize(this.serverContext); log.info("Initialized server context"); // 从邻近的eureka节点复制注册表 int registryCount = this.registry.syncUp(); // 默认每30秒发送心跳,1分钟就是2次 // 修改eureka状态为up // 同时,这里面会开启一个定时任务,用于清理 60秒没有心跳的客户端。自动下线 this.registry.openForTraffic(this.applicationInfoManager, registryCount); // 注册所有监控统计信息 EurekaMonitors.registerAllStats(); } }至此,EurekaServer启动完毕, EurekaServerBootstrap是完全复制了原生EurekaBootstrap的代码, 因为原生的Eureka是servlet应用,但是spring cloud的应用是运行在内嵌的Tomcat等WEB服务器里面的,所以就使用EurekaServerBootstrap来做替换,最终使Eureka能够在spring boot中使用。
参考: https://www.cnblogs.com/dalianpai/p/12263438.html
https://www.cnblogs.com/liukaifeng/p/10052590.html
https://www.cnblogs.com/zooking/p/13795257.html
https://www.cnblogs.com/pjjlt/p/11019247.html