微服务笔记之Euraka(2)

Eureka Server启动过程

入口:SpringCloud充分利用了SpringBoot的自动装配的特点
  • Eureka-server的jar包,发现在MERA-INF下面有配置文件spring.factories

SpringBoot应用启动时会加载EurekaServerAutoConfiguration自动配置

EurekaServerAutoConfiguration类

先看头部信息

@Configuration
@Import(EurekaServerInitializerConfiguration.class)   //导入初始化类
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)  //加载Bean对象----> Marker
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
        InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")  // 配置类和配置信息
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
         //...  
}    

主要分析EurekaServerInitializerConfiguration、Marker、EurekaServerAutoConfiguration这三个类

1、先看Marker这个类,它是什么时候注入的?

其实是由@EnableEurekaServer注解决定的,如下:

@SpringBootApplication
@EnableEurekaServer  //在我们启动类添加该注解,声明是一个EurekaServer
public class EurekaApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

进入EnableEurekaServer注解,会发现他导入了一个EurekaServerMarkerConfiguration类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)  // 导入该类,和我们的Marker类很相像
public @interface EnableEurekaServer {

}

再进入EurekaServerMarkerConfiguration类,在该类中将Marker注入到容器中

@Configuration
public class EurekaServerMarkerConfiguration {

    @Bean   // 此处将Marker注入到容器中
    public Marker eurekaServerMarkerBean() {
        return new Marker();
    }

    class Marker {
    }
}

但是Marker这个类什么都没写,不知道导入它有什么用???

2、再看EurekaServerAutoConfiguration这个类

主要分析以下几个重要的方法

eurekaController这个方法:它注入一个对外的接口(通过这个接口我们可以访问后台界面),这个后台界面我们可以通过配置来修改它是否让它显示,在配置文件中加入eureka.dashboard.enabled = false即可

@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
    return new EurekaController(this.applicationInfoManager);
}

后台界面就是下图

peerAwareInstanceRegistry这个方法:它是对等节点感知实例注册器,在集群模式下,注册服务使用到的注册器

@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
    ServerCodecs serverCodecs) {
    this.eurekaClient.getApplications(); // force initialization
    return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,serverCodecs, this.eurekaClient,                this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
                this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}

peerEurekaNodes这个方法:它的作用是注入对等的实例节点信息,辅助封装对等节点相关的信息和操作。在EurekaServer集群中,更新集群中对等的节点,因为EurekaServer集群节点可能发生变化,更新节点在PeerEurekaNodes类中的start方法中实现

@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
    ServerCodecs serverCodecs) {
        return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,this.eurekaClientConfig,serverCodecs, this.applicationInfoManager);
}

PeerEurekaNodes类中的start方法

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

那么什么时候执行start方法呢?且往下看

eurekaServerContext这个方法:注入EurekaServer的上下文对象,返回的是一个DefaultEurekaServerContext对象,

@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
    return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,registry, peerEurekaNodes, this.applicationInfoManager);
}

DefaultEurekaServerContext类中有个Initialize方法,他会在DefaultEurekaServerContext初始化完成后执行,在它里面会执行上述的start方法

@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");
}

eurekaServerBootstrap这个方法:注入一个EurekaServerBootstrap对象,后续启动会使用该对象

@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext) {
    return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry,serverContext);
}

jerseyFilterRegistration这个方法:它会注册Jersey过滤器。Jersey它是一个rest框架,帮我们发布resetful服务接口的(类似于springmvc)。EurekaServer端和EurekaClient进行通信,Server端发送的是http服务,该服务就是由Jersey发送。

@Bean
public FilterRegistrationBean jerseyFilterRegistration(
            javax.ws.rs.core.Application eurekaJerseyApp) {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new ServletContainer(eurekaJerseyApp));
    bean.setOrder(Ordered.LOWEST_PRECEDENCE);
    bean.setUrlPatterns(Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
    return bean;
}

jerseyApplication这个方法:它会注入一个Application对象,它的作用就是作为jerseyFilterRegistration方法的参数注册Jersey过滤器

@Bean
public javax.ws.rs.core.Application jerseyApplication(Environment environment,ResourceLoader resourceLoader) {
       //...
}

 3、再看EurakaServerInitializerConfiguration这个类

它实现了SmartLifecycle这个接口,该接口的父类是Lifecycle(此接口中有一个start方法),实现该接口,可以在Spring容器的Bean创建完成之后做一些事情(如执行start方法)

@Configuration
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered {
        //...
}

下面我们看一下start方法实现(其中上面我们所讲的EurekaServerBootStrap注册的对象就是在此处使用的)

@Override
public void start() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //TODO: is this class even needed now?
// 初始化EurekaServerContext细节 eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server");
// 发布事件 publish(
new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
// 状态属性设置 EurekaServerInitializerConfiguration.
this.running = true;
// 发布事件 publish(
new EurekaServerStartedEvent(getEurekaServerConfig())); }catch (Exception ex) { // Help! log.error("Could not initialize Eureka servlet context", ex); } } }).start();
}

下面看一下contextInitialized这个方法

public void contextInitialized(ServletContext context) {
   try {
        // 初始化环境信息
        initEurekaEnvironment();
        // 初始化context细节
        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);
    }
}

看一下initEurekaServerContext这个方法

protected void initEurekaServerContext() throws Exception {
    // For backward compatibility
    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();
    }
// 此处是给非IOC容器提供获取EurekaServerContext对象的接口 EurekaServerContextHolder.initialize(
this.serverContext); log.info("Initialized server context"); // 某一个server实例启动的时候,从集群中其他的server拷贝注册信息过来(同步其他节点信息),每一个server对于其他server来说也是一个客户端
int registryCount = this.registry.syncUp();
//
更改实例状态为UP,对外提供服务
this.registry.openForTraffic(this.applicationInfoManager, registryCount); // 注册统计器 EurekaMonitors.registerAllStats(); }

先看一下syncUp这个方法

public int syncUp() {
    // Copy entire entry from neighboring DS node
    int count = 0;   // 统计节点个数
    // serverConfig.getRegistrySyncRetries() 可能由于远程server没有连接上,做重试操作
    for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) {
        if (i > 0) {
            try {
                Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs());
            } catch (InterruptedException e) {
                logger.warn("Interrupted during registry transfer..");
                break;
            }
        }
        // 获取其他server的注册表信息
        Applications apps = eurekaClient.getApplications();
        for (Application app : apps.getRegisteredApplications()) {
            for (InstanceInfo instance : app.getInstances()) {
                try {
                   if (isRegisterable(instance)) {
                       // 把从远程获取到的注册表信息注册到自己的注册表中(map)
                       register(instance, instance.getLeaseInfo().getDurationInSecs(), true);
                       count++;
                   }
                } catch (Throwable t) {
                   logger.error("During DS init copy", t);
                }
            }
        }
    }
    return count;
}

看一下register方法

回过头来在看一下openForTraffic方法

 

 

 以上就是EurekaServer启动过程分析

 

 

 

 

 

 

 

posted @ 2022-11-03 17:37  码到成功hy  阅读(70)  评论(0编辑  收藏  举报
获取

hahah

name age option