@author QYX 由於學習任務繁多,近期暫停了幾天搬運,兩天后恢復
引入服務熔斷Hystrix
簡單是來說,在分布式系統中,假如有一個請求需要調用A服務,但A服務出現了問題,則這個請求就會阻塞,那么只要調用服務A的請求都會阻塞,當阻塞的請求越來越多,占用的計算機資源就越來越多。進一步來說,就是一個服務出現問題,可能導致所有的請求都不可用,從而導致整個分布式系統都不可用,這就是“雪崩效應”。
雪崩效應常見場景
-
硬件故障:如服務器宕機,機房斷電,光纖被挖斷等。
-
流量激增:如異常流量,重試加大流量等。
-
緩存穿透:一般發生在應用重啟,所有緩存失效時,以及短時間內大量緩存失效時。大量的緩存不命中,使請求直擊后端服務,造成服務提供者超負荷運行,引起服務不可用。
-
程序BUG:如程序邏輯導致內存泄漏,JVM長時間FullGC等。
-
同步等待:服務間采用同步調用模式,同步等待造成的資源耗盡。
雪崩效應應對策略
針對造成雪崩效應的不同場景,可以使用不同的應對策略,沒有一種通用所有場景的策略,參考如下:
-
硬件故障:多機房容災、異地多活等。
-
流量激增:服務自動擴容、流量控制(限流、關閉重試)等。
-
緩存穿透:緩存預加載、緩存異步加載等。
-
程序BUG:修改程序bug、及時釋放資源等。
-
同步等待:資源隔離、MQ解耦、不可用服務調用快速失敗等。資源隔離通常指不同服務調用采用不同的線程池;不可用服務調用快速失敗一般通過熔斷器模式結合超時機制實現。
綜上所述,如果一個應用不能對來自依賴的故障進行隔離,那該應用本身就處在被拖垮的風險中。 因此,為了構建穩定、可靠的分布式系統,我們的服務應當具有自我保護能力,當依賴服務不可用時,當前服務啟動自我保護功能,從而避免發生雪崩效應。本文將重點介紹使用Hystrix解決同步等待的雪崩問題。
服務隔離
服務降級
Hystrix:
Hystrix [hɪst'rɪks],中文含義是豪豬,因其背上長滿棘刺,從而擁有了自我保護的能力。本文所說的Hystrix是Netflix開源的一款容錯框架,同樣具有自我保護能力。為了實現容錯和自我保護,下面我們看看Hystrix如何設計和實現的。
Hystrix設計目標:
-
對來自依賴的延遲和故障進行防護和控制——這些依賴通常都是通過網絡訪問的
-
阻止故障的連鎖反應
-
快速失敗並迅速恢復
-
回退並優雅降級
-
提供近實時的監控與告警
Hystrix遵循的設計原則:
-
防止任何單獨的依賴耗盡資源(線程)
-
過載立即切斷並快速失敗,防止排隊
-
盡可能提供回退以保護用戶免受故障
-
使用隔離技術(例如隔板,泳道和斷路器模式)來限制任何一個依賴的影響
-
通過近實時的指標,監控和告警,確保故障被及時發現
-
通過動態修改配置屬性,確保故障及時恢復
-
防止整個依賴客戶端執行失敗,而不僅僅是網絡通信
Hystrix如何實現這些設計目標?
-
使用命令模式將所有對外部服務(或依賴關系)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,並將該對象放在單獨的線程中執行;
-
每個依賴都維護着一個線程池(或信號量),線程池被耗盡則拒絕請求(而不是讓請求排隊)。
-
記錄請求成功,失敗,超時和線程拒絕。
-
服務錯誤百分比超過了閾值,熔斷器開關自動打開,一段時間內停止對該服務的所有請求。
-
請求失敗,被拒絕,超時或熔斷時執行降級邏輯。
-
近實時地監控指標和配置的修改。
Hystrix組件
對RestTemplate的支持
引入hystrix的依賴
order_service
<!--引入hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在啟動類中激活Hystrix
order_service
package qqq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
配置熔斷觸發的降級邏輯
在order_service的controller中配置
/**
* 降級方法
* 和需要收到保護的方法的返回值一致
* 接口參數一致
*/
public Product orderFallBack(Long id)
{
Product product=new Product();
product.setProductName("觸發降級方法");
return product;
}
在需要受到保護的接口上使用@HystrixCommand配置
注意事項:
在之前的案例中,請求在超過1秒后都會返回錯誤信息,這是因為Hystrix的默認超時時長為1,我們可以通過配置修改這個值:
hystrix
配置統一的降級方法:
package com.qqq.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.qqq.entity.Product;
import com.qqq.fegin.ProductFeginClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/order")
/**
* 指定公共的屬性
* 如果過在@DefaultProperties指定了公共的降級方法
* 在@HystrixCommand不需要單獨指定了
*
*/
@DefaultProperties(defaultFallback = "defaultFallBack")
public class OrderController {
//注入RestTemplate對象
@Autowired
private RestTemplate restTemplate;
@Autowired
private ProductFeginClient productFeginClient;
/**
* 使用注解配置熔斷保護
* fallbackmethod:配置熔斷之后的降級方法
* @param id
* @return
*/
@HystrixCommand
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable("id") Long id)
{
Product product=null;
product=productFeginClient.findById(id);
return product;
}
/**
* 降級方法
* 和需要收到保護的方法的返回值一致
* 接口參數一致
*/
public Product orderFallBack(Long id)
{
Product product=new Product();
product.setProductName("觸發降級方法");
return product;
}
public Product defaultFallBack()
{
Product product=new Product();
product.setProductName("觸發統一的降級方法");
return product;
}
}