微服務-服務熔斷/降級-開發實戰

學習思路

  1. 名詞解釋
  2. 代碼實踐-基於註解、基於Feign
  3. 文末有驚喜

一、名詞解釋

  1. 服務熔斷:在分佈式系統中不可避免的會出現服務之間調用異常,一個接口的異常可能導致整個鏈路異常,服務熔斷就是防止這種級聯故障的發生,是異常服務快速返回備用數據,順利完成調用
  2. 服務降級:分佈式系統中,面對突發流量,系統可能出現負荷的情況,最終導致服務不可用,這個時候我們需要將一些非核心的服務進行降級(置爲不可用),以便節省出更多資源保證核心服務正常運行

相同點:

  1. 熔斷和降級都是保證服務在異常情況下還能正常運行,防止系統整體崩潰
  2. 都會導致部分服務短時間不可用

不同點:

  1. 服務熔斷:主要用於調用外部接口異常時進行快速返回,防止異常接口成爲系統瓶頸,甚至拖垮整個系統
  2. 服務降級:主要是整個系統出現負荷時手動/或預先設置某些非核心服務不可用
  3. 熔斷是系統自動判斷開啓關閉,降級是人爲手動或設置開啓關閉

二、代碼實踐-基於註解、基於Feign

1、網關層做熔斷降級,具體實現在微服務-gateway-Filter-熔斷、限流、統一鑑權

個人建議:不建議在網關實現熔斷降級

原因

  1. 網關作爲下游業務系統的統一入口,不關心業務,也並不知道哪些是核心/非核心
  2. 服務熔斷正常來說需要針對業務去做一些備用數據或快速返回,應該針對業務制定,不應該在網關關心
  3. 網關下調用鏈上只有其中一個小服務異常,只需要熔斷這個小接口即可,如果在網關做可能熔斷了整個鏈路開通默認爲下游所有服務不可用

2、單個接口基於註解實現

   1、首先引入jar包

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

    2、在啓動類加上註解:@EnableHystrix

    3、在方法上加@HystrixCommand註解

@HystrixCommand(
			groupKey = "usernameGroup",commandKey = "usernameCommand",threadPoolKey = "usernameThread",
			fallbackMethod = "getUsernameFallback",/*ignoreExceptions = Exception.class,*/
			commandProperties = {
					@HystrixProperty(name = "execution.timeout.enabled",value = "true"),
					@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout",value = "3000")
			},
			threadPoolProperties = {
					@HystrixProperty(name = "coreSize", value = "30"),
					@HystrixProperty(name = "maxQueueSize", value = "101"),
					@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
					@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
					@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
					@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
			}
	)
	@Override
	public String getUsername(@RequestBody OrderDTO orderDTO) {
		log.info("---------------------------=={}", JSON.toJSONString(orderDTO));
		int i=1/0;
		UserDTO userDTO=new UserDTO();
		userDTO.setUserId("userid3");
		userDTO.setUsername("username3");
		String username = productService.getUsername(userDTO);
		log.info("feign2=={}====={}",this.getClass().getSimpleName(),username);
		return username;
	}

註解屬性詳細見:https://github.com/Netflix/Hystrix/wiki/Configuration#coreSize

這裏只介紹幾個常用的

  1. commandKey:配置全局唯一標識服務的名稱,比如,庫存系統有一個獲取庫存服務,那麼就可以爲這個服務起一個名字來唯一識別該服務,如果不配置,則默認是@HystrixCommand註解修飾的函數的函數名。
  2. groupKey:一個比較重要的註解,配置全局唯一標識服務分組的名稱,比如,庫存系統就是一個服務分組。通過設置分組,Hystrix會根據組來組織和統計命令的告、儀表盤等信息。Hystrix命令默認的線程劃分也是根據命令組來實現。默認情況下,Hystrix會讓相同組名的命令使用同一個線程池,所以我們需要在創建Hystrix命令時爲其指定命令組來實現默認的線程池劃分。此外,Hystrix還提供了通過設置threadPoolKey來對線程池進行設置。建議最好設置該參數,使用threadPoolKey來控制線程池組。
  3. threadPoolKey:對線程池進行設定,細粒度的配置,相當於對單個服務的線程池信息進行設置,也可多個服務設置同一個threadPoolKey構成線程組。
  4. fallbackMethod:@HystrixCommand註解修飾的函數的回調函數,@HystrixCommand修飾的函數必須和這個回調函數定義在同一個類中,因爲定義在了同一個類中,所以fackback method可以是public/private均可。
  5. commandProperties:配置該命令的一些參數,如executionIsolationStrategy配置執行隔離策略,默認是使用線程隔離,此處我們配置爲THREAD,即線程池隔離。參見:com.netflix.hystrix.HystrixCommandProperties中各個參數的定義。
  6. threadPoolProperties:線程池相關參數設置,具體可以設置哪些參數請見:com.netflix.hystrix.HystrixThreadPoolProperties
  7. ignoreExceptions:調用服務時,除了HystrixBadRequestException之外,其他@HystrixCommand修飾的函數拋出的異常均會被Hystrix認爲命令執行失敗而觸發服務降級的處理邏輯(調用fallbackMethod指定的回調函數),所以當需要在命令執行中拋出不觸發降級的異常時來使用它,通過這個參數指定,哪些異常拋出時不觸發降級(不去調用fallbackMethod),而是將異常向上拋出。
  8. observableExecutionMode:定義hystrix observable command的模式;
  9. raiseHystrixExceptions:任何不可忽略的異常都包含在HystrixRuntimeException中;
  10. defaultFallback:默認的回調函數,該函數的函數體不能有入參,返回值類型與@HystrixCommand修飾的函數體的返回值一致。如果指定了fallbackMethod,則fallbackMethod優先級更高。

