文章大綱
一、Hystrix基礎介紹
二河绽、斷路器Hystrix簡單使用
三、自定義Hystrix請求命令
四塞淹、Hystrix的服務降級與異常處理
五、Hystrix的請求緩存與請求合并
六罪裹、Hystrix儀表盤與Turbine集群監(jiān)控
七饱普、項目源碼與參考資料下載
八运挫、參考文章
一、Hystrix基礎介紹
1. Hystrix簡介
??一個用戶管理項目套耕,里邊就三個功能:用戶注冊谁帕、用戶登錄、用戶詳情瀏覽冯袍。按照傳統(tǒng)的軟件開發(fā)方式直接創(chuàng)建一個Web項目雇卷,分分鐘就把這三個功能開發(fā)出來了,但是我現(xiàn)在想使用微服務+服務治理的方式來開發(fā):首先我將這個項目拆分為四個微服務颠猴,四個微服務各建一個模塊,分別是用戶注冊模塊小染、用戶登錄模塊翘瓮、用戶詳情瀏覽模塊和數(shù)據(jù)庫操作模塊,這四個模塊通過內部服務治理互相調用裤翩。但是現(xiàn)在存在一個問題资盅,這四個模塊通過服務注冊與訂閱的方式互相依賴,如果一個模塊出現(xiàn)故障會導致依賴它的模塊也發(fā)生故障從而發(fā)生故障蔓延踊赠,進而導致整個服務的癱瘓呵扛。比如說這里的登錄模塊依賴于數(shù)據(jù)庫模塊,如果數(shù)據(jù)庫模塊發(fā)生故障筐带,那么當?shù)卿浤K去調用數(shù)據(jù)庫模塊的時候可能得不到響應今穿,這個調用的線程被掛起,如果處于高并發(fā)的環(huán)境下伦籍,就會導致登錄模塊也崩潰蓝晒。當一個系統(tǒng)劃分的模塊越多,這種故障發(fā)生的頻率就會越高帖鸦,對于這個問題芝薇,Spring Cloud中最重要的解決方案就是斷路。
2. 何時需要保護
??對于一個系統(tǒng)而言作儿,它往往承擔著2層角色洛二,服務提供者與服務消費者。對于服務消費者而言最大的痛苦就是如何“明哲保身”攻锰,做過網關項目的同學肯定感同身受
??上面是一個常見的系統(tǒng)依賴關系晾嘶,底層的依賴往往很多,通信協(xié)議包括 socket口注、HTTP变擒、Dubbo、WebService等等寝志。當通信層發(fā)生網絡抖動以及所依賴的系統(tǒng)發(fā)生業(yè)務響應異常時娇斑,我們業(yè)務本身所提供的服務能力也直接會受到影響策添。
??這種效果傳遞下去就很有可能造成雪崩效應,即整個業(yè)務聯(lián)調發(fā)生異常毫缆,比如業(yè)務整體超時唯竹,或者訂單數(shù)據(jù)不一致。
??那么核心問題就來了苦丁,如何檢測業(yè)務處于異常狀態(tài)浸颓?
??成功率!成功率直接反映了業(yè)務的數(shù)據(jù)流轉狀態(tài)旺拉,是最直接的業(yè)務表現(xiàn)产上。
??當然,也可以根據(jù)超時時間做判斷蛾狗,比如 Sentinel 的實現(xiàn)晋涣。其實這里概念上可以做一個轉化,用時間做超時控制沉桌,超時=失敗谢鹊,這依然是一個成功率的概念。
3. 如何保護
??如同豪豬一樣留凭,“刺”就是他的保護工具佃扼,所有的攻擊都會被刺無情的懟回去。在 Hystrix 的實現(xiàn)中蔼夜,這就出現(xiàn)了“熔斷器”的概念兼耀,即當前的系統(tǒng)是否處于需要保護的狀態(tài)。當熔斷器處于開啟的狀態(tài)時挎扰,所有的請求都不會真正的走之前的業(yè)務邏輯翠订,而是直接返回一個約定的信息,即 FallBack遵倦。通過這種快速失敗原則保護我們的系統(tǒng)尽超。 但是,系統(tǒng)不應該永遠處于“有刺”的狀態(tài)梧躺,當危險過后需要恢復正常似谁。于是對熔斷器的核心操作就是如下幾個功能:如果成功率過低,就打開熔斷器掠哥,阻止正常業(yè)務巩踏,隨著時間的流動,熔斷器處于半打開狀態(tài)续搀,嘗試性放入一筆請求塞琼,熔斷器的核心 API 如下圖:
4. 限流、熔斷禁舷、隔離彪杉、降級
這四個概念是我們談起微服務會經常談到的概念毅往,這里我們討論的是 Hystrix 的實現(xiàn)方式。
4.1 限流
??這里的限流與 Guava 的 RateLimiter 的限流差異比較大派近,一個是為了“保護自我”攀唯,一個是“保護下游”
??當對服務進行限流時,超過的流量將直接 Fallback渴丸,即熔斷侯嘀。而 RateLimiter 關心的其實是“流量整形”,將不規(guī)整流量在一定速度內規(guī)整谱轨。
4.2 熔斷
??當我的應用無法提供服務時戒幔,我要對上游請求熔斷,避免上游把我壓垮土童,當我的下游依賴成功率過低時溪食,我要對下游請求熔斷,避免下游把我拖垮娜扇。
4.3 降級
??降級與熔斷緊密相關,熔斷后業(yè)務如何表現(xiàn)栅组,約定一個快速失敗的 Fallback雀瓢,即為服務降級。
4.3 隔離
業(yè)務之間不可互相影響玉掸,不同業(yè)務需要有獨立的運行空間刃麸,最徹底的,可以采用物理隔離司浪,不同的機器部泊业,次之,采用進程隔離啊易,一個機器多個 Tomcat吁伺,次之,請求隔離租谈,由于 Hystrix 框架所屬的層級為代碼層篮奄,所以實現(xiàn)的是請求隔離,線程池或信號量割去。
二窟却、斷路器Hystrix簡單使用
1. 開始前準備
在之前的文章中我們已經成功的搭建出服務注冊中心、服務提供者和服務消費者三個微服務呻逆,本文的案例我們依然在這三個案例的基礎上來實現(xiàn)夸赫。
首先我們分別啟動服務注冊中心,再啟動兩個服務提供者的實例咖城,端口號分別是8080和8081茬腿,然后再啟動一個服務消費者呼奢,服務消費者的端口號為9000,這幾個都啟動成功之后滓彰,我們訪問http://localhost:9000/ribbon-consumer
這個地址控妻,可以看到如下效果:
此時我們關閉掉任意一個服務提供者,再去訪問這個地址揭绑,會看到如下效果:
2. Hystrix引入
下列的所有操作都是在ribbon-consumer項目中進行
2.1 服務消費者中加入斷路器
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2.2 修改服務消費者啟動入口類
引入hystrix之后弓候,我們需要在入口類上通過@EnableCircuitBreaker開啟斷路器功能,如下:
@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
我們也可以使用一個名為@SpringBootApplication的注解代替這三個注解他匪,@SpringBootApplication注解的定義如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
實際上就是這三個注解的一個整合
2.3 修改Controller
我們創(chuàng)建一個HelloService類菇存,如下:
@Service
public class HelloService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "error")
public String hello() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
return responseEntity.getBody();
}
public String error() {
return "error";
}
}
關于這個HelloService類我說如下幾點:
(1)RestTemplate執(zhí)行網絡請求的操作我們放在HelloService中來完成。
(2)error方法是一個請求失敗時回調的方法邦蜜。
(3)在hello方法上通過@HystrixCommand注解來指定請求失敗時回調的方法依鸥。
最后我們將ConsumerController的邏輯修改成下面這樣:
@RestController
public class ConsumerController {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
public String helloController() {
return helloService.hello();
}
}
此時我們就開啟了斷路器功能
2.4 啟動項目并測試斷路器
啟動項目后,我們的訪問是正常的
現(xiàn)在我們將其中一個服務提供者干掉
重新訪問http://localhost:9000/ribbon-consumer
OK,小伙伴們看到悼沈,此時如果服務調用失敗贱迟,就會調用失敗的那個回調方法。事實上絮供,不僅僅是服務提供者被關閉時我們需要斷路器衣吠,如果請求超時也會觸發(fā)熔斷請求,調用回調方法返回數(shù)據(jù)壤靶。
三缚俏、自定義Hystrix請求命令
1. 簡介
在上面內容中,我們介紹了斷路器Hystrix的一個簡單使用贮乳,主要是通過注解來實現(xiàn)斷路器的功能的忧换,不過對于Hystrix的使用,除了注解向拆,我們也可以使用繼承類的方式來實現(xiàn)亚茬,本文我們就來看看另一種Hystrix的使用方式。
2. 自定義斷路器Hystrix
我們除了使用@HystrixCommand注解浓恳,也可以自定義類繼承自HystrixCommand才写,創(chuàng)建BookCommand.java
package test.custom;
import com.netflix.hystrix.HystrixCommand;
import org.springframework.web.client.RestTemplate;
import java.awt.print.Book;
/**
* 自定義斷路器Hystrix
*
* 在BookCommand中注入RestTemplate,然后重寫兩個方法:一個是getFallback奖蔓,這個方法將在服務調用失敗時回調赞草;
* 另一個是run方法,執(zhí)行請求時調用吆鹤。構造方法的第一個參數(shù)主要用來保存一些分組信息厨疙。
*/
public class BookCommand extends HystrixCommand<String> {
private RestTemplate restTemplate;
@Override
protected String getFallback() {
return "測試出問題了";
}
public BookCommand(Setter setter, RestTemplate restTemplate) {
super(setter);
this.restTemplate = restTemplate;
}
@Override
protected String run() throws Exception {
return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
}
}
在BookCommand中注入RestTemplate,然后重寫兩個方法:一個是getFallback疑务,這個方法將在服務調用失敗時回調沾凄;另一個是run方法梗醇,執(zhí)行請求時調用。構造方法的第一個參數(shù)主要用來保存一些分組信息撒蟀。
3. 同步調用和異步調用
當BookCommand創(chuàng)建成功之后叙谨,我們就可以在我們的Controller中調用它了,創(chuàng)建BookCommandController.java
package test.controller;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import org.springframework.beans.factory.annotation.Autowired;
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 test.custom.BookCommand;
import java.awt.print.Book;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* 測試自定義斷路器Hystrix
*/
@RestController
public class BookCommandController {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String test1() throws ExecutionException, InterruptedException {
// 1.獲取到BookCommand對象之后保屯,我們有兩種方式來執(zhí)行請求手负,一種是調用execute方法發(fā)起一個同步請求,另一種是調用queue方法發(fā)起一個異步請求姑尺。
// 2.同步請求中可以直接返回請求結果竟终。
// 3.異步請求中我們需要通過get方法來獲取請求結果,在調用get方法的時候也可以傳入超時時長切蟋。
BookCommand bookCommand = new BookCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate);
//同步調用
//Book book1 = bookCommand.execute();
//異步調用
Future<String> queue = bookCommand.queue();
String string = queue.get();
return string;
}
}
溫馨提示:
(1)獲取到BookCommand對象之后统捶,我們有兩種方式來執(zhí)行請求,一種是調用execute方法發(fā)起一個同步請求柄粹,另一種是調用queue方法發(fā)起一個異步請求喘鸟。
(2)同步請求中可以直接返回請求結果。
(3)異步請求中我們需要通過get方法來獲取請求結果驻右,在調用get方法的時候也可以傳入超時時長迷守。
4. 項目運行與訪問
保持原本項目啟動情況,即關閉一個服務提供者旺入,之后重新運行ribbon-cunsumer,再訪問http://localhost:9000/test1凯力,出現(xiàn)以下結果
四茵瘾、Hystrix的服務降級與異常處理
1. 服務降級
fallbackMethod所描述的函數(shù)實際上就是一個備胎,用來實現(xiàn)服務的降級處理咐鹤,在注解中我們可以通過fallbackMethod屬性來指定降級處理的方法名稱拗秘,在自定義Hystrix請求命令時我們可以通過重寫getFallback函數(shù)來處理服務降級之后的邏輯。使用注解來定義服務降級邏輯時祈惶,服務降級函數(shù)和@HystrixCommand注解要處于同一個類中雕旨,同時,服務降級函數(shù)在執(zhí)行過程中也有可能發(fā)生異常捧请,所以也可以給服務降級函數(shù)添加‘備胎’
2. 異常處理
我們在調用服務提供者時有可能會拋異常凡涩,默認情況下方法拋了異常會自動進行服務降級,交給服務降級中的方法去處理疹蛉,在自定義Hystrix請求命令的方式下活箕,我們可以在getFallback方法中調用getExecutionException方法來獲取拋出的異常,舉個簡單的例子:
package test.controller;
import org.springframework.beans.factory.annotation.Autowired;
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 test.service.HelloService;
import test.service.HelloServiceImpl;
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
// @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
// public String helloController() {
// return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
// }
@Autowired
private HelloService helloService;
//測試短路訪問
@RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
public String helloController() {
return helloService.hello();
}
}
重新啟動ribbon-cunsumer項目可款,此時訪問http://localhost:9000/test1育韩,控制臺顯示結果如下:
五克蚂、Hystrix的請求緩存與請求合并
1. 請求緩存
??高并發(fā)環(huán)境下如果能處理好緩存就可以有效的減小服務器的壓力,Java中有許多非常好用的緩存工具筋讨,比如Redis埃叭、EHCache等,當然在Spring Cloud的Hystrix中也提供了請求緩存的功能悉罕,我們可以通過一個注解或者一個方法來開啟緩存赤屋,進而減輕高并發(fā)環(huán)境下系統(tǒng)的壓力。OK蛮粮,本文我們就來看看Hystrix中請求緩存的使用益缎。
??請求緩存具體內容可參考https://mp.weixin.qq.com/s/YpWODLrwzFXUQRtIAHLF3Q?
2. 請求合并
??我們將一個項目拆分成很多個獨立的模塊,這些獨立的模塊通過遠程調用來互相配合工作然想,但是莺奔,在高并發(fā)情況下,通信次數(shù)的增加會導致總的通信時間增加变泄,同時令哟,線程池的資源也是有限的,高并發(fā)環(huán)境會導致有大量的線程處于等待狀態(tài)妨蛹,進而導致響應延遲屏富,為了解決這些問題,我們需要來了解Hystrix的請求合并蛙卤。
??請求合并具體內容可參考https://mp.weixin.qq.com/s/0QSKVLaDjBAscRaeccaXuA?
六狠半、Hystrix儀表盤與Turbine集群監(jiān)控
1. 簡介
Hystrix儀表盤,就像汽車的儀表盤實時顯示汽車的各項數(shù)據(jù)一樣颤难,Hystrix儀表盤主要用來監(jiān)控Hystrix的實時運行狀態(tài)神年,通過它我們可以看到Hystrix的各項指標信息,從而快速發(fā)現(xiàn)系統(tǒng)中存在的問題進而解決它行嗤,OK已日,本文我們就來看看Hystrix儀表盤要怎么使用。
2. 創(chuàng)建最基本的Hystrix項目
2.1 新建springboot項目栅屏,項目名稱為hystrix-dashboard
創(chuàng)建后項目結構如下:
2.2 pom.xml文件添加依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wxc</groupId>
<artifactId>hystrix-dashboard</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Dalston.SR3</version>
<relativePath/>
</parent>
<dependencies>
<!-- 其他默認依賴 -->
<!-- 我們需要添加的依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
2.3 創(chuàng)建項目啟動類
com.wxc.test包下新建HystrixDashboardApplication.java
package com.wxc.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
2.4 項目啟動與訪問
瀏覽器輸入http://localhost:2001/進行訪問飘千,訪問成功后結果如下:
三個參數(shù)的含義我已在圖中標注出來了。
OK栈雳,現(xiàn)在我們的儀表盤工程已經創(chuàng)建成功了护奈,但是還不能用來監(jiān)控某一個服務,要監(jiān)控某一個服務哥纫,需要該服務提供一個/hystrix.stream接口逆济,so,我們需要對我們的服務消費者工程稍加改造。
3.改造要監(jiān)控的服務
3.1 改造服務消費者工程ribbon-consumer
在pom.xml文件中添加以下依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在確保服務注冊中心和服務提供者開啟情況下奖慌,在服務消費者工程的入口類上添加@EnableCircuitBreaker注解抛虫,表示開啟斷路器功能。此時項目啟動情況如下:
這個信息表明我們的consumer工程目前已經具備了/hystrix.stream接口简僧,我們可以直接訪問這個接口了建椰。但是這里有一個細節(jié)需要小伙伴們注意:要訪問/hystrix.stream接口,得先訪問consumer工程中的任意一個其他接口岛马,否則如果直接訪問/hystrix.stream接口的話棉姐,會打印出一連串的ping: ping: …。 OK啦逆,我先訪問consumer中的任意一個其他接口伞矩,然后在訪問/hystrix.stream接口,訪問地址如下:http://localhost:9000/hystrix.stream夏志,訪問結果如下:
3.2 hystrix-dashboard服務監(jiān)控
在hystrix儀表盤中輸入監(jiān)控地址乃坤,如下:
然后點擊Monitor Stream按鈕,我們就可以看到監(jiān)控畫面了沟蔑,如下:
3.3 監(jiān)控參數(shù)詳解
4. Turbine集群監(jiān)控
OK湿诊,上文我們看了一個監(jiān)控單體應用的例子,在實際應用中瘦材,我們要監(jiān)控的應用往往是一個集群厅须,這個時候我們就得采取Turbine集群監(jiān)控了。Turbine有一個重要的功能就是匯聚監(jiān)控信息食棕,并將匯聚到的監(jiān)控信息提供給Hystrix Dashboard來集中展示和監(jiān)控朗和。那我們就來看看Turbine集群監(jiān)控如何使用。
搭建監(jiān)控環(huán)境
監(jiān)控環(huán)境的搭建也是分為四個步驟:
第一步:創(chuàng)建一個普通的Spring Boot工程
第一步創(chuàng)建一個名叫turbine的普通Spring Boot工程簿晓。
第二步:添加依賴
工程創(chuàng)建完成之后眶拉,我們需要添加一個依賴,如下:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Dalston.SR3</version>
<relativePath/>
</parent>
<dependencies>
<!-- 其他默認的依賴 -->
<!-- 我們要添加的依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
</dependencies>
第三步:添加注解
在入口類上添加@EnableTurbine注解表示開啟Turbine抢蚀,如下:
@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbine
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
第四步:修改配置
在application.properties配置文件中加入eureka和turbine的相關配置,如下:
spring.application.name=turbine
server.port=2002
management.port=2003
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
turbine.app-config=ribbon-consumer
turbine.cluster-name-expression="default"
turbine.combine-host-port=true
關于這個配置文件镰禾,我說如下幾點:
1.turbine.app-config=ribbon-consumer指定了要監(jiān)控的應用名字為ribbon-consumer
2.turbine.cluster-name-expression=”default”,表示集群的名字為default
3.turbine.combine-host-port=true表示同一主機上的服務通過host和port的組合來進行區(qū)分皿曲,默認情況下是使用host來區(qū)分,這樣會使本地調試有問題
查看監(jiān)控圖
OK吴侦,監(jiān)控服務創(chuàng)建成功之后屋休,我們再次依次啟動eureka-server、provider和consumer备韧,其中consumer啟動兩個實例劫樟,兩個實例的端口不一致,再分別啟動hystrix-dashboard和turbine,然后在hystrix監(jiān)控地址欄輸入如下地址(監(jiān)控之前要記得先訪問一下服務中的任意一個接口):http://localhost:2002/turbine.stream叠艳,訪問結果如下:
七奶陈、項目源碼與參考資料下載
鏈接:https://pan.baidu.com/s/120z7NZZ_-Z_BHySQHHazZw
提取碼:flic