SpringCloud深入學習(二)——eureka的註冊原理(心跳時間、刷新拉取時間、註冊至註冊中心等)

一、瞭解eureka如何進行負載均衡

在我們之前的配置代碼中,採取RestTemplate這個rest請求模板實現負載均衡操作,並且我們深刻體會到

   @Bean
   @LoadBalanced //開啓負載均衡
   public RestTemplate restTemplate(){
	   return new RestTemplate();
   }

中 @LoadBalanced 的作用。

@LoadBalanced 使得RestTemplate請求操作具有以下功能:
1、能夠採取eureka上的服務“別名”方式請求。
2、讓請求具有負載均衡的功能。

現在我們就想知道子服務如何實現的負載均衡,所以我們註釋@LoadBalanced 。

二、探究、模擬負載均衡

2.1、配置服務消費者

我們採取不帶安全校驗的eureka註冊中心實現驗證操作(更簡潔明瞭)。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
//@EnableDiscoveryClient
public class Consumer10001 {
   public static void main(String[] args) {
	SpringApplication.run(Consumer10001.class, args);
   }
   
   @Bean
   //@LoadBalanced //開啓負載均衡
   public RestTemplate restTemplate(){
	   return new RestTemplate();
   }
}

2.2、修改請求控制器

思路:

1、eureka採取別名的方式保存其他服務的端口地址信息,我們應該優先獲取eureka上別名對應的微服務的請求地址信息。
2、通過相應的算法,實現一個簡單的輪詢機制的操作。

所以我們變更處理操作:

import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/test")
public class TestController {
	
	private static Logger log = LoggerFactory.getLogger(TestController.class);
	
	@Autowired
	private RestTemplate restTemplate;
	
	@Autowired
	private DiscoveryClient discoveryClient;
	
	public static int requestCount = 0;
	
	@RequestMapping("/test1")
	public String getTest1(String name){
		String getUrl = getService();
		if( getUrl == null){
			return "根據指定的別名,無法獲取指定的請求地址信息";
		}
		String memberUrl = getUrl+"/product/getProduct?name="+String.valueOf(name);
		String result = restTemplate.getForObject(memberUrl, String.class);
		return "this is consumer project ,get product result = "+String.valueOf(result);
	}
	
	private String getService(){
		List<ServiceInstance> serviceInstanceLists = discoveryClient.getInstances("app-bunana-product");
		if(serviceInstanceLists == null || serviceInstanceLists.size() == 0){
			return null;
		}
		//獲取此時該“別名”對應的請求地址的個數信息
		int size = serviceInstanceLists.size();
		//獲取當前的請求下標信息
		int index = requestCount % size;
		//請求數遞增
		requestCount ++;
		//通過計算得到的下標,獲取對應需要請求的地址和端口信息
		ServiceInstance serviceInstance = serviceInstanceLists.get(index);
		log.info("獲取ServiceInstance對象,他的信息是:{}", serviceInstance.toString());
		String getUrl = serviceInstance.getUri().toString();
		log.info("獲取ServiceInstance對象,url是:{}", getUrl);
		return getUrl;
	}
}

分別啓動eureka註冊中心和各項服務,然後測試
在這裏插入圖片描述
請求操作:

http://localhost:10001/test/test1?name=66666
在這裏插入圖片描述
再次請求:
在這裏插入圖片描述

查看日誌臺輸出情況:
在這裏插入圖片描述
此處的操作,我們只是在項目啓動後,全局一個當前的請求次數,按照當前總請求次數,取餘服務總數,獲得當前需要請求的地址下表值,得到具體的請求地址操作,使用的還是

List<ServiceInstance>  serviceInstanceLists = 
	discoveryClient.getInstances("app-bunana-product");

根據別名獲取到當前動態從註冊中心拉取到的服務別名對應地址信息的集合,按照取餘後的值作爲下標取得對應下標的url地址信息。

2.3、源碼鏈接

《git-springcloud源碼鏈接》

三、@EnableEurekaClient 和 @EnableDiscoveryClient

我們知道,每個服務需要註冊至eureka註冊中心上時,需要在每個啓動類上新增註解@EnableEurekaClient,在子服務自動啓動時,會將當前設備的信息和項目的端口信息等註冊至服務中心上。

其實還有一個註解也可以實現註冊,就是我們常看見的@EnableDiscoveryClient,我們查看註解
在這裏插入圖片描述
得知這是個註解開發。
由於我是用的是Eclipse開發軟件,所以快捷鍵選擇 Ctrl + Shift + t 查看 DiscoveryClient ,
在這裏插入圖片描述
繼續深入查看 @EnableEurekaClient 和 @EnableDiscoveryClient 註解的關係:
在這裏插入圖片描述
在這裏插入圖片描述
得知:DiscoveryClient 是 EurekaClient 的子類關係。

四、探查 DiscoveryClient 的源碼實現

我們查看 @EnableDiscoveryClient 的註解時,發現其作用是開啓DiscoveryClient
經過探查(Ctrl + Shift + t),發現 DiscoveryClient 的類有兩個,一個是 netflix、一個是 Springcloud的。
在這裏插入圖片描述
其實真正的源碼是netflix的,SpringCloud只是對其又包裝了一層結構。
我們查看SpringCloud框架對其的封裝:
在這裏插入圖片描述
發現:

在springcloud中,DiscoveryClient 是一個接口形式。
分別具有的作用:
描述信息、ServiceInstance實例對象、服務信息。

