1:使用SpringCloud框架的程序,除了Eureka服務以外,其他的服務都是Eureka Client。默認的狀況,會向Eureka註冊本身的服務實例,並從Eureka服務器週期性的獲取註冊信息。 在應用程序 引入 spring-cloud-starter-eureka 依賴包,便可引入 Eureka Clinet:spring-cloud-netflix-eureka-client。(可能不一樣的版本,依賴的包名稱不同)。以下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
2:在spring-cloud-netflix-eureka-client包的 spring.factories裏,代碼以下。在這裏裏面,會執行自動配置加載類:EurekaClientAutoConfiguration。這裏主要就是註冊Eureka客戶端須要的對象。spring
org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration, org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration, org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration, org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration= org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration org.springframework.cloud.client.discovery.EnableDiscoveryClient= org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
3:默認狀況,配置 eureka.client.enabled 的值是true。在yml文件以 eureka.client 開頭的配置,解析的對象是EurekaClientConfigBean。
其默認配置以下:
registryFetchIntervalSeconds:從erureka服務端獲取服務實例的頻率。默認狀況下。每一個30秒一次
eurekaServerReadTimeoutSeconds:從eureka服務端讀取獲取信息的超時時間,默認:8秒
eurekaServerConnectTimeoutSeconds:鏈接eureka服務器的超時時間,默認:5秒
serviceUrl:eureka服務註冊的URL:默認地址是:http://localhost:8761/eureka。這裏是個MAP,能夠配置多個地址
eurekaServerTotalConnections:鏈接到eureka的總鏈接數,默認:200個
eurekaServerTotalConnectionsPerHost:鏈接單個eureka的鏈接數,默認:50個
registerWithEureka:是否註冊到eureka,默認值:true
fetchRegistry:是否從eureka服務端獲取註冊信息,默認值:true.bootstrap
@ConfigurationProperties(EurekaClientConfigBean.PREFIX) public class EurekaClientConfigBean implements EurekaClientConfig, EurekaConstants { public static final String PREFIX = "eureka.client"; @Autowired(required = false) PropertyResolver propertyResolver; public static final String DEFAULT_URL = "http://localhost:8761" + DEFAULT_PREFIX + "/"; public static final String DEFAULT_ZONE = "defaultZone"; @Bean @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT) public EurekaClientConfigBean eurekaClientConfigBean() { EurekaClientConfigBean client = new EurekaClientConfigBean(); if ("bootstrap".equals(propertyResolver.getProperty("spring.config.name"))) { // We don't register during bootstrap by default, but there will be another // chance later. client.setRegisterWithEureka(false); } return client; }
4:註冊DiscoveryClient實例,能夠經過該實例對象,從緩存中獲取在eureka上註冊的服務信息。~~~~緩存
@Bean public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) { return new EurekaDiscoveryClient(config, client); }
5:註冊EurekaClient實例,類型是CloudEurekaClient。其父類是 DiscoveryClient。在該類的構造函數裏,會創建註冊到eureka和從eureka獲取服務服務的定時任務。服務器
@Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) { return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); }
6:在 DiscoveryClient 的構造函數裏,除了各類屬性賦值外,還調用了一個很是重要的方法initScheduledTasks()來初始化定時任務,固然,若是該應用的registerWithEureka和fetchRegistry都是false,則不不會執行該方法。app
private void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { //fetchRegistry配置的是true,則執行 // registry cache refresh timer int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); //默認30S,從eureka獲取註冊信息間隔時間 int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); //默認值是10, scheduler.schedule( //創建任務,從eureka獲取註冊信息,初始延遲時間,30S。任務類:TimedSupervisorTask new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); } if (clientConfig.shouldRegisterWithEureka()) { //registerWithEureka配置的值是true則執行。 int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); //默認30秒 logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs); // Heartbeat timer scheduler.schedule( //註冊心跳定時任務,定時向eureka發送信息,初始延遲時間30S new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); // InstanceInfo replicator instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { //註冊事件監聽器,按照上面的代碼,若是發現狀態是DOWN,會馬上註冊到eureka applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); //創建定時任務,初始延遲時間是40S } else { logger.info("Not registering with Eureka server per configuration"); } }
7:實際上上面代碼中,TimedSupervisorTask類實際執行的是類: CacheRefreshThread 和 HeartbeatThread 的run 方法。 這裏就不展開。 代碼也不難。框架
8:在自動化配置中,同時也會實例化 EurekaAutoServiceRegistration 對象。該對象監聽了Spring的事件:EmbeddedServletContainerInitializedEvent。當WEB容器初始化完成後,會發送此事件。 在該事件處理方法中,會把當前節點註冊到 eureka服務器中。ide
@EventListener(EmbeddedServletContainerInitializedEvent.class) public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { // TODO: take SSL into account when Spring Boot 1.2 is available int localPort = event.getEmbeddedServletContainer().getPort(); if (this.port.get() == 0) { log.info("Updating port to " + localPort); this.port.compareAndSet(0, localPort); start(); } } public void start() { // only set the port if the nonSecurePort is 0 and this.port != 0 if (this.port.get() != 0 && this.registration.getNonSecurePort() == 0) { this.registration.setNonSecurePort(this.port.get()); } // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below if (!this.running.get() && this.registration.getNonSecurePort() > 0) { this.serviceRegistry.register(this.registration); this.context.publishEvent( new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig())); this.running.set(true); } }