SpringCloud學習筆記——服務容錯保護

服務容錯保護

在前面的微服務架構學習中我們已經有了自己的註冊中心集羣,服務提供者集羣,以及一個處理客戶端請求的負載均衡,但是微服務的世界裏首先有一條黃金法則:永遠不要相信第三方服務。也就是說在微服務中系統被拆分成了很多個模塊,隨着模塊的增多,系統發生故障的概率也是指數倍的增加,常見的異常如網絡超時,代碼bug等,嚴重的可能由於某個服務的癱瘓而導致多個系統響應超時,如果事先沒有有效的防範措施的話,很有可能會因此產生慢響應甚至宕機等問題,所以微服務斷路器模式應運而生。

斷路器模式

學過電路的人應該都聽說過斷路器,其實就是在電路上起到保護作用的裝置,當電路發生短路導致過載時,斷路器能夠快速熔斷電路,防止因過載導致的發熱甚至火災等更嚴重的後續問題,這個場景用在微服務中就十分貼切,當整個系統中某個服務發生了異常,很有可能導致與其關聯的服務也出現異常,那麼異常就會像病毒傳播一樣迅速影響整個系統,而如果有了熔斷機制,那麼當請求中存在異常節點時可以直接返回調用方明確的異常信息,從而降低風險

快速入門

我們仍然沿用之前搭建的微服務系統,啓動我們的註冊中心服務eureka-server,端口號1111,再啓動兩個服務單元hello-service,端口號分別爲2221和2222,再啓動一個ribbon客戶端,端口號3333,此時直接訪問我們之前寫好的接口地址http://localhost:3333/ribbon-consumer, 反覆刷新幾次,頁面上會輪流出現每次負載均衡後的端口號2221和2222,說明整個微服務系統在正常運轉,這時我們只需要關閉端口號爲2221的服務單元,當下次請求該server的請求發出後會有短暫的等待時間,然後接口拋出異常信息:

{
    "timestamp": "2020-02-12T15:18:55.004+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "I/O error on GET request for \" http://hello-service/hello\ ": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect",
    "path": "/ribbon-consumer"
}

說明此時我們有個服務已經掛了,我本地使用post進行調試,本次接口耗時2.06s,兩秒的時間也許不算長,但是如果這個服務每秒有成千上萬次的調用呢?或者當前正處於高峯期時間,一個兩秒響應的接口足以造成災難性的後果了。

所以接下來我們就要引入我們的微服務斷路器:Spring Cloud Hystrix

在ribbon-consumer項目的build.gradle文件中引入依賴,並在application類上使用註解 @EnableCircuitBreaker從而開啓熔斷功能

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'

同時我們改進一下我們的代碼,雖然只是個小demo,但是很多規則還是要遵守的,比如業務代碼不要寫在controller裏,這裏我們再新建一個HelloService,把之前的代碼挪到service類中的hello方法中,並在方法上增加@HystrixCommand註解,並標明回調方法(fallbackMethod = “helloFallback”):

@Service
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello() {
        return restTemplate.getForEntity("http://hello-service/hello", String.class).getBody();
    }

    private String fallback() {
        return "system error!";
    }
}

對應修改controller類中的方法,重新注入HelloService進行調用:

@RestController
public class ConsumerController {
    @Autowired
    HelloService helloService;

    @RequestMapping(value = "/ribbon-consumer")
    public String helloConsumer() {
        return helloService.hello();
    }
}

此時再次運行ribbon consumer項目,保持端口號爲2221的服務單元終止服務狀態,這次我們再來看下通過postman調用結果就會變成我們預設的返回值system error!,當然了,在回調函數中可以自定義友好的錯誤提示信息或者進行額外的業務處理。此外,除了服務宕機導致的不可用之外,可能由於當前系統正在承受非常大的流量請求或其他異常導致接口響應時間過長,hytrix同樣可以達到斷路的效果,hytrix默認的響應時間爲2000毫秒最長,也就是說如果接口響應時間超過了兩千毫秒,hytrix也會認爲當前服務不再可用,從而像前面模擬服務器宕機一樣調用我們預設的回調函數,這裏我們需要修改以下hello-service的代碼,在/hello接口中我們加入隨機睡眠時間來隨機性觸發響應時間過長的問題,並且在ribbon-consumer調用代碼中增加接口響應時長的日誌打印,便於我們觀察,以下是代碼修改:

int time = new Random().nextInt(5000);
Thread.sleep(time);
@Service
public class HelloService {
    private static final Logger LOGGER = LoggerFactory.getLogger(HelloService.class);

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello() {
        long start = System.currentTimeMillis();
        String res = restTemplate.getForEntity("http://hello-service/hello", String.class).getBody();
        LOGGER.info("hellor cost : {}ms", System.currentTimeMillis() - start);
        return res;
    }

    private String helloFallback() {
        return "system error!";
    }
}

重新啓動後繼續通過postman調用,通過觀察控制條輸出日誌和接口返回信息不難發現,凡是大於2000ms的請求全部調用了回調函數返回錯誤信息,其他請求則正常返回,那麼在日常使用的過程中,合理的在請求中加入hytrix可以簡單有效的防止服務宕機等因素帶來的異常問題,同時要記得記錄好日誌以及回調之後的後續補償操作。

發佈了21 篇原創文章 · 獲贊 7 · 訪問量 7365
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章