設置完HystrixCommand註解後,需要編寫程序運行異常的fallbackMethod

private String getUsernameFallback(OrderDTO orderDTO,Throwable throwable){
		log.info("fallback====={}==={}",JSON.toJSONString(orderDTO),throwable.getMessage());
		UserDTO userDTO=new UserDTO();
		userDTO.setUserId("userid4");
		userDTO.setUsername("username4");
		return userDTO.getUsername();
	}

這裏可以打印異常,返回兜底數據等,當然也可以直接返回空對象,只要調用發有針對性的處理即可

整體思路:

  1. 引入jar
  2. 啓動類加註解,啓動Hystrixy
  3. 在需要熔斷的方法上配置註解
  4. 編寫熔斷方法的fallbackMethod

3、單個接口基於Feign實現

這裏默認認爲FeignClient調用環境正常

    1、在FeignClient接口添加fallbackFactory配置

@FeignClient(name = "hystrix-feign2",primary = false,fallbackFactory = HystrixClientFactory.class)
public interface UserService {

	@PostMapping("/feign2/user/get")
	UserDTO getUser(UserDTO userDTO);

}

  2、編寫HystrixClientFactory:主要處理服務異常的fallback方法

@Component
@Log4j2
public class HystrixClientFactory implements FallbackFactory<UserService> {
	@Override
	public UserService create(Throwable throwable) {
		log.error("{}{}",throwable.getMessage(),"==============");
		return (userDTO)-> {
			log.info("系統異常=={}",userDTO==null?"userDTO is null":userDTO.toString());
			UserDTO dto=new UserDTO();
			dto.setUsername("error-username");
			dto.setUserId("error-userid");
			return dto;
		};
	}
}

這裏需要注意的是要實現FallbackFactory接口,實現create方法,在方法裏返回我們要熔斷的接口即可,這裏只有一個可以直接用lambda表達式即可,多個方法可new接口實現逐個實現方法即可

   3、開啓feign.hystrix.enabled=true:這個屬性默認是false必須在配置文件顯示開啓,當然還可以設置超時熔斷

hystrix:
  command:
    hystrix-feign2:  #default全局有效,service id指定應用有效
      execution:
        timeout:
          #是否開啓超時熔斷
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 5000 #斷路器超時時間,默認1000ms

feign.hystrix.enabled: true

題外話:至於你需要什麼樣的熔斷策略可以根據系統情況自己配置

比如10秒內錯誤率達到50%等

最後給個圖自己體會

三、服務降級

降級基本都是人工控制,比如系統監控到流量過大觸發我們之前的配置,系統某些非核心功能不可用,還可以利用配置中心動態配置開關,我們手動去修改使某些接口進入快速響應

 

文內代碼地址:https://gitee.com/carpentor/spring-cloud-example

公衆號主要記錄各種源碼、面試題、微服務技術棧,幫忙關注一波,非常感謝

 

參考文章;

Hystrix的Command屬性解讀

Spring Cloud @HystrixCommand和@CacheResult註解使用,參數配置

https://github.com/Netflix/Hystrix/wiki/Configuration#coreSize

https://github.com/netflix/hystrix/wiki

簡單談談什麼是Hystrix,以及SpringCloud的各種超時時間配置效果,和簡單談談微服務優化

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