Spring Cloud Eureka源码分析(二) : Eureka Server端源码分析

前言

我们按照如下流程去看eureka server源码
在这里插入图片描述
自动配置 -> server启动 -> 初始化上下文 -> 创建服务注册表 -> 心跳监控

1.自动配置类-EurekaServerAutoConfiguration

这个类里面,主要了三件事

  • 1 加载对外提供eruka信息的的接口
  • 2 初始化集群注册表
  • 3 配置节点信息-双节点成集群
  • 4 上下文
  • 5 启动server
  • 6 配置拦截器
@Configuration
 @Import(EurekaServerInitializerConfiguration.class)
 @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
 @EnableConfigurationProperties({ EurekaDashboardProperties.class,
 InstanceRegistryProperties.class })
 @PropertySource("classpath:/eureka/server.properties")
 public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {

 
 // 加载EurekaController(下面会讲), 用来获取eurekaServer的信息
 @Bean
 @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
 public EurekaController eurekaController() {
 	return new EurekaController(this.applicationInfoManager);
 }

 //初始化集群注册表
 @Bean
 public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
	 ServerCodecs serverCodecs) {
	 this.eurekaClient.getApplications(); // force initialization
	 return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,serverCodecs, this.eurekaClient,
	 this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
	 this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
 }

 // 配置服务节点信息,这里的作用主要是为了配置Eureka的peer节点,也就是说当有收到有节点注册上来
 //的时候,需要通知给那些服务节点, (互为一个集群)
 @Bean
 @ConditionalOnMissingBean
 public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
 ServerCodecs serverCodecs) {
	 return new PeerEurekaNodes(registry, this.eurekaServerConfig,
	 this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
 }
 
 // EurekaServer的上下文
 @Bean
 public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
	 return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
	 registry, peerEurekaNodes, this.applicationInfoManager);
 }
 // 这个类的作用是spring‐cloud和原生eureka的胶水代码,通过这个类来启动EurekaSever
 // 后面这个类会在EurekaServerInitializerConfiguration被调用,进行eureka启动
 @Bean
 public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext) {
	 return new EurekaServerBootstrap(this.applicationInfoManager,
	 this.eurekaClientConfig, this.eurekaServerConfig, registry,
	 serverContext);
 }
 // 配置拦截器,通过他来实现eurekaServer对外的restFull接口
   @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;
    }

EurekaControoler 解析

EurekaControoler.class 这个类很好理解,字面意思即功能,开放出一个rest接口

定义

@Controller
@RequestMapping({"${eureka.dashboard.path:/}"})
public class EurekaController {
    @Value("${eureka.dashboard.path:/}")
    private String dashboardPath = "";
    private ApplicationInfoManager applicationInfoManager;

    public EurekaController(ApplicationInfoManager applicationInfoManager) {
        this.applicationInfoManager = applicationInfoManager;
    }
}

方法
在这里插入图片描述

观察看 这个方法

  public String status(HttpServletRequest request, Map<String, Object> model) {
        this.populateBase(request, model);
        this.populateApps(model);

        StatusInfo statusInfo;
        try {
            statusInfo = (new StatusResource()).getStatusInfo();
        } catch (Exception var5) {
            statusInfo = Builder.newBuilder().isHealthy(false).build();
        }

        model.put("statusInfo", statusInfo);
        this.populateInstanceInfo(model, statusInfo);
        this.filterReplicas(model, statusInfo);
        return "eureka/status";
    }

我们点进去 populateInstanceInfo()

    private void populateInstanceInfo(Map<String, Object> model, StatusInfo statusInfo) {
        InstanceInfo instanceInfo = statusInfo.getInstanceInfo();
        Map<String, String> instanceMap = new HashMap();
        instanceMap.put("ipAddr", instanceInfo.getIPAddr());
        instanceMap.put("status", instanceInfo.getStatus().toString());
        if (instanceInfo.getDataCenterInfo().getName() == Name.Amazon) {
            AmazonInfo info = (AmazonInfo)instanceInfo.getDataCenterInfo();
            instanceMap.put("availability-zone", info.get(MetaDataKey.availabilityZone));
            instanceMap.put("public-ipv4", info.get(MetaDataKey.publicIpv4));
            instanceMap.put("instance-id", info.get(MetaDataKey.instanceId));
            instanceMap.put("public-hostname", info.get(MetaDataKey.publicHostname));
            instanceMap.put("ami-id", info.get(MetaDataKey.amiId));
            instanceMap.put("instance-type", info.get(MetaDataKey.instanceType));
        }

        model.put("instanceInfo", instanceMap);
    }

在这个里面,可以看到instanceMap这个字段,里面看到了instance-id,instance-type就是存储的实例信息,也就是说,每次启动的时候,都会走这个方法,去把当前节点给注册进去(除了设置不注册自己)

EurekaServerInitializerConfiguration 解析

EurekaServerAutoConfiguration 这个类,他引入(import) 了这个EurekaServerInitializerConfiguration
在这里插入图片描述

我们点进去看看里面都有啥
在这里插入图片描述
怎么看就是这个start()有点特殊,我们点开方法看看

public void start() {
		// 这里启动了一个线程
        (new Thread(new Runnable() {
            public void run() {
                try {
             
				//初始化EurekaServer,同时启动Eureka Server
                EurekaServerInitializerConfiguration.this.eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);

				// 日志 不管
                EurekaServerInitializerConfiguration.log.info("Started Eureka Server");
                   
                // 发布EurekaServer的注册事件
                EurekaServerInitializerConfiguration.this.publish(new EurekaRegistryAvailableEvent(EurekaServerInitializerConfiguration.this.getEurekaServerConfig()));
                    
                // 设置启动的状态为true
                EurekaServerInitializerConfiguration.this.running = true;

				// 发送Eureka Start 事件 , 其他还有各种事件,我们可以监听这种时间,然后做一些特定的业务需求
                EurekaServerInitializerConfiguration.this.publish(new EurekaServerStartedEvent(EurekaServerInitializerConfiguration.this.getEurekaServerConfig()));
                } catch (Exception var2) {
                    EurekaServerInitializerConfiguration.log.error("Could not initialize Eureka servlet context", var2);
                }

            }
        })).start();
    }

到了这一步,各项配置和参数准备好了,就差启动了!下面看这个类

EurekaServerBootstrap 解析

依旧在 EurekaServerAutoConfiguration 中,存在这么一个方法

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

废话少说,跟进去
在这里插入图片描述

我们先看下这个方法 contextInitialize

contextInitialize() 解析

里面并没有什么东西,就是调自身的两个方法
在这里插入图片描述
先看第一个 initEurekaEnvironmen()

initEurekaEnvironmen() 解析

这个方法里面,设置了服务实例的的相关属性,字面意思是数据中心和环境,默认是test环境
在这里插入图片描述

initEurekaServerContext() 解析

这个方法很关键,是在这个方法里面,去初始化eureka server的上下文的

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


		// 初始化eureka server上下文
        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();
    }

这个方法里面,有一个syncUp()方法,这个方法有什么用呢?

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();
            Iterator var4 = apps.getRegisteredApplications().iterator();

            while(var4.hasNext()) {
                Application app = (Application)var4.next();
                // 获取服务实例列表 -> 这一步就是client往server上注册啦
                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;
    }

server端快解析完了,可是我不想写了,明天再写!~ 累…

发布了240 篇原创文章 · 获赞 66 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u014131617/article/details/103889715