在微服務架構中,系統被拆分成很多個服務單元,各個服務單元的應用通過 HTTP 相互呼叫、依賴。
在某個服務由於網路或其他原因自身出現故障、延遲時,呼叫方也會出現延遲。若呼叫方請求不斷增加,可能會形成任務積壓,最終導致呼叫方服務癱瘓,服務不可用現象逐漸放大。
Spring Cloud Hystrix
針對上述問題,Spring Cloud Hystrix 實現了一系列服務保護措施,從而實現服務降級、服務熔斷等功能,對延遲和故障提供強大的容錯能力。
Hystrix 有以下主要特性:
- 服務熔斷
Hystrix 會記錄各個服務的請求資訊,通過
成功
、失敗
、拒絕
、超時
等統計資訊判斷是否開啟斷路器,將某個服務的請求進行熔斷。一段時間後切換到半開路狀態,如果後面的請求正常則關閉斷路器,否則繼續開啟斷路器。
- 服務降級
服務降級是請求失敗時的後備方法,故障時執行降級邏輯。
- 執行緒隔離
Hystrix 通過執行緒池實現資源的隔離,確保對某一服務的呼叫在出現故障時不會對其他服務造成影響。
容錯實踐
首先啟動之前提到的服務註冊中心 eureka-server、服務提供方 hello-service 和服務消費放 ribbon-consumer,然後改造 ribbon-consumer 使其具備容錯能力。
1.新增相關依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
複製程式碼
2.配置 hystrix 超時時間
spring.application.name=ribbon-consumer
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# hystrix command 請求執行超時進入降級邏輯
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
複製程式碼
3.編寫 service 完成服務呼叫
package com.ulyssesss.ribbonconsumer.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.ulyssesss.ribbonconsumer.exception.NotFallbackException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
private static final String HELLO_SERVICE = "http://hello-service/";
@HystrixCommand(fallbackMethod = "helloFallback", ignoreExceptions = {NotFallbackException.class}
, groupKey = "hello", commandKey = "str", threadPoolKey = "helloStr")
public String hello(String p1, String p2) {
return restTemplate.getForObject(HELLO_SERVICE + "hello", String.class, p1, p2);
}
private String helloFallback(String p1, String p2, Throwable e) {
System.out.println("class: " + e.getClass());
return "error, " + p1 + ", " + p2;
}
}
複製程式碼
其中 fallbackMethod 指定處理降級邏輯的方法,ignoreExceptions 指定不執行降級邏輯的異常,groupKey、commandKey 作為命令統計的分組及命令名稱,threadPoolKey 用於指定執行緒池的劃分。
4.Controller 通過 HelloService 完成請求
package com.ulyssesss.ribbonconsumer.web;
import com.ulyssesss.ribbonconsumer.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired
HelloService helloService;
@GetMapping("hello")
public String hello(@RequestParam String p1, @RequestParam String p2) {
System.out.println("hello");
return helloService.hello(p1, p2);
}
}
複製程式碼
5.調整 hello-service 使其便於測試
package com.ulyssesss.helloservice.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(@RequestParam String p1, @RequestParam String p2) throws Exception {
int sleepTime = new Random().nextInt(2000);
System.out.println("hello sleep " + sleepTime);
Thread.sleep(sleepTime);
return "hello, " + p1 + ", " + p2;
}
}
複製程式碼
完成上述步驟後啟動應用,訪問 http://localhost:8080/hello?p1=1&p2=2 ,正常情況下響應為 hello, 1, 2
,關閉 hello-service 或在 sleepTime 超過 1000ms 時,執行降級邏輯,返回 error, 1, 2
。
示例程式碼 歡迎 Star