eureka源碼之eureka-client服務註冊/心跳機制

 

 

入口

 

 

public class EurekaClientAutoConfiguration {

....省略……
// 獲取當前微服務的相關的配置信息
@Bean
	@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
	public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
															 ManagementMetadataProvider managementMetadataProvider) {
		String hostname = getProperty("eureka.instance.hostname");
		boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
		String ipAddress = getProperty("eureka.instance.ip-address");
		boolean isSecurePortEnabled = Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled"));

		String serverContextPath = env.getProperty("server.context-path", "/");
		int serverPort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));

		Integer managementPort = env.getProperty("management.server.port", Integer.class);// nullable. should be wrapped into optional
		String managementContextPath = env.getProperty("management.server.context-path");// nullable. should be wrapped into optional
		Integer jmxPort = env.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable
		EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);

		instance.setNonSecurePort(serverPort);
		instance.setInstanceId(getDefaultInstanceId(env));
		instance.setPreferIpAddress(preferIpAddress);
		instance.setSecurePortEnabled(isSecurePortEnabled);
		if (StringUtils.hasText(ipAddress)) {
			instance.setIpAddress(ipAddress);
		}

		if(isSecurePortEnabled) {
			instance.setSecurePort(serverPort);
		}

		if (StringUtils.hasText(hostname)) {
			instance.setHostname(hostname);
		}
		String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path");
		String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path");

		if (StringUtils.hasText(statusPageUrlPath)) {
			instance.setStatusPageUrlPath(statusPageUrlPath);
		}
		if (StringUtils.hasText(healthCheckUrlPath)) {
			instance.setHealthCheckUrlPath(healthCheckUrlPath);
		}

		ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort,
				serverContextPath, managementContextPath, managementPort);

		if(metadata != null) {
			instance.setStatusPageUrl(metadata.getStatusPageUrl());
			instance.setHealthCheckUrl(metadata.getHealthCheckUrl());
			if(instance.isSecurePortEnabled()) {
				instance.setSecureHealthCheckUrl(metadata.getSecureHealthCheckUrl());
			}
			Map<String, String> metadataMap = instance.getMetadataMap();
			if (metadataMap.get("management.port") == null) {
				metadataMap.put("management.port", String.valueOf(metadata.getManagementPort()));
			}
		}

		setupJmxPort(instance, jmxPort);
		return instance;
	}

…… 省略……


	@Configuration
	@ConditionalOnMissingRefreshScope
	protected static class EurekaClientConfiguration {

		…… 省略……

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

    …… 省略……
}

_________________________________________________________________________________________


public class CloudEurekaClient extends DiscoveryClient {
 …… 省略……
	public CloudEurekaClient( …… 省略……) {
		super(applicationInfoManager, config, args);
		 …… 省略……
	}
 …… 省略……
}

CloudEurekaClient#CloudEurekaClient(省略) {
		super(applicationInfoManager, config, args);
		//省略
	}
_________________________________________________________________________________________


DiscoveryClient# DiscoveryClient(省略) {
        this(applicationInfoManager, config, args, new Provider<BackupRegistry>() {
            private volatile BackupRegistry backupRegistryInstance;
            //這個get是拿備用服務,eureka-client不僅僅可以配置集羣服務,還可以配置一些備用的服務,
            //如果集羣服務拿不到會去備用服務拿
            @Override
            public synchronized BackupRegistry get() {
            	//省略
            }
        }, randomizer);
    }
_________________________________________________________________________________________


DiscoveryClient最終的構造器,在DiscoveryClient代碼320行
DiscoveryClient# DiscoveryClient(){
	//如果客戶端配置了 既不要註冊到服務中心,也不要從服務中心拿數據,那麼本eureka-client其實就失去了意義,直接return
	if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
            //省略 把很多值置爲null
            //直接return
            return;  
   }
	//下面是幾行僞代碼
	//核心池大小爲2的線程池
	ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
	//心跳機制執行,其實就是定時發送心跳請求給eureka-server告訴它我還活着,不要把我剔除
	ThreadPoolExecutor heartbeatExecutor = new ThreadPoolExecutor();
	//服務發現機制執行,其實就是定時去eureka-server拿微服務實例數據
    ThreadPoolExecutor cacheRefreshExecutor = new ThreadPoolExecutor();
    
    //如果客戶端配置應該從註冊中心拿數據,並且這次fetchRegistry(即服務發現)失敗了,則會從備用服務拿
 	if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
            fetchRegistryFromBackup();
    }
	//如果配置了應該註冊到註冊中心(默認true)&&初始化的時候強制註冊(默認false),所以不做配置的情況下,不會進if
	//但沒關係,心跳的時候會註冊的,這個看到後面就懂了
    if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
       		if (!register()) {
                 throw new IllegalStateException("");
           }              
    }
    
    initScheduledTasks();
                   
}

上面這些代碼就是eureka-client初始化的代碼,到此爲止在默認配置的情況下只進行了一次服務發現,還沒有進行服務註冊

服務註冊+心跳機制

private void initScheduledTasks() {
    ……省略……
        //省略cacheRefreshTask ,因爲這個是服務發現的任務,下篇博客說
		//應該註冊到註冊中心
        if (clientConfig.shouldRegisterWithEureka()) {
        	//心跳的間隔(單位s)
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            //心跳任務
            heartbeatTask = new TimedSupervisorTask(
                    "heartbeat",
                    scheduler,
                    heartbeatExecutor,
                    renewalIntervalInSecs,
                    TimeUnit.SECONDS,
                    expBackOffBound,
                    new HeartbeatThread()
            );
            //默認30s執行一次心跳
            scheduler.schedule(heartbeatTask,renewalIntervalInSecs, TimeUnit.SECONDS);
    ……省略……
}

HeartbeatThread心跳檢測

 private class HeartbeatThread implements Runnable {
        public void run() {
            if (renew()) {
                lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
            }
        }
    }


 

boolean renew() {
		//這裏就是訪問eureka-server的renew續期接口了,在之前的博文說過,不再贅述
 		 EurekaHttpResponse<InstanceInfo>  httpResponse = 
 		 eurekaTransport.registrationClient.sendHeartBeat
 		 (instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
          //如果續期失敗(失敗原因:沒有找到要續期的微服務,該微服務已過期)
            if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
              	//續期失敗就轉爲註冊
                boolean success = register();
                return success;
            }
            return httpResponse.getStatusCode() == Status.OK.getStatusCode();
        
    }

心跳檢測請求地址:http://localhost:3000/eureka/apps/CLIENT-POWER/power-5001

eureka-service端對應的地址

@Path("/{version}/apps")
@Produces({"application/xml", "application/json"})
public class ApplicationsResource {
 
// 心跳檢測
    @Path("{appId}")
    public ApplicationResource getApplicationResource(
            @PathParam("version") String version,
            @PathParam("appId") String appId) {
        CurrentRequestVersion.set(Version.toEnum(version));
        return new ApplicationResource(appId, serverConfig, registry);
    }

}

 

 

 

總結

1、@EnableEurekaClient此註解可加可不加,有點雞肋這個
2:eureka-client初始化過程中會先服務發現,如果失敗會去備用服務拿
3:可以配置EnforceRegistrationAtInit屬性強制初始化的時候就進行服務註冊
4:初始化過程開啓定時器30s執行一次心跳續約,如果心跳續約失敗會進行服務註冊
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章