目錄
- `Hystrix` 簡介
- `Hystrix` 服務熔斷産生的背景
-
- 引起雪崩效應常見場景
- 初探 `Hystrix`
-
- `Hystrix` 設計目标,作用
- `Hystrix` 如何實作這些設計目标
- `Hystrix` 入門
-
- 項目概覽
- `Hystrix` 的使用(不整合 `Feign`)
- `Hystrix-study-user` 服務的主啟動類
- `Hystrix-study-user` 服務的 `Feign` 的用戶端接口
- `Hystrix-study-user` 服務的服務降級類
- `Hystrix-study-user` 服務的 `Service` 實作類
- `Hystrix-study-user` 服務的配置檔案
- 服務熔斷測試
- `@HystrixCommand` 注解的簡單使用
-
- `Hystrix-study-user` 服務的 `Service` 實作類添加方法
- `Hystrix-study-activity` 服務的 `Controller` 類
- 服務降級測試
- 修改 `Hystrix-study-activity` 服務的 `Controller` 類再測試
- `@HystrixCommand` 注解的常見使用
- `Hystrix` 與 `Ribbon` 的逾時時間
- `Hystrix` 的斷路器工作原理
-
- 斷路器開啟的條件
- 斷路器關閉的條件
- 服務降級與服務熔斷差別
-
- 服務熔斷
- 服務降級
- 差別
-
- 相同點
- 不同點
Hystrix
簡介
Hystrix
Hystrix
是
Netlifx
開源的一款容錯架構,防雪崩利器,具備服務降級,服務熔斷,依賴隔離,監控(
Hystrix Dashboard
)等功能
官網:https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix
服務熔斷産生的背景
Hystrix
分布式系統環境下,服務間類似依賴非常常見,一個業務調用通常依賴多個基礎服務。如下圖,對于同步調用,當庫存服務不可用時,商品服務請求線程被阻塞,當有大批量請求調用庫存服務時,最終可能導緻整個商品服務資源耗盡,無法繼續對外提供服務。并且這種不可用可能沿請求調用鍊向上傳遞,這種現象被稱為雪崩效應

