文章目錄
容錯
雪崩效應
由“基礎服務故障”導致“級聯故障”的現象被稱爲雪崩效應。雪崩效應描述的是生產者不可用導致消費者不可用,並將不可用逐漸放大的過程。
如何容錯
想要防止雪崩效應,必須有一個強大的容錯機制。需實現以下兩點:
- 爲網絡請求設置超時:通常情況下,一次遠程調用對應一個線程/進程。如果響應太慢,這個線程/進程就得不到釋放。而線程/進程對應着系統資源,如果得不到釋放的線程/進程越積越多,資源就會逐漸被耗盡,最終導致服務不可用。
- 使用斷路器模式:斷路器可以實現微服務的快速失敗,如果它在一段時間內檢查到許多類似的錯誤(如超時),就會在之後的一段時間內,強迫對該微服務的調用快速失敗。斷路器也可自動診斷依賴的服務是否已經恢復正常。如果發現依賴的服務已經恢復正常,那麼就恢復請求該服務。
斷路器狀態轉換邏輯:- 正常情況下,斷路器關閉,可正常請求依賴的服務
- 當一段時間內,請求失敗率達到一定閾值,斷路器就會打開。此時不會再去請求依賴的服務
- 斷路器打開一段時間後,會自動進入“半開”狀態。此時,斷路器可允許一個請求訪問依賴的服務。若訪問成功,則關閉斷路器;否則,斷路器繼續保持打開狀態。
Hystrix
Hytrix
簡介
Hystrix是由Netflix開源的一個延遲與容錯庫,用於隔離訪問遠程系統、服務或者第三方庫,防止級聯失敗,從而提升系統的可用性與容錯性。
- 包裹請求:使用HystrixCommand包裹對依賴的調用邏輯,每個命令在獨立線程中執行。使用了設計模式中的“命令模式”。
- 跳閘機制:當某服務的錯誤率超過一定閾值時,可以自動或手動跳閘,停止請求該服務一段時間。
- 資源隔離:Hystrix爲每個依賴都維護了一個小型的線程池(或者信號量)。如果該線程池已滿,發往該依賴的請求就被立即拒絕,而不是排隊等候,從而加速失敗判定。
- 監控:Hystrix可以近乎實時地監控運行指標和配置的變化,例如成功、失敗、超時和被拒絕的請求等。
- 回退機制:當請求失敗、超時、被拒絕或當斷路器打開時,執行回退邏輯。
- 自我修復:斷路器打開一段時間後,會自動進入“半開”狀態。
Spring Cloud整合Hystrix
- 在消費者pom文件中增加Hystrix依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 消費者啓動類上增加註解
@EnableHystrix
或@EnableCircuitBreaker
,從而爲項目啓用斷路器支持:
@SpringBootApplication
@EnableCircuitBreaker
public class EurekaClientHystrixProducerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientHystrixProducerApplication.class, args);
}
}
- 在需要實現容錯功能的方法上加
@HystrixCommand
註解:
@RestController
@Log4j2
public class TestController {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "findByIdFallback")
@GetMapping("findById/{id}")
public String findById(@PathVariable Long id) {
log.info("開始調用");
//使用虛擬主機名(eureka-client-hystrix-producer)請求,需要加入ribbon依賴,並且在RestTemplate的bean上加上@LoadBalanced註解
String forObject = restTemplate.getForObject("http://eureka-client-hystrix-producer/findById/" + id, String.class);
log.info("調用成功,返回值:" + forObject);
return "{'id':"+id+",'status':'success'}";
}
/**
* findUserInfo方法的回退方法
* @param id
*/
public String findByIdFallback(Long id) {
log.info("回退方法");
return "{'id':"+id+",'status':'fail'}";
}
}
測試:先按順序正常啓動,運行調用正常。然後關閉生產者,再訪問鏈接,發現進入回退方法。
注意:
- 進入回退方法並不表示斷路器已經打開,只有錯誤率超過一定閾值時纔會打開。
- 使用
@HystrixCommand
註解中的fallbackMethod
屬性,設置了一個回退方法findByIdFallback,該方法與findById方法必須具有相同的參數與返回類型。 - 獲得造成回退的原因,只需在fallback方法上添加一個Throwable 參數即可:
public String findByIdFallback(Long id,Throwable throwable) {
log.info("回退原因:",throwable);
//TODO
}
- 當發生業務異常時,不想觸發fallback方案:
1、繼承HystrixBadRequestException類:該類是一個特殊的異常類,當異常發生時,不會觸發回退。因此,可將自定義的業務異常繼承該類,從而達到業務異常不回退的效果。
2、使用@HystrixCommand
註解的ignoreExceptions
屬性,忽略不想執行回退的異常類:
@HystrixCommand(fallbackMethod = "findByIdFallback",ignoreExceptions = {
IllegalStateException.class
})
@HystrixCommand註解
使用說明:https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#configuration
@HystrixCommand的配置可使用commandProperties和@HystrixProperty來配置。如,配置HystrixCommand執行的超時時間,單位爲毫秒:
@HystrixCommand(fallbackMethod = "findByIdFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
})
@GetMapping("findById/{id}")
public String findById(@PathVariable Long id) {
//TODO
}
配置說明
public @interface HystrixCommand {
// HystrixCommand 命令所屬的組的名稱:默認註解方法類的名稱
String groupKey() default "";
// HystrixCommand 命令的key值,默認值爲註解方法的名稱
String commandKey() default "";
// 線程池名稱,默認定義爲groupKey
String threadPoolKey() default "";
// 定義回退方法的名稱, 此方法必須和hystrix的執行方法在相同類中
String fallbackMethod() default "";
// 配置hystrix命令的參數
HystrixProperty[] commandProperties() default {};
// 配置hystrix依賴的線程池的參數
HystrixProperty[] threadPoolProperties() default {};
// 如果hystrix方法拋出的異常包括RUNTIME_EXCEPTION,則會被封裝HystrixRuntimeException異常。我們也可以通過此方法定義哪些需要忽略的異常
Class<? extends Throwable>[] ignoreExceptions() default {};
// 定義執行hystrix observable的命令的模式,類型詳細見ObservableExecutionMode
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
// 如果hystrix方法拋出的異常包括RUNTIME_EXCEPTION,則會被封裝HystrixRuntimeException異常。此方法定義需要拋出的異常
HystrixException[] raiseHystrixExceptions() default {};
// 定義回調方法:但是defaultFallback不能傳入參數,返回參數和hystrix的命令兼容
String defaultFallback() default "";
}
屬性說明
配置屬性說明:https://github.com/Netflix/Hystrix/wiki/Configuration
Command屬性主要用來控制HystrixCommand命令的行爲,它主要分下面的類別:
-
Execution:用來控制HystrixCommand.run()的執行
execution.isolation.strategy:該屬性用來設置HystrixCommand.run()執行的隔離策略。默認爲THREAD。
execution.isolation.thread.timeoutInMilliseconds:該屬性用來配置HystrixCommand執行的超時時間,單位爲毫秒。
execution.timeout.enabled:該屬性用來配置HystrixCommand.run()的執行是否啓用超時時間。默認爲true。
execution.isolation.thread.interruptOnTimeout:該屬性用來配置當HystrixCommand.run()執行超時的時候是否要它中斷。
execution.isolation.thread.interruptOnCancel:該屬性用來配置當HystrixCommand.run()執行取消時是否要它中斷。
execution.isolation.semaphore.maxConcurrentRequests:當HystrixCommand命令的隔離策略使用信號量時,該屬性用來配置信號量的大小。當最大併發請求達到該設置值時,後續的請求將被拒絕。 -
Fallback:用來控制HystrixCommand.getFallback()的執行
fallback.isolation.semaphore.maxConcurrentRequests:該屬性用來設置從調用線程中允許HystrixCommand.getFallback()方法執行的最大併發請求數。當達到最大併發請求時,後續的請求將會被拒絕並拋出異常。
fallback.enabled:該屬性用來設置服務降級策略是否啓用,默認是true。如果設置爲false,當請求失敗或者拒絕發生時,將不會調用HystrixCommand.getFallback()來執行服務降級邏輯。 -
Circuit Breaker:用來控制HystrixCircuitBreaker的行爲。
circuitBreaker.enabled:確定當服務請求命令失敗時,是否使用斷路器來跟蹤其健康指標和熔斷請求。默認爲true。
circuitBreaker.requestVolumeThreshold:用來設置在滾動時間窗中,斷路器熔斷的最小請求數。例如,默認該值爲20的時候,如果滾動時間窗(默認10秒)內僅收到19個請求,即使這19個請求都失敗了,斷路器也不會打開。
circuitBreaker.sleepWindowInMilliseconds:用來設置當斷路器打開之後的休眠時間窗。休眠時間窗結束之後,會將斷路器設置爲“半開”狀態,嘗試熔斷的請求命令,如果依然時候就將斷路器繼續設置爲“打開”狀態,如果成功,就設置爲“關閉”狀態。
circuitBreaker.errorThresholdPercentage:該屬性用來設置斷路器打開的錯誤百分比條件。默認值爲50,表示在滾動時間窗中,在請求值超過requestVolumeThreshold閾值的前提下,如果錯誤請求數百分比超過50,就把斷路器設置爲“打開”狀態,否則就設置爲“關閉”狀態。
circuitBreaker.forceOpen:該屬性默認爲false。如果該屬性設置爲true,斷路器將強制進入“打開”狀態,它會拒絕所有請求。該屬性優於forceClosed屬性。
circuitBreaker.forceClosed:該屬性默認爲false。如果該屬性設置爲true,斷路器強制進入“關閉”狀態,它會接收所有請求。如果forceOpen屬性爲true,該屬性不生效。 -
Metrics:該屬性與HystrixCommand和HystrixObservableCommand執行中捕獲的指標相關。
metrics.rollingStats.timeInMilliseconds:該屬性用來設置滾動時間窗的長度,單位爲毫秒。該時間用於斷路器判斷健康度時需要收集信息的持續時間。斷路器在收集指標信息時會根據設置的時間窗長度拆分成多個桶來累計各度量值,每個桶記錄了一段時間的採集指標。例如,當爲默認值10000毫秒時,斷路器默認將其分成10個桶,每個桶記錄1000毫秒內的指標信息。
metrics.rollingStats.numBuckets:用來設置滾動時間窗統計指標信息時劃分“桶”的數量。默認值爲10。
metrics.rollingPercentile.enabled:用來設置對命令執行延遲是否使用百分位數來跟蹤和計算。默認爲true,如果設置爲false,那麼所有的概要統計都將返回-1。
metrics.rollingPercentile.timeInMilliseconds:用來設置百分位統計的滾動窗口的持續時間,單位爲毫秒。
metrics.rollingPercentile.numBuckets:用來設置百分位統計滾動窗口中使用桶的數量。
metrics.rollingPercentile.bucketSize:用來設置每個“桶”中保留的最大執行數。
metrics.healthSnapshot.intervalInMilliseconds:用來設置採集影響斷路器狀態的健康快照的間隔等待時間。 -
Request Context:涉及HystrixCommand使用HystrixRequestContext的設置。
requestCache.enabled:用來配置是否開啓請求緩存。
requestLog.enabled:用來設置HystrixCommand的執行和事件是否打印到日誌的HystrixRequestLog中。
隔離策略
Hystrix的隔離策略有兩種,分別是線程隔離和信號量隔離:
- THREAD(線程隔離):HystrixCommand將在單獨的線程上執行,併發請求受到線程池中的線程數量的限制。Hystrix中默認並且推薦使用線程隔離,因爲這種方式有一個除網絡超時以外的額外保護層。
- SEMAPHORE(信號量隔離):HystrixCommand將在調用線程上執行,開銷相對較小,併發請求受到信號量個數的限制。一般來說,只有當調用負載非常高時(例如每個實例每秒調用數百次)才需要使用信號量隔離,因爲在這種場景下使用線程隔離開銷會比較高。信號量隔離一般僅適用於非網絡調用的隔離。
可使用
execution.isolation.strategy
屬性指定隔離策略。
如果發生找不到上下文的運行異常,可以考慮將隔離策略設置爲SEMAPHORE。
Feign使用Hystrix
- 在消費者配置文件application.yml中,增加配置feign打開hystrix支持:
feign:
hystrix:
# feign打開hystrix支持,默認爲false
enabled: true
- 編寫Feign接口:
/**
* 使用@FeignClient的fallback屬性指定回退類
*/
@FeignClient(name = "eureka-client-hystrix-producer"
,fallback = UserFeignClient.FeignClientFallback.class)
public interface UserFeignClient {
@RequestMapping("findById/{id}")
String queryUserInfo(@PathVariable("id") Long id);
/**
* 回退類需要實現UserFeignClient接口
* FeignClientFallback方法作用域可以是public,無區別
*/
@Component
class FeignClientFallback implements UserFeignClient {
@Override
public String queryUserInfo(Long id) {
return "{'id':"+id+",'status':'fail'}";
}
}
}
3.修改controller類,方法上不需要增加 @HystrixCommand
註解:
@RestController
@Log4j2
public class TestController {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("findById/{id}")
public String findById(@PathVariable Long id) {
log.info("開始調用");
String forObject = userFeignClient.queryUserInfo(id);
log.info("調用成功,返回值:" + forObject);
return forObject;
}
}
通過Fallback Factory檢查回退原因
修改 UserFeignClient 類的內容:
/**
* 使用@FeignClient的fallback屬性指定回退類
*/
@FeignClient(name = "eureka-client-hystrix-producer"
,fallbackFactory = UserFeignClient.FeignClientFallbackFactory.class)
public interface UserFeignClient {
@RequestMapping("findById/{id}")
String queryUserInfo(@PathVariable("id") Long id);
/**
* FeignClientFallbackFactory類需要實現FallbackFactory接口,重載create方法
* FeignClientFallback方法作用域可以是public,無區別
*/
@Component
@Log4j2
class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
@Override
public UserFeignClient create(Throwable throwable) {
return new UserFeignClient() {
@Override
public String queryUserInfo(Long id) {
//爲了引用啓動時不打印日誌,日誌最好放在各個fallback方法中,而不要直接放在create方法中
log.info("fallback;reason was:", throwable);
return "{'id':"+id+",'status':'fail'}";
}
};
}
}
}
爲Feign禁用Hystrix
很多場景下,不需要Feign使用斷路器包裹Feign客戶端的所有方法。
- 爲指定Feign客戶端禁用Hystrix:藉助Feign的自定義配置。
新建Feign配置類:
@Configuration
public class FeignDisableHystrixConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder builder() {
return Feign.builder();
}
}
爲想要禁用Hystrix的@FeignClient,引用該配置類即可。
@FeignClient(name = "eureka-client-hystrix-producer"
,configuration = FeignDisableHystrixConfiguration.class)
public interface UserFeignClient {
//TODO
}
- 全局禁用Hystrix,默認就是全局禁用:
feign:
hystrix:
enabled: false
監控
Hystrix監控
- 增加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.修改配置文件:
management:
endpoints:
# 啓用所有端點
enabled-by-default: true
web:
exposure:
# 暴露所有端點
include: "*"
- 訪問 http://localhost:8080/actuator/hystrix.stream 獲取Hystrix的監控信息
使用Hystrix Dashboard可視化監控
1.新建項目
2. 添加依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 在啓動類上增加
@EnableHystrixDashboard
註解
啓動項目,訪問 http://localhost:8080/hystrix ,出現如下頁面:
使用Turbine聚合監控數據
Turbine是一個聚合Hystrix監控數據的工具,它可將所有相關/actuator/hystrix.stream端點的數據聚合到一個組合的/turbine.stream中,從而讓集羣的監控更加方便。
使用
- 新建項目
- 增加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 在啓動類上增加註解
@EnableTurbine
- 編輯配置文件
server:
port: 8031
spring:
application:
name: hystrix-turbine
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
turbine:
# 監控的應用名稱,多個使用“,”隔開,如:app1,app2
app-config: eureka-client-hystrix-consumer
cluster-name-expression: "'default'"
Turbine會在Eureka Server中找到微服務,並聚合微服務的監控數據。
- 依次啓動Euraka Server、生產者、消費者、turbine和dashboard
- 調用消費者包含hystrix的方法,產生監控數據
- 打開Hystrix Dashboard首頁,在url地址欄填入 http://localhost:8031/turbine.stream