Dubbo的高階特性:服務治理篇

2023-07-12 12:02:34

王有志,一個分享硬核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上,並學習如何設定路由規則。

如果本文對你有幫助的話,還請多多點贊支援。如果文章中出現任何錯誤,還請批評指正。最後歡迎大家關注分享硬核技術的金融摸魚俠王有志,我們下次再見!