Spring Cloud Eureka-Server 源码分析一
Eureka Server的启动过程
1、首先根据SpringBoot的自动装配原则,在jar包找到/META-INF/spring.factories,查看自动装配的类。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
@Configuration
@Import({EurekaServerInitializerConfiguration.class})
@ConditionalOnBean({Marker.class})
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
从以上注解,可以看出以下三点:
I、如果要装配本类,需要一个前提@ConditionalOnBean({Marker.class}),就是必须有Marker的Bean对象;
II、当前自动配置类的处理:EurekaServerAutoConfiguration
III、导入了一个EurekaServerInitializerConfiguration 初始化配置类。
第一点分析 Marker
那么问题来了,如何初始化这个Marker Bean呢?
我们知道使用Eureka Server 需要使用注解@EnableEurekaServer,我们来看看这个注解的实现:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
注解类上方导入了一个Marker配置类,Ctrl+点击进入
@Configuration
public class EurekaServerMarkerConfiguration {
public EurekaServerMarkerConfiguration() {
}
@Bean
public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
return new EurekaServerMarkerConfiguration.Marker();
}
class Marker {
Marker() {
}
}
}
嗯,原来是jar包自己定义了一个内容部类,并且自己创建了Bean对象。
也就是说,使用EurekaServer就必须引入jar包+注解@EnableEurekaServer。
第二点分析 EurekaServerAutoConfiguration
上面满足了EurekaServerAutoConfiguration类实例化的条件,下面来看下实例化是都做了哪些工作
1、默认创建了一个仪表盘,可以在配置文件中配置关闭:
eureka.dashboard: false
@Bean
@ConditionalOnProperty(
prefix = "eureka.dashboard",
name = {"enabled"},
matchIfMissing = true
)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}
2、实例化对等节点感知注册器PeerAwareInstanceRegistry
目的是:在集群模式下,EurekaServer集群中的各个节点是平等的,没有主从之分
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
this.eurekaClient.getApplications();
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
3、实例化 对等节点相关信息PeerEurekaNodes
配置服务节点的相关操作:启动服务(配置线程池)、停止服务、服务节点对等更新
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) {
return new EurekaServerAutoConfiguration.RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}
//对等Eureka节点
@Singleton
public class PeerEurekaNodes {
public void start() {
//构建线程池
this.taskExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
thread.setDaemon(true);
return thread;
}
});
try {
this.updatePeerEurekaNodes(this.resolvePeerUrls());
Runnable peersUpdateTask = new Runnable() {
public void run() {
try {
PeerEurekaNodes.this.updatePeerEurekaNodes(PeerEurekaNodes.this.resolvePeerUrls());
} catch (Throwable var2) {
PeerEurekaNodes.logger.error("Cannot update the replica Nodes", var2);
}
}
};
this.taskExecutor.scheduleWithFixedDelay(peersUpdateTask, (long)this.serverConfig.getPeerEurekaNodesUpdateIntervalMs(), (long)this.serverConfig.getPeerEurekaNodesUpdateIntervalMs(), TimeUnit.MILLISECONDS);
} catch (Exception var3) {
throw new IllegalStateException(var3);
}
Iterator var4 = this.peerEurekaNodes.iterator();
while(var4.hasNext()) {
PeerEurekaNode node = (PeerEurekaNode)var4.next();
logger.info("Replica node URL: {}", node.getServiceUrl());
}
}
public void shutdown() {
this.taskExecutor.shutdown();
List toRemove = this.peerEurekaNodes;
this.peerEurekaNodes = Collections.emptyList();
this.peerEurekaNodeUrls = Collections.emptySet();
Iterator var2 = toRemove.iterator();
while(var2.hasNext()) {
PeerEurekaNode node = (PeerEurekaNode)var2.next();
node.shutDown();
}
}
}
4、实例化EurekaServer上下文EurekaServerContext
实例化EurekaServer上下文后,启动对等节点:peerEurekaNodes.start();
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);
}
@Singleton //EurekaServer上下文实现类
public class DefaultEurekaServerContext implements EurekaServerContext {
private static final Logger logger = LoggerFactory.getLogger(DefaultEurekaServerContext.class);
private final EurekaServerConfig serverConfig;
private final ServerCodecs serverCodecs;
private final PeerAwareInstanceRegistry registry;
private final PeerEurekaNodes peerEurekaNodes;
private final ApplicationInfoManager applicationInfoManager;
@Inject //对象注入
public DefaultEurekaServerContext(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes, ApplicationInfoManager applicationInfoManager) {
this.serverConfig = serverConfig;
this.serverCodecs = serverCodecs;
this.registry = registry;
this.peerEurekaNodes = peerEurekaNodes;
this.applicationInfoManager = applicationInfoManager;
}
@PostConstruct
public void initialize() {
logger.info("Initializing ...");
this.peerEurekaNodes.start();
try {
this.registry.init(this.peerEurekaNodes);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
logger.info("Initialized");
}
@PreDestroy
public void shutdown() {
logger.info("Shutting down ...");
this.registry.shutdown();
this.peerEurekaNodes.shutdown();
logger.info("Shut down");
}
}
@Inject
1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject;实现注入。
2、@Inject是根据**类型**进行自动装配的,如果需要按名称进行装配,则需要配合@Named;
3、@Inject可以作用在变量、setter方法、构造函数上。
5、实例化EurekaServer启动类
@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) {
return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext);
}
6、实例化Jersey过滤器
Jersey是一个rest框架,帮 我们发布restfal服务接口;作用类似于SpringMVC
@Bean
public FilterRegistrationBean jerseyFilterRegistration(Application eurekaJerseyApp) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new ServletContainer(eurekaJerseyApp));
bean.setOrder(2147483647);
bean.setUrlPatterns(Collections.singletonList("/eureka/*"));
return bean;
}
@Bean
public Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);
provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
Set> classes = new HashSet();
String[] var5 = EUREKA_PACKAGES;
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
String basePackage = var5[var7];
Set beans = provider.findCandidateComponents(basePackage);
Iterator var10 = beans.iterator();
while(var10.hasNext()) {
BeanDefinition bd = (BeanDefinition)var10.next();
Class> cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader());
classes.add(cls);
}
}
Map propsAndFeatures = new HashMap();
propsAndFeatures.put("com.sun.jersey.config.property.WebPageContentRegex", "/eureka/(fonts|images|css|js)/.*");
DefaultResourceConfig rc = new DefaultResourceConfig(classes);
rc.setPropertiesAndFeatures(propsAndFeatures);
return rc;
}
第三点分析 EurekaServerInitializerConfiguration
看看@import 的 这个类是做什么的:
@Configuration
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered {
public void start() {
(new Thread(new Runnable() {
public void run() {
try {
// 初始化上下文细节:向下看
this.eurekaServerBootstrap.contextInitialized(this.servletContext);
log.info("Started Eureka Server");
//发布事件
this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));
this.running = true; //设置当前服务正在运行
this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));
} catch (Exception var2) {
log.error("Could not initialize Eureka servlet context", var2);
}
}
})).start();
}
public void stop() {
this.running = false;
this.eurekaServerBootstrap.contextDestroyed(this.servletContext);
}
}
public class EurekaServerBootstrap {
//初始化上下文细节
public void contextInitialized(ServletContext context) {
try {
this.initEurekaEnvironment();//初始化环境信息
this.initEurekaServerContext(); //初始化上下文信息
context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
} catch (Throwable var3) {
log.error("Cannot bootstrap eureka server :", var3);
throw new RuntimeException("Cannot bootstrap eureka server :", var3);
}
}
//初始化上下文信息
protected void initEurekaServerContext() throws Exception {
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);
if (this.isAws(this.applicationInfoManager.getInfo())) {
this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig, this.eurekaClientConfig, this.registry, this.applicationInfoManager);
this.awsBinder.start();
}
//非ioc容器提供的获取serverContext对象的接口
EurekaServerContextHolder.initialize(this.serverContext);
log.info("Initialized server context");
//某一个server实例启动的时候,从集群中其他server拷贝server注册信息
//每一个server对于其他 server来说也是客户端
int registryCount = this.registry.syncUp();//向下看
//更改实例状态为UP:对外提供服务
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
//注册统计器
EurekaMonitors.registerAllStats();
}
}
介绍下syncUp方法
public int syncUp() {
int count = 0;
for(int i = 0; i < this.serverConfig.getRegistrySyncRetries() && count == 0; ++i) {
if (i > 0) {
try {
Thread.sleep(this.serverConfig.getRegistrySyncRetryWaitMs());
} catch (InterruptedException var10) {
logger.warn("Interrupted during registry transfer..");
break;
}
}
Applications apps = this.eurekaClient.getApplications();
//获取到所有的server注册信息
Iterator var4 = apps.getRegisteredApplications().iterator();
while(var4.hasNext()) {
Application app = (Application)var4.next();
Iterator var6 = app.getInstances().iterator();
while(var6.hasNext()) {
InstanceInfo instance = (InstanceInfo)var6.next();
try {
if (this.isRegisterable(instance)) {
//把远程信息获取过来注册信息到自己的注册表中
this.register(instance, instance.getLeaseInfo().getDurationInSecs(), true);
++count;
}
} catch (Throwable var9) {
logger.error("During DS init copy", var9);
}
}
}
}
return count;
}
看下register,实例注册功能
private final ConcurrentHashMap>> registry = new ConcurrentHashMap();
//注册实例到注册表
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
this.read.lock();
Map> gMap = (Map)this.registry.get(registrant.getAppName());
EurekaMonitors.REGISTER.increment(isReplication);
}
.....
}
看下openForTraffic()
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
this.expectedNumberOfClientsSendingRenews = count;
this.updateRenewsPerMinThreshold();
logger.info("Got {} instances from neighboring DS node", count);
logger.info("Renew threshold is: {}", this.numberOfRenewsPerMinThreshold);
this.startupTime = System.currentTimeMillis();
if (count > 0) {
this.peerInstancesTransferEmptyOnStartup = false;
}
Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
boolean isAws = Name.Amazon == selfName;
if (isAws && this.serverConfig.shouldPrimeAwsReplicaConnections()) {
logger.info("Priming AWS connections for all replicas..");
this.primeAwsReplicas(applicationInfoManager);
}
logger.info("Changing status to UP");
applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
super.postInit();
}
//开启定时任务,每隔60秒,进行一次服务剔除
protected void postInit() {
this.renewsLastMin.start();
if (this.evictionTaskRef.get() != null) {
((AbstractInstanceRegistry.EvictionTask)this.evictionTaskRef.get()).cancel();
}
this.evictionTaskRef.set(new AbstractInstanceRegistry.EvictionTask());
this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), this.serverConfig.getEvictionIntervalTimerInMs(), this.serverConfig.getEvictionIntervalTimerInMs());
}
1、实现ServletContextAware:自动注入Servlet上下文
2、实现SmartLifecycle,规范启动、停止动作
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}