編碼實現Spring Cloud微服務負載均衡呼叫(eureka、ribbon)
Spring 封裝、揉和了一批開源專案,其中以Netflix開源的為主,比如zuul、eureka、hystrix、robbin等;然後就有了現在的Spring cloud微服務架構。這也充分展現了Spring的揉合能力。
Spring cloud通過封裝使這些專案融入spring的bean管理機制中,從而方便使用。這套微服務的核心功能還是使用這些專案的。
由本篇的標題可以想到本篇就是不使用Spring的註解和配置來使用這套微服務。看看現在網上關於Spring cloud的示例,千篇一律那幾行註解和程式碼。確實方便了使用。
本篇主要從微服務的呼叫端來講解的,主要涉及如下幾點:
- 全域性配置,包括服務註冊中心配置;
- 初始化服務發現客戶端;
- 例項化負載均衡呼叫實現類;
- 例項化某個具體呼叫requset,再對根據服務均衡器選擇的服務提供者進行呼叫;
- 處理呼叫結果;
如果你使用過dubbo的泛化呼叫,會發現很相似;難怪有人專門對比使用Spring cloud和dubbo來實現微服務。
如下為編碼實現的例子,很多說明在註解中;
EurekaClientConfigBean bean = new EurekaClientConfigBean();
Map<String, String> map = new HashMap<String, String>();
//eureka服務註冊中心地址
map.put(EurekaClientConfigBean.DEFAULT_ZONE,"http://xx.xx.xxx.xx:1062/eureka/,http://xx.xx.xxx.xx:1063/eureka/,http://xx.xx.xxx.xx:1064/eureka/");
bean.setServiceUrl(map);
EurekaInstanceConfigBean instanceConfigBean = new EurekaInstanceConfigBean(new InetUtils(new InetUtilsProperties()));
instanceConfigBean. setPreferIpAddress(true);
ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(instanceConfigBean);
//以上完成全域性配置,基本使用預設配置
//例項化eureka服務消費端,查詢Eureka Server中的服務例項列表
final DiscoveryClient client = new DiscoveryClient(applicationInfoManager, bean);
Provider<EurekaClient> eurekaClientProvider = new Provider<EurekaClient>() {
@Override
public synchronized EurekaClient get() {
return client;
}
};
IClientConfig clientConfig = new DefaultClientConfigImpl();
clientConfig.loadDefaultValues();
//設定vipAddress,該值對應spring.application.name配置,指定某個應用
clientConfig.set(CommonClientConfigKey.DeploymentContextBasedVipAddresses,"ZPROVIDER");
//根據eureka client獲取服務列表,client以provide形式提供
DiscoveryEnabledNIWSServerList discoveryEnabledNIWSServerList = new DiscoveryEnabledNIWSServerList(clientConfig, eurekaClientProvider);
//例項化負載均衡器介面ILoadBalancer,這裡使用了ZoneAwareLoadBalancer,這也是spring cloud預設使用的。該實現可以避免了跨區域(Zone)訪問的情況。
//其中的引數分別為,1)某個具體應用的客戶端配置,2)負載均衡的處理規則IRule物件,負載均衡器實際進行服務例項選擇任務是委託給了IRule例項中的choose函式來實現,
//這裡使用了ZoneAvoidanceRule,3)例項化檢查服務例項是否正常服務的IPing介面物件,負載均衡器啟動一個用於定時檢查Server是否健康的任務。該任務預設的執行間隔為:10秒。
//這裡沒有做真實的ping操作,他只是檢查DiscoveryEnabledNIWSServerList定時重新整理過來的服務列表中的每個服務的狀態;4)如上,ServerList介面有兩個方法,分別為
//獲取初始化的服務例項清單和獲取更新的服務例項清單;5)ServerListFilter介面實現,用於對服務例項列表的過濾,根據規則返回過濾後的服務列表;6)ServerListUpdater服務更新器介面
//實現動態獲取更新服務列表,預設30秒執行一次
ILoadBalancer loadBalancer = new ZoneAwareLoadBalancer(clientConfig, new ZoneAvoidanceRule(), new NIWSDiscoveryPing(),
discoveryEnabledNIWSServerList,new DefaultNIWSServerListFilter(), new EurekaNotificationServerListUpdater(eurekaClientProvider));
//例項化request client,他對由負載均衡器選擇的Server進行請求,Spring cloud封裝了apache HttpClient和OkHttp兩種實現
RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient = new RibbonLoadBalancingHttpClient(clientConfig, new DefaultServerIntrospector());
ribbonLoadBalancingHttpClient.setLoadBalancer(loadBalancer);
// OkHttpLoadBalancingClient okHttpLoadBalancingClient = new OkHttpLoadBalancingClient(clientConfig, new DefaultServerIntrospector());
// okHttpLoadBalancingClient.setLoadBalancer(loadBalancer);
//例項化某個具體request的上下文,如果對應到開放平臺上,這些資訊就是開放某個具體介面時,錄入的API資訊
RibbonCommandContext context = new RibbonCommandContext("ZPROVIDER","get", "/test/weber", Boolean.FALSE, new HttpHeaders(),
new LinkedMultiValueMap(), null, new ArrayList<RibbonRequestCustomizer>(), null);
RibbonCommandContext context1 = new RibbonCommandContext("ZPROVIDER","get", "/sum?v=2&vv=3", Boolean.FALSE, new HttpHeaders(),
new LinkedMultiValueMap(), null, new ArrayList<RibbonRequestCustomizer>(), null);
BufferedReader br = null;
String result = "";
try {
//例項化request,對Service請求呼叫
RibbonApacheHttpResponse response = ribbonLoadBalancingHttpClient.executeWithLoadBalancer(new RibbonApacheHttpRequest(context1));
// OkHttpRibbonResponse response = okHttpLoadBalancingClient.executeWithLoadBalancer(new OkHttpRibbonRequest(context1));
//如果服務介面輸出json或xml,可以拿到顯示
br = new BufferedReader(new InputStreamReader(response.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
result+=line;
}
System.out.println("Result: "+result);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(null!=br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
zuul實現了閘道器框架,主要邏輯為執行pre、route、post三種類型的filter,且可以動態載入filter,使用方可以實現自己的filter。
Spring cloud封裝實現的route型別filter,預設使用了ribbon對eureka 服務發現的負載均衡client。
Spring cloud在結合了ribbon的負載均衡實現中,封裝增加了HttpClient和OkHttp兩種HTTP請求端實現。
Spring cloud中還集成了Feign,方便的使用HTTP請求呼叫遠端服務,且可以靈活的使用和自定義各種編解碼器實現入參和出差的序列化和反序列化。
做上面的程式碼示例的想法來源於現在負責的閘道器專案中一些需求想法,之前閘道器只適配了dubbo型別的rpc服務叢集,目前一些新專案開始試行spring cloud架構,所以閘道器在做對應的適配。圖例如下: