王有志,一個分享硬核Java技術的互金摸魚俠
加入Java人的提桶跑路群:共同富裕的Java人
上一篇中,我們已經在Spring Boot應用中整合了Dubbo,並註冊了一個服務提供方和一個服務使用方。當然,生產環境中應用往往會部署多個節點,以此來保證服務的高可用,那麼如何設定Dubbo的負載均衡策略呢?
下面我們以此為切入點,來介紹Dubbo在服務治理方面提供的高階特性的設定與使用。Dubbo預設支援6種設定來源:
JVM System Properties,JVM -D引數
System environment,JVM程序的環境變數
Externalized Configuration,外部化設定,從設定中心讀取
Application Configuration,應用的屬性設定,從Spring應用的Environment中提取"dubbo"打頭的屬性集
API//註解等程式設計介面採集的設定可以被理解成設定來源的一種,是直接面向用戶程式設計的設定採集方式
從classpath讀取組態檔 dubbo.properties
在今天的內容中,我們只會涉及到Application Configuration,XML和註解這3種常見的設定方式。
Tips:本篇為基礎內容,重點在設定和使用,不涉及任何實現原理和演演算法原理。
微服務中,負載均衡指的是將請求「合理」的分配在服務的不同節點間,以達到優化使用資源,提供高吞吐量,降低響應時間的目的。Dubbo 3.X中提供了7種負載均衡策略:
演演算法 | 特性 | 設定值 | 預設設定 | 說明 |
---|---|---|---|---|
Weighted Random LoadBalance | 加權隨機 | random | 是 | 預設演演算法,預設權重相同 |
RoundRobin LoadBalance | 加權輪詢 | roundrobin | 否 | 借鑑於 Nginx 的平滑加權輪詢演演算法,預設權重相同, |
LeastActive LoadBalance | 最少活躍優先 + 加權隨機 | leastactive | 否 | 背後是能者多勞的思想 |
Shortest-Response LoadBalance | 最短響應優先 + 加權隨機 | shortestresponse | 否 | 更加關注響應速度 |
ConsistentHash LoadBalance | 一致性雜湊 | consistenthash | 否 | 確定的入參,確定的提供者,適用於有狀態請求 |
P2C LoadBalance | Power of Two Choice | p2c | 否 | 隨機選擇兩個節點後,繼續選擇「連線數」較小的那個節點。 |
Adaptive LoadBalance | 自適應負載均衡 | adaptive | 否 | 在 P2C 演演算法基礎上,選擇二者中 load 最小的那個節點 |
Dubbo支援為服務提供方和服務使用方設定負載均衡策略,當兩者都設定了負載均衡策略,以服務使用方的負載均衡策略為準。
首先我們來看XML檔案的設定方式,通過XML檔案,可以為介面,方法設定負載均衡策略,先為服務提供方的DubboDemoService
介面設定負載均衡策略:
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" loadbalance="roundrobin"/>
接著我們為服務使用方在呼叫DubboDemoService#say
方法時設定負載均很策略:
<dubbo:reference id="DubboDemoService" interface="com.wyz.api.DubboDemoService">
<dubbo:method name="say" loadbalance="roundrobin"/>
</dubbo:reference>
接著我們使用Application檔案設定負載均衡策略,通過Application檔案,可以設定Dubbo應用的全域性負載均衡策略,內容如下:
dubbo:
provider:
loadbalance: roundrobin
consumer:
loadbalance: roundrobin
最後註解的形式設定負載均衡策略,可以為介面和方法設定負載均衡策略設定,程式碼如下:
@DubboService(loadbalance = "roundrobin")
public class DubboDemoServiceImpl implements DubboDemoService {
@Override
@DubboService(loadbalance = "leastactive")
public String say(String message) {
return "DubboProvider say : " + message;
}
}
當設定好負載均衡策略後,程式一直穩定執行。可是突然有一天,叢集中的某個節點不可用了,當請求「命中」了不可用的節點後,Dubbo會如何處理呢?
Dubbo提供了叢集容錯的能力,實現了9中針對叢集中節點故障的處理方案:
策略 | 設定值 | 預設設定 | 特性 | 應用場景 |
---|---|---|---|---|
Failover Cluster | failover | 是 | 失敗自動切換,當出現失敗,重試其它伺服器 | 通常用於讀操作,可以設定重試次數 |
Failfast Cluster | failfast | 否 | 快速失敗,失敗後立即報錯 | 通常用於非冪等寫操作 |
Failsafe Cluster | failsafe | 否 | 失敗安全,出現異常時,直接忽略 | 通常用於寫入審計紀錄檔 |
Failback Cluster | failback | 否 | 失敗自動恢復,後臺記錄失敗請求,定時重發 | 通常用於訊息通知 |
Forking Cluster | forking | 否 | 並行呼叫多個伺服器,只要一個成功即返回 | 通常用於實時性要求較高的讀操作 |
Broadcast Cluster | broadcast | 否 | 廣播呼叫所有提供者,逐個呼叫,任意一臺報錯則報錯 | 快取更新 |
Available Cluster | available | 否 | 呼叫目前可用的範例(只呼叫一個),如果當前沒有可用的範例,則丟擲異常 | 不需要負載均衡的場景 |
Mergeable Cluster | mergeable | 否 | 將叢集中的呼叫結果聚合起來返回結果,通常和group一起配合使用 | / |
ZoneAware Cluster | / | 否 | 多註冊中心訂閱的場景,註冊中心叢集間的負載均衡 | / |
XML檔案的形式依舊提供了介面及介面方法級別的叢集容錯策略設定:
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" cluster="failover" retries="2"/>
其中retries
是Failover Cluster策略的引數,設定了介面重試的次數(不含正常呼叫次數),例如,在上述設定中,正常呼叫失敗後,至多會再呼叫兩次。通過XML設定方法級別的叢集容錯策略與設定負載均衡策略一致,這裡就不過多贅述了。
Application檔案中可以設定全域性的叢集容錯策略,內容如下:
dubbo:
provider:
cluster: failover
retries: 2
最後是註解設定方式,依舊是通過@DubboService
來設定介面和介面方法級別的叢集容錯策略,程式碼如下:
@DubboService(cluster = "failover", retries = 2)
public class DubboDemoServiceImpl implements DubboDemoService {
@Override
@DubboService(cluster = "failover", retries = 2)
public String say(String message) {
return "DubboProviderXML say : " + message;
}
}
Dubbo自身提供的服務降級功能較為簡陋,只提供了mock功能,如果想要體驗功能完善的限流熔斷等功能,可以使用Sentinel,Hystrix以及Resilience4j等Dubbo支援的專業的技術元件。由於Dubbo的服務降級功能較為簡陋,這裡只通過3種XML檔案的設定方式瞭解服務降級的功能的設定即可。
設定方式一
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" mock="true"/>
這種方式的設定中,需要在相同包下有類名 + Mock
字尾的實現類,例如:DubboDemoServiceMock
。
public class DubboDemoServiceMock implements DubboDemoService {
@Override
public String say(String message) {
return "服務出錯了!";
}
}
設定方式二
通過Mock類的全限名設定:
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" mock="com.wyz.api.DubboDemoServiceMock"/>
設定方式三
使用Dubbo定義的表示式:mock="[fail|force]return|throw xxx"
。
[fail|force]
,預設值fail,表示呼叫失敗後,或不進行方法呼叫直接強制執行mock方法;
return xxx
,表示返回指定結果,需要符合介面的返回型別;
throw xxx
,表示丟擲指定異常。
我們舉幾個例子:
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" mock="return fail"/>
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" mock="force:return false"/>
<dubbo:service interface="com.wyz.api.DubboDemoService" ref="dubboDemoServiceImpl" mock="fail:throw java.lang.NullPointException"/>
允許通過分組的方式來區分同一個介面的不同實現方式。例如:為DubboXMLService介面新增兩個不同的實現DubboXMLServiceImpl和NewDubboXMLServiceImpl。
public interface DubboXMLService {
String say(String message);
}
public class DubboXMLServiceImpl implements DubboXMLService {
@Override
public String say(String message) {
return "DubboProviderXML say : " + message;
}
}
public class NewDubboXMLServiceImpl implements DubboXMLService {
@Override
public String say(String message) {
return "NewDubboProviderXML say : " + message;
}
}
首先是通過XML檔案設定服務提供方:
<bean id="dubboXMLServiceImpl" class="com.wyz.service.impl.DubboXMLServiceImpl"/>
<dubbo:service interface="com.wyz.api.DubboXMLService" ref="dubboXMLServiceImpl" group="XML-Provider"/>
<bean id="newDubboXMLServiceImpl" class="com.wyz.service.impl.NewDubboXMLServiceImpl"/>
<dubbo:service interface="com.wyz.api.DubboXMLService" ref="newDubboXMLServiceImpl" group="New-XML-Provider"/>
接著來看服務使用方的設定:
<dubbo:reference id="dubboXMLService" interface="com.wyz.api.DubboXMLService" group="XML-Provider"/>
<dubbo:reference id="newDubboXMLService" interface="com.wyz.api.DubboXMLService" group="New-XML-Provider"/>
使用的話也非常簡單,可以直接通過@Autowired
方式注入不同分組的服務:
public class DubboConsumerXMLService implements CommandLineRunner {
@Autowired
DubboXMLService dubboXMLService;
@Autowired
DubboXMLService newDubboXMLService;
@Override
public void run(String... args) {
String message = dubboXMLService.say("wyz");
System.out.println(message);
String newMessage = newDubboXMLService.say("wyz");
System.out.println(newMessage);
}
}
再來看如何通過註解的方式進行設定:
@DubboService(group = "Annotation-Provider")
public class DubboAnnotationServiceImpl implements DubboAnnotationService {
@Override
public String say(String message) {
return "DubboProviderAnnotation say : " + message;
}
}
@DubboService(group = "New-Annotation-Provider")
public class NewDubboAnnotationServiceImpl implements DubboAnnotationService {
@Override
public String say(String message) {
return "NewDubboProviderAnnotation say : " + message;
}
}
最後是通過註解使用不同分組的服務,注意這裡要使用@DubboReference
注入Bean:
@Component
public class DubboConsumerAnnotationService implements CommandLineRunner {
@DubboReference(group = "Annotation-Provider")
DubboAnnotationService dubboAnnotationService;
@DubboReference(group = "New-Annotation-Provider")
DubboAnnotationService newDubboAnnotationService;
@Override
public void run(String... args) {
String message = dubboAnnotationService.say("wyz-Annotation");
System.out.println(message);
String newMessage = newDubboAnnotationService.say("wyz-Annotation");
System.out.println(newMessage);
}
}
Tips:不推薦通過Application檔案的方式進行設定。
Dubbo中,介面並不能唯一的確定一個服務,只有明確介面+分組+版本號才能唯一的確定一個服務,例如:
<dubbo:service interface="com.wyz.api.DubboXMLService" ref="dubboXMLServiceImpl" group="XML-Provider" version="1.0.0"/>
服務區分版本常見於版本釋出驗證的場景中,叢集中部分節點升級服務,並切換少許流量,驗證成功後,再大範圍升級節點,可以降低服務升級帶來的風險。
服務版本的設定與使用方式與服務分組一樣,這裡只展示XML檔案的設定方式:
<bean id="dubboXMLServiceImpl" class="com.wyz.service.impl.DubboXMLServiceImpl"/>
<dubbo:service interface="com.wyz.api.DubboXMLService" ref="dubboXMLServiceImpl" version="1.0.0"/>
服務使用者的XML檔案設定:
<dubbo:reference id="dubboXMLService" interface="com.wyz.api.DubboXMLService" version="1.0.0"/>
XML檔案的設定方式可以直接使用@Autowired
注入不同版本的服務,我們這裡就不贅述了。
今天,我們一起了解並學習瞭如何設定和使用Dubbo提供的服務治理的能力。
除了上面提到的服務治理能力外,Dubbo還提供了服務路由的能力,Dubbo 2.7之前,可以通過<dubbo:route>
標籤來設定路由規則,但在Dubbo 2.7之後,官方移除了<dubbo:route>
標籤,引入了一套依賴於註冊中心和設定中心的全新路由設定機制,後面會出一篇番外篇,將註冊中心遷移到Nacos上,並學習如何設定路由規則。
如果本文對你有幫助的話,還請多多點贊支援。如果文章中出現任何錯誤,還請批評指正。最後歡迎大家關注分享硬核技術的金融摸魚俠王有志,我們下次再見!