通俗易懂的 Dubbo 教程(十):集羣容錯

前言

在分佈式系統中,集羣中的一些節點出現問題並不是什麼稀奇的事情,所以我們在設計分佈式 RPC 框架的時候,應該重點考慮失敗問題。在調用失敗之後,應該如何選擇對失敗的處理策略,這是一個問題。Dubbo 爲我們提供了多種策略,每一種策略對應一種場景,以供我們選擇。

Dubbo 會爲我們提供一組可調用的服務提供者,在經過路由規則過濾,負載均衡選址之後,選中一個具體地址進行調用,若調用失敗,則會按照集羣配置的容錯策略進行容錯處理。

內置容錯策略

Dubbo默認內置了一些容錯策略,如果還不能滿足用戶需求,我們可以自定義容錯策略進行配置。Dubbo 內置了以下幾種容錯策略:

  1. Failover(失敗自動切換)
  2. Failsafe(失敗安全)
  3. Failfast(快速失敗)
  4. Failback(失敗自動恢復)
  5. Forking(並行調用)
  6. Broadcast(廣播調用)

下面我們分別對其進行介紹。

Failover(失敗自動切換)

Failover 是 Dubbo 默認的容錯策略。

其實,Failover 是高可用的一個常用概念,服務器通常擁有主備兩套機器配置,當主服務器出現故障時,會自動切換到備服務器中,從而保證了整體的高可用性。

當調用失敗時,會根據配置的重試次數,自動從其他可用地址中重新選擇一個可用的地址進行調用,直到調用成功,或者是達到重試的上限位置。

Failover 會自動對失敗進行重試,但它也帶來了一些副作用。首先,重試會增加開銷,再者,重試會增加調用的響應時間,最後,在某些情況下,重試會造成資源的浪費。

Failsafe(失敗安全)

Failsafe 在調用失敗時,會忽略此錯誤,並記錄一條日誌,同時返回一個空結果,在上游看來調用是成功的。

Failsafe 即使失敗了也不會影響整個調用流程,它的失敗不影響核心業務的正確性,通常用於旁路系統或流程中,一般用於寫入審計日誌等操作。

Failfast(快速失敗)

有一些業務場景中,其操作是非冪等的,不能重複調用。這種情況下,重試並不是一個好辦法,需要用到 Failfast,調用失敗立即報錯,讓調用方來決定下一步的操作並保證業務的冪等性。

Failback(失敗自動恢復)

在 Failback 中,如果調用失敗,則此次失敗相當於 Failsafe,將返回一個空結果,但與 Failsafe 不同的是,Failback 策略會將這次調用加入內存中的失敗列表中,對於這個列表中的失敗調用,會在另一個線程中進行異步重試,重試如果再發生失敗,則會忽略,即使重試調用成功,原來的調用方也感知不到了。因此它通常適合於對於實時性要求不高,且不需要返回值的一些異步操作。

Forking(並行調用)

Forking 在第一次調用就同時發起多個調用,只要其中一個調用成功,就認爲成功。在資源充足,且對於失敗的容忍度較低的場景下,可以採用此策略。

Broadcast(廣播調用)

在某些場景下,我們可能需要對所有服務提供者進行操作,我們可以採用廣播調用策略,會逐個調用所有提供者,只要任意有一個提供者出錯,則認爲此次調用出錯。通常用於通知所有提供者更新緩存或日誌等本地資源信息。

集成斷路器 Hystrix

在開發中,我們一般會在 Dubbo 中集成斷路器 Hystrix 做集羣容錯。

在服務提供者與服務消費者中導入 Hystrix 的依賴:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

然後在服務提供者的啓動類添加 @EnableHystrix 註解

package edu.szu.producer;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

@SpringBootApplication
@EnableDubbo
@EnableHystrix
public class ProducerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }

}

在可能出現異常的方法添加 @HystrixCommand 註解,表示該方法會經過 Hystrix 代理。

package edu.szu.producer.serviceImpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import edu.szu.api.service.NameService;
import org.springframework.stereotype.Component;

@Component
@Service
public class NameServiceImpl implements NameService {

    @Override
    @HystrixCommand
    public String updateName(String name) {
        return "遠程調用的值:" + name;
    }
}

在服務消費者的啓動類添加 @EnableHystrix 註解

package edu.szu.consumer;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

@SpringBootApplication
@EnableDubbo
@EnableHystrix
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

然後在調用方法上增加 @HystrixCommand 註解,並指定 fallbackMethod ,該屬性指定出錯之後調用的方法。

package edu.szu.consumer.serviceImpl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import edu.szu.api.service.NameService;
import edu.szu.consumer.service.ChangeService;
import org.springframework.stereotype.Component;

@Component
public class ChangeServiceImpl implements ChangeService {

    @Reference
    NameService nameService;

    @Override
    @HystrixCommand(fallbackMethod = "use")
    public String change(String name) {
        return nameService.updateName(name);
    }

    public String use(String name) {
        return "服務熔斷:" + name;
    }
}

這時我們在服務提供者中顯式拋出一個異常。

package edu.szu.producer.serviceImpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import edu.szu.api.service.NameService;
import org.springframework.stereotype.Component;

@Component
@Service
public class NameServiceImpl implements NameService {

    @Override
    @HystrixCommand
    public String updateName(String name) {
        if(Math.random() > 0.5) {
            throw new RuntimeException();
        }
        return "遠程調用的值:" + name;
    }
}

遠程調用十次,其返回值分別如下:

遠程調用的值:HelloDubbo
遠程調用的值:HelloDubbo
遠程調用的值:HelloDubbo
遠程調用的值:HelloDubbo
服務熔斷:HelloDubbo
服務熔斷:HelloDubbo
服務熔斷:HelloDubbo
服務熔斷:HelloDubbo
服務熔斷:HelloDubbo
遠程調用的值:HelloDubbo

可見 Dubbo 與斷路器 Hystrix 的集成成功。

服務降級與服務熔斷的區別

在我們的上一篇博客提到過 Dubbo 如何進行服務降級,在本博客中我們又提到了 Dubbo 如何集成 Hystrix 做服務熔斷的功能,那麼,這兩者有什麼區別呢?

我們可以將服務熔斷抽象爲保險絲,如果服務出現不可用或響應超時的情況時,爲了防止整個系統出現雪崩,暫時停止對該服務的調用。而服務降級是從整個系統的負荷情況出發和考慮的,對某些負荷會比較高的情況,爲了預防某些服務出現負荷過載或者響應慢的情況,在其內部暫時捨棄對一些非核心的接口和數據的請求,而會直接返回一個提前準備好的錯誤處理信息。這樣,雖然提供的是一個有損的服務,但卻保證了整個系統的穩定性和可用性。

總結一下,熔斷與降級都是從可用性和可靠性出發,爲了防止系統崩潰,最終都讓用戶體驗到的是某些功能暫時不可用,它們的區別在於服務熔斷一般是某個服務(下游服務)故障引起,而服務降級一般是從整體負荷考慮。

參考:集羣容錯

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