概要
本文是以SpringBoot學習8.5-feign負載均衡調用微服務爲基礎的。
hystrix:springcloud提供的微服務訪問熔斷和降級組件。
當微服務相應變慢,可能瞬間堆積很多請求,導致其他微服務或請求端的請求積壓,可能導致系統癱瘓。這時候需要將這些超時或者無法處理的請求釋放出去(熔斷、降級),避免系統癱瘓,hystrix可以解決上述問題。
開發要點:
- 依賴hystrix
- spring斷路器配置
- @EnableCircuitBreaker開啓斷路器
- @HystrixCommand設置降級方法
1.maven依賴
熔斷機制主要依賴hystrix(當然負載均衡的需要的ribbon、feign也是必須的)。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
......
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- 實現負載均衡的ribbon依賴包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 實現負載均衡的feign依賴包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 斷路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.spring配置
關注其中的斷路器配置。
server:
port: 8001 # 服務器端口
spring:
application:
name: app-server # 微服務名稱
mvc: #定義視圖解析器的規則
view:
prefix: classpath:/templates/ #文件前綴,templates是thymeleafd的默認路徑
suffix: .html #文件後綴
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7001/eureka/ # 治理客戶端服務域
ribbon:
OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false
ReadTimeout: 2000 #負載均衡超時時間,默認值5000 # 超過則會斷路
ConnectTimeout: 1000 #ribbon請求連接的超時時間,默認值2000
MaxAutoRetries: 0 #對當前實例的重試次數,默認0
MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1
# 斷路器
hystrix:
command:
default: #default全局有效,service id指定應用有效
execution:
timeout:
enabled: true #如果enabled設置爲false,則請求超時交給ribbon控制,爲true,則超時作爲熔斷根據
isolation:
thread:
timeoutInMilliseconds: 1000 #斷路器超時時間,默認1000ms # 注意:與ribbon.ReadTimeout誰小誰生效
3.開發斷路器
@EnableCircuitBreaker開啓斷路器:
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCircuitBreaker // 開啓斷路器
public class CircuitBreakerConfig {
}
@HystrixCommand斷路器設置降級方法(斷路時來處理請求的方法)、單個方法的超時時間設置。
spring配置中設定了全局超時時間。
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@Controller
@RequestMapping("/product")
@ResponseBody
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/getById/{id}")
@HystrixCommand(fallbackMethod = "errorGetById", // 斷路器-降級處理(默認超時時間1000ms,超時則調用fallbackMethod方法)
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") }) // 設置超時時間
public Product getById(@PathVariable String id) {
// 調用微服務
Product product = productService.getById(id);
return product;
}
// 斷路器-降級處理方法
public Product errorGetById(String id) {
System.out.println("主鍵查詢連接超時!");
return new Product("", "主鍵查詢連接超時!");
}
}
4.測試
微服務端代碼:
其中取2000以內的隨機數作爲線程休眠時間,製造超時現象。
@RestController
@RequestMapping("/productRestController")
public class ProductRestController {
@GetMapping("/product/{id}")
public Product getProduct(@PathVariable String id) throws InterruptedException {
Long ms = (long) (2000L*Math.random());// 隨機數,2000之內
System.out.println(ms);
Thread.sleep(ms);
return new Product(id, "產品" + id);
}
}
4.1.測試a
注意:ribbon.ReadTimeout: 2000,hystrix的timeoutInMilliseconds:1000
微服務打印的睡眠時間(我們認爲是服務端的相應時間),請求端的日誌:
爲什麼超過1000ms時請求端會打印成功日誌?
現象:熔斷了的話降級方法內的日誌輸出,但是原方法內的日誌也輸出了,說明了原方法的邏輯在微服務響應後繼續執行(這還是我的猜想),而前臺會獲得降級方法的返回結果。
因爲微服務調用的響應時間是ribbon.ReadTimeout: 2000,上述時間均未超過2000。
下面的實驗驗證上述說法。
4.1.測試b
修改ribbon.ReadTimeout: 1000,hystrix的timeoutInMilliseconds不變。
現象:熔斷了的話降級方法內的日誌輸出,原方法內的日誌不輸出了,說明了原方法的邏輯在超時的情況沒執行,前臺會獲得降級方法的返回結果。
綜上兩種測試情況,我理解爲:
- 超過ribbon.ReadTimeout響應時間,則請求端方法在沒有獲得微服務響應的情況下後續邏輯不執行了。
- 超過hystrix的timeoutInMilliseconds則熔斷且調用降級方法。原方法的邏輯不歸我管理。
github:https://github.com/zhangyangfei/spring-cloud-learn.git 中的cloud-parent工程。