一、雪崩效應
二、hrstrix組件
-
包裹請求:使用HystrixCommand包裹對依賴的調用邏輯,每個命令在獨立線程中執行。這使用了設計模式中的“命令模式”。
-
跳閘機制:當某服務的錯誤率超過一定的閾值時,Hystrix可以自動或手動跳閘,停止請求該服務一段時間。
- 資源隔離:Hystrix爲每個依賴都維護了一個小型的線程池(或者信號量)。如果該線程池已滿, 發往該依賴的請求就被立即拒絕,而不是排隊等待,從而加速失敗判定。
- 監控:Hystrix可以近乎實時地監控運行指標和配置的變化,例如成功、失敗、超時、以及被拒絕的請求等。
- 回退機制:當請求失敗、超時、被拒絕,或當斷路器打開時,執行回退邏輯。回退邏輯由開發人員自行提供,例如返回一個缺省值。
- 自我修復:斷路器打開一段時間後,會自動進入“半開”狀態。
三、Rest實現服務熔斷
(1)order-service
<?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">
<parent>
<artifactId>spring_cloud_hystrix_demo</artifactId>
<groupId>cn.itcast</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order_service_rest</artifactId>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--引入EurekaClient-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--springcloud整合的openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 9004 #端口
spring:
application:
name: service-order #服務名稱
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
username: root
password: 111111
jpa:
database: MySQL
show-sql: true
open-in-view: true
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/
instance:
prefer-ip-address: true #使用ip地址註冊
package cn.itcast.order;
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;
@SpringBootApplication
//激活hystrix
@EnableCircuitBreaker
@EntityScan("cn.itcast.order.entity")
public class RestOrderApplication {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RestOrderApplication.class,args);
}
}
package cn.itcast.order.controller;
import cn.itcast.order.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* 使用註解配置熔斷保護
* fallbackmethod : 配置熔斷之後的降級方法
*/
@HystrixCommand(fallbackMethod = "orderFallBack")
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
return restTemplate.getForObject("http://service-product/product/1",Product.class);
}
/**
* 降級方法
* 和需要收到保護的方法的返回值一致
* 方法參數一致
*/
public Product orderFallBack(Long id) {
Product product = new Product();
product.setProductName("觸發降級方法");
return product;
}
}
(2)service-product 不變
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Product product = productService.findById(id);
product.setProductName("訪問的服務地址:"+ip + ":" + port);
return product;
}
hystrix:
command:
default:
execution:
isolation:
strategy: ExecutionIsolationStrategy.SEMAPHORE #信號量隔離
strategy: # ExecutionIsolationStrategy.THREAD 線程池隔離
thread:
timeoutInMilliseconds: 3000 #默認的連接超時時間1秒,若1秒沒有返回數據,自動的觸發降級邏輯
統一降級接口:如果我們的服務有多個接口需要降級,該怎麼辦呢?
添加統一降級方法,和@DefaultProperties註解在類上面。這樣就不需要給某個接口特地的指定某個方法
package cn.itcast.order.controller;
import cn.itcast.order.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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 : 指定此接口中公共的熔斷設置
* 如果過在@DefaultProperties指定了公共的降級方法
* 在@HystrixCommand不需要單獨指定了
*/
@DefaultProperties(defaultFallback = "defaultFallBack")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* 使用註解配置熔斷保護
* fallbackmethod : 配置熔斷之後的降級方法
*/
@HystrixCommand
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
return restTemplate.getForObject("http://service-product/product/1",Product.class);
}
/**
* 指定統一的降級方法
* * 參數 : 沒有參數
*/
public Product defaultFallBack() {
Product product = new Product();
product.setProductName("觸發統一的降級方法");
return product;
}
}
測試:
四、Feign實現服務熔斷
添加依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
feign中開啓配置hystrix:
feign:
client:
config:
service-product: #需要調用的服務名稱
loggerLevel: FULL
#開啓對hystrix的支持
hystrix:
enabled: true
配置一個feign的降級邏輯類:
package cn.itcast.order.feign;
import cn.itcast.order.entity.Product;
import org.springframework.stereotype.Component;
@Component
public class ProductFeignClientCallBack implements ProductFeignClient {
/**
* 熔斷降級的方法
*/
@Override
public Product findById(Long id) {
Product product = new Product();
product.setProductName("feign調用觸發熔斷降級方法");
return product;
}
}
package cn.itcast.order.feign;
import cn.itcast.order.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* 聲明需要調用的微服務名稱
* @FeignClient
* * name : 服務提供者的名稱
* * fallback : 配置熔斷髮生降級方法
* 實現類
*/
@FeignClient(name="service-product",fallback = ProductFeignClientCallBack.class)
public interface ProductFeignClient {
/**
* 配置需要調用的微服務接口
*/
@RequestMapping(value="/product/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable("id") Long id);
}
啓動類:
package cn.itcast.order;
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.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EntityScan("cn.itcast.order.entity")
//激活Feign
@EnableFeignClients
//激活hystrix
@EnableCircuitBreaker
public class FeignOrderApplication {
public static void main(String[] args) {
SpringApplication.run(FeignOrderApplication.class,args);
}
}
測試:
降級成功。
五、hystrix的監控平臺
這樣有助於我們去看哪些接口出現了降級
使用feign服務來驗證。
引入依賴
<!--引入hystrix的監控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
配置暴露接口:
management:
endpoints:
web:
exposure:
include: '*'
就是這樣的,那麼大家是不是感覺這樣很不方便看啊
解決辦法:
引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
啓動類添加一個註解:
這三個0有一些特別的含義:
第一個代表:成功的次數,第二個代表熔斷的次數,第三個代表錯誤請求的次數。
六、斷路器聚合監控Turbine
創建一個新的工程:
引入依賴:
<?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">
<parent>
<artifactId>spring_cloud_hystrix_demo</artifactId>
<groupId>cn.itcast</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hystrix_turbine</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
</project>
配置啓動類
package cn.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
//trubin配置
@EnableTurbine
@EnableHystrixDashboard
public class TurbinAppliation {
public static void main(String[] args) {
SpringApplication.run(TurbinAppliation.class,args);
}
}
配置文件
server:
port: 8031
spring:
application:
name: hystrix-turbine
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
instance:
prefer-ip-address: true
turbine:
# 要監控的微服務列表,多個用,分隔
appConfig: service-order
clusterNameExpression: "'default'"
測試:
這是跟剛纔一樣的,輸入url就可以了。(http://localhost:8031/turbine.stream)
七、斷路器
狀態:
-
Closed:關閉狀態(斷路器關閉),所有請求都正常訪問。代理類維護了最近調用失敗的次數,如果某次調用失敗,則使失敗次數加1。如果最近失敗次數超過了在給定時間內允許失敗的閾值,則代理類切換到斷開(Open)狀態。此時代理開啓了一個超時時鐘,當該時鐘超過了該時間,則切換到半斷開(Half-Open)狀態。該超時時間的設定是給了系統一次機會來修正導致調用失敗的錯誤。
-
Open:打開狀態(斷路器打開),所有請求都會被降級。Hystix會對請求情況計數,當一定時間內失敗請求百分比達到閾值,則觸發熔斷,斷路器會完全關閉。默認失敗比例的閾值是50%,請求次數最少不低於20次。
-
Half Open:半開狀態,open狀態不是永久的,打開後會進入休眠時間(默認是5S)。隨後斷路器會自動進入半開狀態。此時會釋放1次請求通過,若這個請求是健康的,則會關閉斷路器,否則 繼續保持打開,再次進行5秒休眠計時。
下面結合一張小圖,給大家說一下:
大致就是這麼一張圖,還有一個圖,結合一起來說。
(1)用戶對訂單系統發起訪問,訂單系統調用遠程的微服務。調用過程可能發生一些意外,導致熔斷。
(2)起初斷路器是關閉的狀態,放行所有的請求,如果調用某一個接口,一直失敗。這裏有一個閾值(比如20次,50%以上的失敗率)。那麼斷路器的狀態就變成了打開。否則還是關閉。
(3)一旦斷路器打開,所有請求這個接口的請求,均降級處理。這裏打開狀態有一個時間,如果狀態超過了是這個時間,就自動調用一次對應微服務的接口,如果成功,狀態就變成關閉。否則還是打開,再等一段時間測試。
八、隔離策略
- 線程池隔離策略:使用一個線程池來存儲當前的請求,線程池對請求作處理,設置任務返回處理超時時間,堆積的請求堆積入線程池隊列。這種方式需要爲每個依賴的服務申請線程池,有一定的資 源消耗,好處是可以應對突發流量(流量洪峯來臨時,處理不完可將數據存儲到線程池隊裏慢慢處 理)
- 信號量隔離策略:使用一個原子計數器(或信號量)來記錄當前有多少個線程在運行,請求來先判斷計數器的數值,若超過設置的最大線程個數則丟棄改類型的新請求,若不超過則執行計數操作請求來計數器+1,請求返回計數器-1。這種方式是嚴格的控制線程且立即返回模式,無法應對突發流量(流量洪峯來臨時,處理的線程超過數量,其他的請求會直接返回,不繼續去請求依賴的服務)
配置文件配置:
- hystrix.command.default.execution.isolation.strategy : 配置隔離策略(ExecutionIsolationStrategy.SEMAPHORE 信號量隔離、ExecutionIsolationStrategy.THREAD 線程池隔離 )
-
hystrix.command.default.execution.isolation.maxConcurrentRequests : 最大信號量上限
源碼:[email protected]:Zesystem/springclouddemohystrix.git