在集中式環境中服務的機器臺只有一臺,這樣對於服務不僅存在服務單點故障問題而且還存在流量問題。爲了解決這個問題,就引入的分佈式與集羣概念。
分佈式:一個業務分拆多個子業務,部署在不同的服務器上
集羣:同一個業務,部署在多個服務器上
1、 dubbo 服務治理
當請求來臨時,如何從多個服務器中,選擇一個有效、合適的服務器,這個集羣所需要面對一問題。所以在集羣裏面就引申出負載均衡(LoadBalance),高可用(HA),路由(Route)等概念。我們來看一下 dubbo 在進行服務調用的時候是如何處理的。
這張集羣容錯包含以下幾個角色:
Invoker
:對Provider
(服務提供者) 的一個可調用 Service 接口的抽象,Invoker
封裝了Provider
地址及Service
接口信息。Cluster
:Directory
中的多個Invoker
僞裝成一個Invoker
,對上層透明,僞裝過程包含了容錯邏輯,調用失敗後,重試另一個Directory
:代表多個Invoker
,可以把它看成List<Invoker>
,但與 List 不同的是,它的值可能是動態變化的,比如註冊中心推送變更Router
: 負責從多個Invoker
中按路由規則選出子集,比如讀寫分離,應用隔離等LoadBalance
:LoadBalance
負責從多個Invoker
中選出具體的一個用於本次調用,選的過程包含了負載均衡算法,調用失敗後,需要重選.
2、集羣容錯
下面我們來分析一下 Cluster, 也就是集羣。集羣裏面包含:集羣與容錯兩個概念。下面我們來看一下維基百科對於集羣與容錯的描述。
2.1 集羣
計算機集羣是一組鬆散或緊密連接的計算機,它們協同工作,因此在許多方面,它們可以被看作是一個單一的系統。與網格計算機不同,計算機集羣使每個節點集執行相同的任務,由軟件控制和調度。
集羣的組件通常通過快速的本地區域網絡連接到一起,每個節點(計算機用作服務器)運行自己的操作系統實例。在大多數情況下,所有的節點都使用相同的硬件1更好的源和相同的操作系統,儘管在某些設置中(例如使用開放源碼集羣應用程序資源(OSCAR)),不同的操作系統可以在每個計算機或不同的硬件上使用。
2.2 容錯
容錯是一種屬性,它使系統能夠在出現故障(或內部一個或多個故障)的情況下繼續正常運行。如果它的運行質量下降,那麼下降與失敗的嚴重程度成正比,與一個天真的設計系統相比,即使是很小的故障也會導致完全崩潰。
在高可用性或生命關鍵系統中,容錯是特別需要的。當系統的某些部分被分解時,維護功能的能力被稱爲優雅的降級
3、Cluster & Invoker
下面我們來看一下 Cluster & Invoker 的接口定義
Cluster
@SPI(FailoverCluster.NAME)
public interface Cluster {
/**
* Merge the directory invokers to a virtual invoker.
*
* @param <T>
* @param directory
* @return cluster invoker
* @throws RpcException
*/
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
Cluster 只定義了一個方法 join
,它的作用是將目錄下面的 invoker 列表合併到虛擬調用程序中。其實就是把 Provider 端暴露的調用合併到一個集羣當中。外部調用的時候不管這個服務到底有幾個提供者,Cluster 將 Directory 中的多個 Invoker 僞裝成一個 Invoker,對上層透明,僞裝過程包含了容錯邏輯,調用失敗後,重試另一個。
Invoker
public interface Invoker<T> extends Node {
/**
* get service interface.
*
* @return service interface.
*/
Class<T> getInterface();
/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke(Invocation invocation) throws RpcException;
}
Invoker 是 Dubbo 領域模型中非常重要的一個概念,很多設計思路都是向它靠攏。這就使得 Invoker 滲透在整個實現代碼裏。 下面我們用一個官方的圖來說明最重要的兩種 Invoker:服務提供 Invoker 和服務消費 Invoker:
爲了更好的解釋上面這張圖,我們結合服務消費和提供者的代碼示例來進行說明:
服務消費者代碼:
public class DemoClientAction {
private DemoService demoService;
public void setDemoService(DemoService demoService) {
this.demoService = demoService;
}
public void start() {
String hello = demoService.sayHello("world" + i);
}
}
上面代碼中的 DemoService
就是上圖中服務消費端的 proxy,用戶代碼通過這個 proxy 調用其對應的 Invoker
5,而該 Invoker
實現了真正的遠程服務調用。
服務提供者代碼:
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) throws RemoteException {
return "Hello " + name;
}
}
上面這個類會被封裝成爲一個 AbstractProxyInvoker 實例,並新生成一個 Exporter 實例。這樣當網絡通訊層收到一個請求後,會找到對應的 Exporter 實例,並調用它所對應的 AbstractProxyInvoker 實例,從而真正調用了服務提供者的代碼。Dubbo 裏還有一些其他的 Invoker 類,但上面兩種是最重要的。
4、集羣容錯
在 dubbo 源碼中, 在包 com.alibaba.dubbo.rpc.cluster.support
中,我們可以看到 Cluster 和 Invoker 是成對出現的。
下面我們通過類圖來看一下它們之間的關係:
每一個 Cluster 其實都是創建一個 Cluster 調用的實例,Cluster 把集羣調用功能委託給 AbstractClusterInvoker (抽象集羣調用)。其實最終返回給 Consumer 的 Invoker 實例是 MockClusterInvoker 對象。這個對象持有一個 RegistryDirectory 實例(服務自動發現與註冊)與一個 FailoverClusterInvoker 實例(失敗轉移,當出現失敗,重試其它服務器,通常用於讀操作,但重試會帶來更長延遲。)
集羣容錯主要包括以下幾種模式:
- Failover Cluster:失敗自動切換,當出現失敗,重試其它服務器 。通常用於讀操作,但重試會帶來更長延遲。可通過
retries="2"
來設置重試次數(不含第一次)。 - Failfast Cluster:快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。
- Failsafe Cluster:失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。
- Failback Cluster:失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知操作。
- Forking Cluster:並行調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks=“2” 來設置最大並行數。
- Broadcast Cluster:廣播調用所有提供者,逐個調用,任意一臺報錯則報錯 2。通常用於通知所有提供者更新緩存或日誌等本地資源信息。
參考地址:
1、http://en.wikipedia.org/wiki/Computer_cluster
2、http://en.wikipedia.org/wiki/Fault-tolerant_system
3、http://dubbo.apache.org/books/dubbo-user-book/demos/fault-tolerent-strategy.html
4、http://dubbo.apache.org/books/dubbo-dev-book/implementation.html