作爲接口,就必須需要有接口的實例化對象,也就是其實現的子類,我們依據 Eclipse 軟件的快捷鍵 Ctrl + t,查看其子類信息:
在這裏插入圖片描述
在這裏插入圖片描述
此處也說明了爲什麼 EurekaDiscoveryClient 可以 有服務的 註冊上報作用了。

我們回到 com.netflix.discovery; 下的
在這裏插入圖片描述
在這裏插入圖片描述
查看其實現類,此處纔是netfliix 的eureka源碼部分:

我們需要知道 eureka 子服務,在啓動時做了什麼,只需要去查看其在加載時,需要執行什麼。
在這裏插入圖片描述
查看 com.netflix.discovery.DiscoveryClient 類頭信息,我們知道他是一個單例對象,作爲單例,也就是在這個類中存在實例化對象的方式(不管懶漢式還是餓漢式)。所以我們只需要看他實例化 DiscoveryClient 類時做了什麼就行了。
我們查看其源碼記錄,可以看到他的一個構造方法:
在這裏插入圖片描述
在這其中,使用了加載eureka client時的一個初始化方法,用於初始化eureka client 使用 DiscoverClient 做了哪些事
在這裏插入圖片描述
我們就分析這個方法了。

4.1、獲取註冊信息

在我們的配置文件中,針對單個服務,如果需要做到rest請求操作前,我們必須要此時的子服務能夠定時從註冊中心上拉取其他註冊服務的註冊信息。
所以配置文件中,我們一般這麼配置:

#從註冊中心上拉取別的註冊服務信息
eureka.client.fetch-registry=true

除此之外,我們也知道默認拉取服務的時間爲30秒,當然我們也可以自己配置拉取服務的時間是多少。

eureka.client.registry-fetch-interval-seconds

關於eureka的相關命令的配置,可以參考我的另外一篇博客:
spring cloud eureka 參數配置
接下來我們來分析,他是如何識別以下的幾點需求的:

1、從註冊中心拉取其他註冊子服務信息。
2、設置自動拉取其他服務的時間。

我們查看當前 com.netflix.discovery.DiscoveryClient.initScheduledTasks() 中的方法:
在這裏插入圖片描述
從源碼截圖中我們可以發現:

首先判斷我們此時的服務是否需要拉取;
如果需要拉取,接下來我們需要做什麼?
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
從中看出,默認表示是true的,也就是說eureka.client.fetch-registry 的默認配置爲 true

在這裏插入圖片描述
繼續看這個方法做了什麼。

4.1.1、我們先分析int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();是做什麼的?

在這裏插入圖片描述
在這裏插入圖片描述
從EurekaClientConfig 接口的實現類 org.springframework.cloud.netflix.eureka.EurekaClientConfigBean 中,我們可以知道,此處表示獲取 自動拉取服務的時間,數值默認爲30秒。
我們繼續看這個類之前的幾個參數配置:
在這裏插入圖片描述


在這裏插入圖片描述
再yml中進行配置時,可以參考如下形式:
在這裏插入圖片描述

[注:]支持 駝峯命名、帶"-“的命名方式、帶”."的命名方式 等。


當我們沒有設置
在這裏插入圖片描述
時,他會默認查找是否存在一個 http://localhost:8761/eureka 的註冊地址!!

4.1.2、我們再看clientConfig.getCacheRefreshExecutorExponentialBackOffBound()幹了什麼?

查看其實現子類中的源碼,我們發現,他返回了一個參數信息
在這裏插入圖片描述
然後這個參數信息默認爲 10。
在這裏插入圖片描述

緩存刷新執行器指數回退相關屬性。
在發生超時序列的情況下,它是重試延遲的最大乘數。

然後查看整個方法,我們大致知道
在這裏插入圖片描述
如果項目啓動,我們配置(或默認配置)拉取註冊服務信息爲true時,eureka子服務會從配置文件(或默認配置)中獲取各項參數信息,然後創建一個定時任務,定時拉取註冊中心的相關注冊信息

4.2、註冊至註冊中心上

第二個方法中,存在的操作很多,不急 我們慢慢來看:
在這裏插入圖片描述

4.2.1、執行前

方法執行之前,依舊會判斷當前的服務是否需要註冊至註冊中心上!
在這裏插入圖片描述
在這裏插入圖片描述

4.2.2、判斷需要執行後

獲取續約週期:
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();

再源碼中的體現,爲以下所示:

在這裏插入圖片描述
從中,我們清楚地看到,心跳間隔的默認時間爲:30秒;
就像配置文件中,可以對其進行設置一樣:
在這裏插入圖片描述
其中,再最後面有一個start(xxxx),

instanceInfoReplicator.start
(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());

我們查看其調用的這個方法對應的這個類是什麼?
在這裏插入圖片描述
從此處看,com.netflix.discovery.InstanceInfoReplicator 是一個線程,實現了 Runnable 接口,具體的操作,我們就去看其實現的 run 方法做了什麼。
在這裏插入圖片描述
再註冊執行的流程中,我們發現了其註冊的實現邏輯:
在這裏插入圖片描述
從代碼中的流程我們發現:

1、採取http rest方式進行將實例化對象信息發送給註冊中心。
在這裏插入圖片描述
2、註冊完成後,返回註冊碼信息 204.

4.2.3、關於心跳,準確來說是 子服務將心跳時間請求發送至註冊中心 (客戶端請求服務器)

爲了驗證這個問題,我們可以參考源碼:
在這裏插入圖片描述
在這裏插入圖片描述

使用了一個定時任務(定時調度)。
再run方法中,重要的是其中的 renew() 操作。
在這裏插入圖片描述

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