引起雪崩效應常見場景
- 硬體故障:如伺服器當機,機房斷電,光纖被挖斷等
- 流量激增:如異常流量,重試加大流量等
- 緩存擊穿:一般發生在應用重新開機,所有緩存失效時,以及短時間内大量緩存失效時。大量的緩存不命中,使請求直擊後端服務,造成服務提供者超負荷運作,引起服務不可用
- 程式
:如程式邏輯導緻記憶體洩漏,BUG
長時間JVM
等FullGC
- 同步等待:服務間采用同步調用模式,同步等待造成的資源耗盡
初探 Hystrix
Hystrix
Hystrix
設計目标,作用
Hystrix
- 服務的降級
- 服務的熔斷
- 服務的限流
- 提供近實時的監控與告警
Hystrix
如何實作這些設計目标
Hystrix
- 使用指令模式将所有對外部服務(或依賴關系)的調用包裝在
或HystrixCommand
對象中,并将該對象放在單獨的線程中執行HystrixObservableCommand
- 每個依賴都維護着一個線程池(或信号量),線程池被耗盡則拒絕請求(而不是讓請求排隊)
- 記錄請求成功,失敗,逾時和線程拒絕
- 服務錯誤百分比超過了門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求
- 請求失敗,被拒絕,逾時或熔斷時執行降級邏輯
- 近實時地監控名額和配置的修改
Hystrix
入門
Hystrix
項目概覽
Hystrix
的使用(不整合 Feign
)
Hystrix
Feign
在使用
Feign
元件作為接口調用遠端服務時,是不需要添加
Hystrix
的依賴的。因為
Feign
預設已經內建了
Hystrix
和
Ribbon
。如果單獨使用
Hystrix
元件時,可以導入以下依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Hystrix-study-user
服務的主啟動類
Hystrix-study-user
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker // 開啟斷路器
@EnableHystrixDashboard // 開啟 Hystrix 的監控儀表盤
public class UserApplication {
private static final Logger log = LoggerFactory.getLogger(UserApplication.class);
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
log.info("===============springcloud user啟動了=================");
}
// 解決 hystrix-dashBoard 儀表盤不能通路
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
Hystrix-study-user
服務的 Feign
的用戶端接口
Hystrix-study-user
Feign
@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {
@RequestMapping(path = { "/activity/getCoupon" }, method = RequestMethod.POST)
String getCoupon(@RequestBody Integer id);
@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
String getCouponTimeOut(@RequestBody Integer id);
@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
String timeOut(@RequestParam Integer mills);
@RequestMapping(path = { "/timeOut" }, method = RequestMethod.POST)
String tripTest(@RequestParam Integer mills);
}
Hystrix-study-user
服務的服務降級類
Hystrix-study-user
@Component
public class UserFeignFallback implements UserFeign {
@Override
public String getCoupon(Integer id) {
return null;
}
@Override
public String getCouponTimeOut(Integer id) {
return "------超過2000毫秒時,直接進入服務降級處理------";
}
@Override
public String timeOut(Integer mills) {
return "---------------您的請求【逾時】或【失敗】,已進入服務降級模式了----------------";
}
@Override
public String tripTest(Integer mills) {
return "-------------請求未通過-----------";
}
}
Hystrix-study-user
服務的 Service
實作類
Hystrix-study-user
Service
@Service
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserFeign userFeign;
// 使用注解 @HystrixCommand 指定服務熔斷的回退方法
@HystrixCommand(fallbackMethod = "firstLoginError")
@Override
public String firstLogin(Integer id) {
// 采用 Feign 用戶端來調用服務 Hystrix-study-activity
String result = userFeign.getCoupon(id);
log.info("===================result的值為:" + result + "======================");
return result;
}
// 當服務 Hystrix-study-activity 不可用時,這時讓其回調回退方法
public String firstLoginError(Integer id) {
return "---------您請求的服務暫時不可用,請稍後再試--------------";
}
}
Hystrix-study-user
服務的配置檔案
Hystrix-study-user
server.port=8080
spring.application.name=study-user
eureka.client.service-url.defaultZone=http://eureka7001.com:8761/eureka/
#事實上,springcloud預設已為Feign整合了Hystrix,要想為Feign打開Hystrix支援,隻需要設定feign.hystrix.enabled=true即可。
feign.hystrix.enabled=true
#補充:在springcloud Dalston之前的版本中,Feign預設開啟Hystrix支援,無需設定feign.hystrix.enabled=true.
#從springcloud Dalston版本開始,Feign的Hystrix支援預設關閉,需要手動設定開啟
#配置Hystrix的逾時時間
#default全局有效,service id指定應用有效
hystrix.command.default.execution.timeout.enabled=true #預設為true
#預設值為 1000毫秒(對于每一個标有注解 @FeignClient 的接口的所有抽象方法生效)
#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=9000
#顯示伺服器詳細的健康資訊
management.endpoint.health.show-details=always
#暴露全部的監控資訊(解決hystrix-dashBoard儀表盤不能通路)
management.endpoint.web.exposure.include="*"
服務熔斷測試
分别啟動
Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user
三個服務,使用
Postman
測試如下
此時再關閉
Hystrix-study-activity
服務
說明執行了
@HystrixCommand(fallbackMethod = "firstLoginError")
服務熔斷的回退方法
@HystrixCommand
注解的簡單使用
@HystrixCommand
依然使用上面的入門項目,隻不過不需要引入
Hystrix
的依賴了,直接使用
Feign
用戶端進行調用。在使用
Feign
元件作為接口調用遠端服務時,是不需要添加
Hystrix
的依賴的。因為
Feign
預設已經內建了
Hystrix
和
Ribbon
。如果單獨使用
Hystrix
元件時,可以導入以下依賴
Hystrix-study-user
服務的 Service
實作類添加方法
Hystrix-study-user
Service
execution.isolation.thread.timeoutInMilliseconds
:該屬性用來配置方法執行的逾時時間。我們在之前對于降級處理時間的配置,都是在全局配置檔案
application.yml
中配置的,
commandProperties
可以讓我們在一些具有獨特要求的方法上,單獨進行一些配置操作
// 設定Hystrix的服務降級逾時時間,超過2000毫秒時,直接進入服務降級處理
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
@Override
public String firstLoginTimeOut(Integer id) {
String result = userFeign.getCouponTimeOut(id);
log.info("===================result的值為:" + result + "======================");
return result;
}
Hystrix-study-activity
服務的 Controller
類
Hystrix-study-activity
Controller
@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {
private static final Logger log = LoggerFactory.getLogger(ActivityController.class);
@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
@ResponseBody
public String getCouponTimeOut(@RequestBody Integer id) {
try {
Random random = new Random();
TimeUnit.SECONDS.sleep(random.nextInt(10) % (7) + 4);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("=============該使用者首次登陸(注冊),領取優惠券失敗============");
return "error";
}
}
服務降級測試
分别啟動
Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user
三個服務,使用
Postman
測試如下
說明執行了
UserFeignFallback
服務降級處理類的相應方法
修改 Hystrix-study-activity
服務的 Controller
類再測試
Hystrix-study-activity
Controller
@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {
private static final Logger log = LoggerFactory.getLogger(ActivityController.class);
@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
@ResponseBody
public String getCouponTimeOut(@RequestBody Integer id) {
log.info("=============該使用者首次登陸(注冊),領取優惠券成功============");
return "SUCCESS";
}
}
再測試結果
@HystrixCommand
注解的常見使用
@HystrixCommand
@Service
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserFeign userFeign;
@HystrixCommand(threadPoolKey = "time", threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "2"),
@HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
@Override
public String timeOut(Integer mills) {
log.info("-----------mills:的值為:" + mills + "--------------");
return userFeign.timeOut(mills);
}
@HystrixCommand(threadPoolKey = "time_1", threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "2"),
@HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
@Override
public String timeOut_1(Integer mills) {
log.info("-----------mills:的值為:" + mills + "--------------");
return userFeign.timeOut(mills);
}
/**
* Hystrix的斷路器測試
*
* 模拟測試:3秒鐘内,請求次數達到2次,并且失敗率在50%以上,斷路器做跳閘動作。跳閘後的活動視窗設定為3秒
*
* 服務的健康狀态檢查:http://ip:port/actuator/health
* Hystrix的健康狀态為:status: "UP"
* Hystrix的斷路器跳閘後狀态為:status: "CIRCUIT_OPEN"
* 此時要删除服務的降級處理類 UserFeignFallback,才能檢視健康狀态
*/
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "3000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")})
@Override
public String tripTest(Integer mills) {
return userFeign.tripTest(mills);
}
}
鑒于篇幅,文章不再贅述,詳情可以檢視:https://www.cnblogs.com/zhenbianshu/p/9630167.html
Hystrix
與 Ribbon
的逾時時間
Hystrix
Ribbon
- 如果
,則會有兩個執行方法逾時的配置:一個就是hystrix.command.default.execution.timeout.enabled = true(預設)
的ribbon
,一個就是熔斷器ReadTimeout
的hystrix
,此時誰的值小誰生效timeoutInMilliseconds
- 如果
,則熔斷器不進行逾時熔斷,而是根據hystrix.command.default.execution.timeout.enabled = false
的ribbon
抛出的異常而熔斷,也就是取決于ReadTimeout
ribbon
-
的ribbon
配置的是請求服務的逾時時間,除非服務找不到,或者網絡原因,這個時間才會生效ConnectTimeout
-
還有ribbon
對目前執行個體的重試次數,MaxAutoRetries
對切換執行個體的重試次數,如果MaxAutoRetriesNextServer
的ribbon
逾時,或者ReadTimeout
連接配接逾時,會進行重試操作ConnectTimeout
- 由于
的重試機制,通常熔斷的逾時時間需要配置的比ribbon
長,ReadTimeout
比ReadTimeout
長,否則還未重試就熔斷了ConnectTimeout
- 為了確定重試機制的正常運作,理論上(以實際情況為準)建議
的逾時時間為:hystrix
(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout
Hystrix
的斷路器工作原理
Hystrix
斷路器開啟的條件
- 當請求達到閥值時候:預設
秒内10
次請求,當請求失敗率達到預設20
的時,此時斷路器将會開啟,所有的請求都不會執行50%
斷路器關閉的條件
- 當斷路器開啟
秒(預設)時,這時斷路器是半開狀态, 會允許其中一個請求執行5
- 如果執行成功,則斷路器會關閉;如果失敗,則繼續開啟。循環重複這兩個流程
服務降級與服務熔斷差別
服務熔斷
熔斷機制是應對雪崩效應的一種微服務鍊路保護機制,一般來說,每個服務都需要熔斷機制的
- 如高壓電路中,如果某個地方的電壓過高,熔斷器就會熔斷,對電路進行保護。在微服務架構中,熔斷機制也是起着類似的作用
- 當扇對外連結路的某個微服務不可用,或響應逾時,或當機,或異常時,進而熔斷該節點微服務的調用,快速傳回錯誤的響應資訊。當檢測到該節點微服務調用響應正常後,恢複調用鍊路
使用注解
@HystrixCommand(fallbackMethod = " ")
來指定服務熔斷的回退方法
@Service
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserFeign userFeign;
// 使用注解 @HystrixCommand 指定服務熔斷的回退方法
@HystrixCommand(fallbackMethod = "firstLoginError")
@Override
public String firstLogin(Integer id) {
// 采用 Feign 用戶端來調用服務 Hystrix-study-activity
String result = userFeign.getCoupon(id);
log.info("===================result的值為:" + result + "======================");
return result;
}
// 當服務 Hystrix-study-activity 不可用時,這時讓其回調回退方法
public String firstLoginError(Integer id) {
return "---------您請求的服務暫時不可用,請稍後再試--------------";
}
}
服務降級
- 降級是指自己的待遇下降了,從
調用環節來講,就是說整體資源快不夠了,忍痛将某些服務單元先關掉,關閉後還要傳回一些可處理的備選方法,待渡過難關,再開啟回來RPC
- 降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)
@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {
@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
String getCouponTimeOut(@RequestBody Integer id);
@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
String timeOut(@RequestParam Integer mills);
}
@Component
public class UserFeignFallback implements UserFeign {
@Override
public String getCouponTimeOut(Integer id) {
return "------超過2000毫秒時,直接進入服務降級處理------";
}
@Override
public String timeOut(Integer mills) {
return "---------------您的請求【逾時】或【失敗】,已進入服務降級模式了----------------";
}
}
差別
相同點
- 目的很一緻,都是從服務的可用性可靠性着想,為防止系統的整體緩慢甚至崩潰,采用的技術手段
- 最終表現類似,對于兩者來說,最終讓使用者體驗到的是某些功能暫時不可達或不可用
不同點
- 觸發原因不太一樣:服務熔斷一般是某個服務(下遊服務)故障或異常引起,而服務降級一般是從系統整體負荷考慮的
- 管理層次不太一樣:熔斷其實是一個架構級的處理,每個服務都需要(無層級之分),而降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)