Spring Cloud Eureka-Server 源码分析一Eureka Server的启动过程

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();
}

你可能感兴趣的:(Spring Cloud Eureka-Server 源码分析一Eureka Server的启动过程)