Spring Cloud Hystrix服務容錯 (學習總結)
一、雪崩效應
“雪崩效應”: 因 '服務提供者' 的不可用導致 '服務呼叫者' 的不可用,並將不可用逐漸放大的過程
二、Hystrix介紹
Spring Cloud中服務之間的呼叫方式主要有兩種,一種是Ribbon+RestTemplate,一種是Feign宣告式服務呼叫,在實際專案中,為了服務高可用,一個服務通常會叢集部署,執行多個例項, 由於網路原因或者服務自身原因,被呼叫的服務並不能保證100%請求成功,如果這時候有大量的請求請求這個故障的服務,由於服務之間的依賴關係,故障會進行蔓延,這時候會導致呼叫服務自身也出現不可用的情況,使用Hystrix可以解決這個問題。當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因呼叫故障服務被長時間佔用不釋放,避免了故障在分散式系統中的蔓延。
三、準備工作:
本文同樣有三個工程,分別是:
eureka-server: 服務註冊中心,埠1111;
hystrix-service:服務提供者,埠2222和3333,需要啟動多個例項;
ribbon-hystrix: Ribbon服務容錯,埠4444
feign-hystrix: Feign服務容錯,埠5555
對於eureka-server以及hystrix-service的搭建本文不做詳細介紹,需要注意的是hystrix-service需要暴露一個介面/getInfo給外部呼叫:
/** * @Title: HystrixServiceController * @Description: 服務提供者 * @Author WeiShiHuai * @Date 2018/9/11 9:31 */ @RestController public class HystrixServiceController { private static Logger logger = LoggerFactory.getLogger(HystrixServiceController.class); @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/getInfo") public String getInfo(@RequestParam("name") String name) { ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance(); String host = serviceInstance.getHost(); Integer port = serviceInstance.getPort(); String info = "hello, name = " + name + ", host = " + host + ", port = " + port; logger.info(info); return info; } }
四、新建springcloud_ribbon_hystrix專案
首先講一下在Ribbon專案中使用Hystrix斷路器的方法:
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.springcloud.wsh</groupId> <artifactId>springcloud_ribbon_hystrix</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud_ribbon_hystrix</name> <description>Spring Cloud Hystrix Ribbon服務單元</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Camden.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
五、啟動類加上@EnableCircuitBreaker註解
該註解主要是開啟Hystrix斷路器功能
/**
* @Description: 應用程式啟動類
* @Author: WeiShiHuai
* @Date: 2018/9/11 9:37
* Spring Cloud中服務之間的呼叫方式主要有兩種,一種是Ribbon+RestTemplate,一種是Feign宣告式服務呼叫,在實際專案中,為了服務高可用,一個服務通常會叢集部署,執行多個例項,
* 由於網路原因或者服務自身原因,被呼叫的服務並不能保證100%請求成功,如果這時候有大量的請求請求這個故障的服務,由於服務之間的依賴關係,故障會進行蔓延,這時候會導致呼叫服務自身也出現不可用的情況,使用Hystrix可以解決這個問題
* 當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因呼叫故障服務被長時間佔用不釋放,避免了故障在分散式系統中的蔓延。
* <p>
* “雪崩效應”: 因 '服務提供者' 的不可用導致 '服務呼叫者' 的不可用,並將不可用逐漸放大的過程
*/
@SpringBootApplication
@EnableDiscoveryClient
// @EnableHystrix 與 @EnableCircuitBreaker註解用於開啟Hystrix斷路器功能
//@EnableHystrix
@EnableCircuitBreaker
//@EnableHystrixDashboard註解開啟Hystrix儀表盤功能,監控請求情況
//通過Hystrix Dashboard可以直接看到各個Hystrix Command的請求響應時間,請求成功率等資料
@EnableHystrixDashboard
//@SpringCloudApplication = @SpringBootApplication + @EnableDiscoveryClient + @EnableCircuitBreaker
public class SpringcloudRibbonHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudRibbonHystrixApplication.class, args);
}
/**
* 開啟Ribbon負載均衡能力,並注入spring容器
*/
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
六、新建HystrixService
- 改造原來的服務消費方式,在使用ribbon消費服務的函式上增加
@HystrixCommand
註解來指定回撥方法。
/**
* @Title: HystrixService
* @ProjectName springcloud_hystrix
* @Description: 實現Hystrix斷路器
* @Author WeiShiHuai
* @Date 2018/9/11 9:52
*/
@Service
public class HystrixService {
private static Logger logger = LoggerFactory.getLogger(HystrixService.class);
@Autowired
RestTemplate restTemplate;
//@HystrixCommand註解主要對getInfo()開啟熔斷器的功能,並指定fallbackMethod熔斷方法(服務不可用時執行熔斷方法)
@HystrixCommand(fallbackMethod = "getInfoErrorFallBack")
public String getInfo(String name) {
String info = restTemplate.getForObject("http://hystrix-service/getInfo?name={name}", String.class, name);
logger.info(info);
return info;
}
/**
* Hystrix熔斷方法(即呼叫失敗回撥方法)
*
* @param name
* @return
*/
public String getInfoErrorFallBack(String name) {
return "sorry, " + name + ", the hystrix service is not available! ";
}
}
七、新建HystrixController
注入HystrixService,呼叫hystrixService.getInfo()介面進行斷路器測試:
/**
* @Title: HystrixController
* @ProjectName springcloud_hystrix
* @Description: 測試
* @Author WeiShiHuai
* @Date 2018/9/11 9:48
*/
@RestController
public class HystrixController {
private static Logger logger = LoggerFactory.getLogger(HystrixController.class);
@Autowired
private HystrixService hystrixService;
@RequestMapping("/getInfo")
public String getInfo(@RequestParam("name") String name) {
return hystrixService.getInfo(name);
}
}
八、啟動專案
依次啟動eureka-server埠1111、hystrix-service可以啟動兩個例項2222和3333,ribbon-hystrix埠4444,
介面成功呼叫,同時看後臺列印的日誌,也實現了服務的負載均衡呼叫
可以看到,當hystrix-service服務不可用時,我們訪問其中的介面,執行了我們制定的fallback熔斷方法。這樣在Ribbon中使用Hystrix斷路器功能已經實現。接下來看一下在Feign中使用Hystrix,因為Feign預設集成了Hystrix,因此不需要新增Hystrix的依賴。
九、新建springcloud_feign_hystrix工程
pom.xml只需要引入主要的Feign依賴:
<?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.sprincloud.wsh</groupId>
<artifactId>springcloud_feign_hystrix</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud_feign_hystrix</name>
<description>Spring Cloud Feign Hystrix</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Camden.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
十、啟動類加上@EnableFeignClients註解
該註解主要是開啟Feign遠端服務呼叫的功能
/**
* @Description: 應用程式啟動類
* @Author: WeiShiHuai
* @Date: 2018/9/11 10:23
*
* Feign預設集成了Hystrix,所以只需要加入Feign的依賴即可。
* Feign是通過@FeignClient()中指定fallback來實現Hystrix斷路器功能的,當遠端服務呼叫失敗的時候就會執行這個回撥方法。
*
*/
@SpringBootApplication
@EnableDiscoveryClient
//開啟Feign宣告式服務呼叫功能
@EnableFeignClients
public class SpringcloudFeignHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudFeignHystrixApplication.class, args);
}
}
新建配置檔案application.yml:
server:
port: 5555
spring:
application:
name: feign-hystrix
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/
十一、新建Feign介面HystrixFeign
/**
* @Title: HystrixFeign
* @ProjectName springcloud_hystrix
* @Description:
* @Author WeiShiHuai
* @Date 2018/9/11 10:25
*/
@FeignClient(value = "hystrix-service", fallback = HystrixFeignFallback.class)
public interface HystrixFeign {
@RequestMapping("/getInfo")
String getInfo(@RequestParam("name") String name);
}
在feign介面中,我們通過指定fallback來實現Hystrix服務容錯功能
新建HystrixFeignFallback回撥類,並且實現@FeignClient修飾的介面:
/**
* @Title: HystrixFeignFallback
* @ProjectName springcloud_feign_hystrix
* @Description: FeignClient失敗回撥方法
* @Author WeiShiHuai
* @Date 2018/9/11 10:28
* FeignClient失敗回撥方法必須實現使用@FeignClient標識的介面(implements HystrixFeign),實現其中的方法
*/
@Component
public class HystrixFeignFallback implements HystrixFeign {
/**
* 由於某種原因使得服務呼叫不成功時會執行該回調方法
*
* @param name
* @return
*/
@Override
public String getInfo(String name) {
return "sorry " + name + ", feign client error";
}
}
十二、新建FeignHystrixController
暴露一個getInfo介面給外部呼叫,如下:
/**
* @Title: HystrixFeignController
* @ProjectName springcloud_feign_hystrix
* @Description: 測試Feign
* @Author WeiShiHuai
* @Date 2018/9/11 10:28
* 注入FeignClient,呼叫feignClient的方法實現遠端方法呼叫
*/
@RestController
public class HystrixFeignController {
private static Logger logger = LoggerFactory.getLogger(HystrixFeignController.class);
@Autowired
private HystrixFeign hystrixFeign;
/**
* 使用http://localhost:5555/getInfo?name=xxx訪問,實際上會通過FeignClient呼叫服務hystrix-service提供的getInfo介面
*
* @param name
* @return
*/
@GetMapping("/getInfo")
public String getInfo(@RequestParam("name") String name) {
String info = hystrixFeign.getInfo(name);
logger.info(info);
return info;
}
}
接下來,同樣啟動eureka-server、以及hystrix-service兩個例項,還有啟動feign-hystrix,訪問http://localhost:5555/getInfo?name=weixiaohuai,結果如下:
介面已經成功呼叫,通過檢視後臺日誌,同樣feign也實現對服務的負載均衡呼叫
由此證明Hystrix斷路器起作用了。
十三、總結
以下內容,摘自網上總結:
雪崩效應的原因: 1) 服務提供者不可用 a.硬體故障 b.程式Bug c.使用者大量請求:在秒殺和大促開始前,如果準備不充分,使用者發起大量請求造成服務提供者的不可用 2) 重試加大流量 a.使用者重試:使用者由於忍受不了介面上長時間的等待,而不斷重新整理頁面甚至提交表單 b.程式碼邏輯重試:服務呼叫端的會存在大量服務異常後的重試邏輯 3) 服務呼叫者不可用 a.同步等待造成的資源耗盡:使用 同步呼叫 時, 會產生大量的等待執行緒佔用系統資源. 一旦執行緒資源被耗盡,服務呼叫者提供的服務也將處於不可用狀態, 造成服務雪崩效應產生 雪崩效應的解決措施: 1) 流量控制 a.閘道器限流 因為Nginx的高效能, 目前一線網際網路公司大量採用Nginx+Lua的閘道器進行流量控制, 由此而來的OpenResty也越來越熱門. b.使用者互動限流 具體措施: a21. 採用載入動畫,提高使用者的忍耐等待時間. a22. 提交按鈕新增強制等待時間機制. c.關閉重試 2) 改進快取模式 a.快取預載入 b.同步改為非同步重新整理 3) 服務自動擴容 a.AWS的auto scaling 4) 服務呼叫者降級服務 a.資源隔離:主要是對呼叫服務的執行緒池進行隔離. b.對依賴服務進行分類 依賴服務分為: 強依賴和若依賴. 強依賴服務不可用會導致當前業務中止,而弱依賴服務的不可用不會導致當前業務的中止. c.不可用服務的呼叫快速失敗 一般通過 超時機制, 熔斷器 和熔斷後的 降級方法 來實現