1.Spring Cloud Hystrix簡介
(1).分散式問題
複雜分散式體系結構中的應用程式有數十個依賴關係,每個依賴關係在某些時候將不可避免地失敗。
多個微服務之間呼叫的時候,假設微服務A呼叫微服務B和微服務C,微服務B和微服務C又呼叫其它的微服務,這就是所謂的“扇出”。如果扇出的鏈路上某個微服務的呼叫響應時間過長或者不可用,對微服務A的呼叫就會佔用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”.
對於高流量的應用來說,單一的後端依賴可能會導致所有伺服器上的所有資源都在幾秒鐘內飽和。比失敗更糟糕的是,這些應用程式還可能導致服務之間的延遲增加,備份佇列,執行緒和其他系統資源緊張,導致整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關係的失敗,不能取消整個應用程式或系統。
備註:一般情況對於服務依賴的保護主要有3中解決方案:
①.熔斷模式:這種模式主要是參考電路熔斷,如果一條線路電壓過高,保險絲會熔斷,防止火災。放到我們的系統中,如果某個目標服務呼叫慢或者有大量超時,此時,熔斷該服務的呼叫,對於後續呼叫請求,不在繼續呼叫目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復呼叫。
②.隔離模式:這種模式就像對系統請求按型別劃分成一個個小島的一樣,當某個小島被火少光了,不會影響到其他的小島。例如可以對不同型別的請求使用執行緒池來資源隔離,每種型別的請求互不影響,如果一種型別的請求執行緒資源耗盡,則對後續的該型別請求直接返回,不再呼叫後續資源。這種模式使用場景非常多,例如將一個服務拆開,對於重要的服務使用單獨伺服器來部署,再或者公司最近推廣的多中心。
③.限流模式:上述的熔斷模式和隔離模式都屬於出錯後的容錯處理機制,而限流模式則可以稱為預防模式。限流模式主要是提前對各個型別的請求設定最高的QPS閾值,若高於設定的閾值則對該請求直接返回,不再呼叫後續資源。這種模式不能解決服務依賴的問題,只能解決系統整體資源分配問題,因為沒有被限流的請求依然有可能造成雪崩效應。
(2).Hystrix斷路器
Hystrix是一個用於處理分散式系統的延遲和容錯的開源庫,在分散式系統裡,許多依賴不可避免的會呼叫失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分散式系統的彈性。
斷路器:本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者丟擲呼叫方無法處理的異常,這樣就保證了服務呼叫方的執行緒不會被長時間、不必要地佔用,從而避免了故障在分散式系統中的蔓延,乃至雪崩。
(3).Hystrix作用
[1].服務降級
Hystrix服務降級,其實就是執行緒池中單個執行緒障處理,防止單個執行緒請求時間太長,導致資源長期被佔有而得不到釋放,從而導致執行緒池被快速佔用完,導致服務崩潰。
Hystrix能解決如下問題:
①.請求超時降級,執行緒資源不足降級,降級之後可以返回自定義資料
②.執行緒池隔離降級,分散式服務可以針對不同的服務使用不同的執行緒池,從而互不影響
③.自動觸發降級與恢復
④.實現請求快取和請求合併
[2].服務熔斷
熔斷模式:這種模式主要是參考電路熔斷,如果一條線路電壓過高,保險絲會熔斷,防止火災。放到我們的系統中,如果某個目標服務呼叫慢或者有大量超時,此時,熔斷該服務的呼叫,對於後續呼叫請求,不在繼續呼叫目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復呼叫。
[3].服務限流
限流模式主要是提前對各個型別的請求設定最高的QPS閾值,若高於設定的閾值則對該請求直接返回,不再呼叫後續資源。這種模式不能解決服務依賴的問題,只能解決系統整體資源分配問題,因為沒有被限流的請求依然有可能造成雪崩效應。
[4].接近實時的監控
(4).官方文件
https://github.com/Netflix/Hystrix/wiki/How-To-Use
2.服務熔斷
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制。當扇出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的呼叫,快速返回"錯誤"的響應資訊。當檢測到該節點微服務呼叫響應正常後恢復呼叫鏈路。在SpringCloud框架裡熔斷機制通過Hystrix實現。Hystrix會監控微服務間呼叫的狀況,當失敗的呼叫到一定閾值,預設是5秒內20次呼叫失敗就會啟動熔斷機制。熔斷機制的註解是@HystrixCommand。
(1).建立工程
新建microservicecloud-provider-dept-hystrix-8001
將microservicecloud-provider-dept-8001下的package和applicaiton.yml配置檔案複製到microservicecloud-provider-dept-hystrix-8001上。
(2).配置pom檔案
修改部分:
<!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
完整部分:
<?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>microservicecloud</artifactId> <groupId>com.hosystem</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>microservicecloud-provider-dept-hystrix-8001</artifactId> <dependencies> <!-- 引入自己定義的api通用包,可以使用Dept部門Entity --> <dependency> <groupId>com.hosystem</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </dependency> <!-- 將微服務provider側註冊進eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- actuator監控資訊完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- 修改後立即生效,熱部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> </dependencies>
</project> |
(3).修改applicaiton.yml
server: port: 8001
mybatis: config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置檔案所在路徑 type-aliases-package: com.hosytem.springcloud.entities # 所有Entity別名類所在包 mapper-locations: - classpath:mybatis/mapper/**/*.xml # mapper對映檔案
#name spring.application.name=microservicecloud-dept 很重要很重要很重要 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource # 當前資料來源操作型別 driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包 url: jdbc:mysql://192.168.188.188:3306/cloudDB01 # 資料庫名稱 username: root password: 123456 dbcp2: min-idle: 5 # 資料庫連線池的最小維持連線數 initial-size: 5 # 初始化連線數 max-total: 5 # 最大連線數 max-wait-millis: 200 # 等待連線獲取的最大超時時間
eureka: client: #客戶端註冊進eureka服務列表內 service-url: # defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: microservicecloud-dept8001-hystrix #自定義服務名稱資訊 prefer-ip-address: true #訪問路徑可以顯示IP地址 info: app.name: hosystem-microservicecloud company.name: www.hosystem.com build.artifactId: $project.artifactId$ build.version: $project.version$ |
(4).修改DeptController
package com.hosystem.springcloud.controller;
import com.hosystem.springcloud.entities.Dept; import com.hosystem.springcloud.service.DeptService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController public class DeptController {
@Autowired private DeptService service = null;
@RequestMapping(value="/dept/get/{id}",method=RequestMethod.GET) //一旦呼叫服務方法失敗並丟擲了錯誤資訊後,會自動呼叫@HystrixCommand標註好的fallbackMethod呼叫類中的指定方法 @HystrixCommand(fallbackMethod = "processHystrix_Get") public Dept get(@PathVariable("id") Long id) { Dept dept = this.service.get(id); if(null == dept) { throw new RuntimeException("該ID:"+id+"沒有沒有對應的資訊"); } return dept; }
public Dept processHystrix_Get(@PathVariable("id") Long id) { return new Dept().setDeptno(id) .setDname("該ID:"+id+"沒有沒有對應的資訊,null--@HystrixCommand") .setDb_source("no this database in MySQL"); } } |
(5).修改主啟動類
修改DeptProvider8001_App主啟動類。
package com.hosystem.springcloud;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication @EnableEurekaClient //本服務啟動後會自動註冊進eureka服務中 @EnableDiscoveryClient @EnableCircuitBreaker//對hystrixR熔斷機制的支援 public class DeptProvider8001_Hystrix_App { public static void main(String[] args) { SpringApplication.run(DeptProvider8001_Hystrix_App.class, args); } } |
(6).測試
[1].啟動eureka7001、eureka7002、eureka7003
[2].啟動DeptProvider8001_Hystrix_App
[3].啟動microservicecloud-consumer-dept-80
[4].訪問
http://localhost/consumer/dept/get/112 |
3.服務降級
整體資源不足於應對當前的困難,則選擇性關掉某些服務,優先提供重要的服務。
服務降級處理是在客戶端實現完成的,與服務端沒有關係。
(1).修改microservicecloud-api
修改microservicecloud-api工程,根據已經有的DeptClientService介面新建一個實現了FallbackFactory介面的類DeptClientServiceFallbackFactor。
package com.hosystem.springcloud.service;
import com.hosystem.springcloud.entities.Dept; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component;
import java.util.List;
@Component//不要忘記新增,不要忘記新增 public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept get(long id) { return new Dept().setDeptno(id) .setDname("該ID:"+id+"沒有沒有對應的資訊,Consumer客戶端提供的降級資訊,此刻服務Provider已經關閉") .setDb_source("no this database in MySQL"); } @Override public List<Dept> list() { return null; } @Override public boolean add(Dept dept) { return false; } }; } } |
(2).修改DeptClientService介面
修改microservicecloud-api工程,DeptClientService介面在註解@FeignClient中新增fallbackFactory屬性值。
package com.hosystem.springcloud.service;
import com.hosystem.springcloud.entities.Dept; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
//@FeignClient(value = "MICROSERVICECLOUD-DEPT") @FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory=DeptClientServiceFallbackFactory.class) public interface DeptClientService { @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET) public Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list",method = RequestMethod.GET) public List<Dept> list();
@RequestMapping(value = "/dept/add",method = RequestMethod.POST) public boolean add(Dept dept); } |
(3).mvn clean和mvn install
對microservicecloud-api工程進行mvn clean install
(4).修改microservicecloud-consumer-dept-feign
microservicecloud-consumer-dept-feign工程修改YML
server: port: 80 feign: hystrix: enabled: true eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ |
(5).測試
[1].啟動eureka7001、eureka7002、eureka7003
[2].啟動microservicecloud-provider-dept-8001
[3].啟動microservicecloud-consumer-dept-feign
[4].訪問測試
①.正常測試
http://localhost/consumer/dept/get/1 |
②.關閉服務測試
關閉微服務microservicecloud-provider-dept-8001。
http://localhost/consumer/dept/get/1 |
此時服務端provider已經down了,但是我們做了服務降級處理,讓客戶端在服務端不可用時也會獲得提示資訊而不會掛起耗死伺服器。
4.服務監控hystrixDashboard
除了隔離依賴服務的呼叫以外,Hystrix還提供了準實時的呼叫監控(Hystrix Dashboard),Hystrix會持續地記錄所有通過Hystrix發起的請求的執行資訊,並以統計報表和圖形的形式展示給使用者,包括每秒執行多少請求多少成功,多少失敗等。Netflix通過hystrix-metrics-event-stream專案實現了對以上指標的監控。Spring Cloud也提供了Hystrix Dashboard的整合,對監控內容轉化成視覺化介面。
(1).新建工程
新建工程microservicecloud-consumer-hystrix-dashboard
(2).修改pom
修改部分:
<!-- hystrix和 hystrix-dashboard相關--> <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> |
完整部分:
<?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>microservicecloud</artifactId> <groupId>com.hosystem</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>microservicecloud-consumer-hystrix-dashboard</artifactId> <dependencies> <!-- 自己定義的api --> <dependency> <groupId>com.hosystem</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<!-- 修改後立即生效,熱部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
<!-- Ribbon相關 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
<!-- feign相關 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
<!-- hystrix和 hystrix-dashboard相關--> <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> </dependencies> </project> |
(3).配置applicaiton.yml
server: port: 9001 |
(4).配置主啟動類
新建主啟動類DeptConsumer_DashBoard_App,然後新增註解@EnableHystrixDashboard.
package com.hosystem.springcloud;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication @EnableHystrixDashboard public class DeptConsumer_DashBoard_App { public static void main(String[] args) { SpringApplication.run(DeptConsumer_DashBoard_App.class,args); } } |
(5).配置provider監控依賴
所有Provider微服務提供類(8001/8002/8003)都需要監控依賴配置.
<!-- actuator監控資訊完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> |
(6).啟動專案
[1].啟動dashboard
啟動microservicecloud-consumer-hystrix-dashboard該微服務監控消費端。
http://localhost:9001/hystrix |
[2].啟動eureka
啟動eureka7001、eureka7002、eureka7003
[3].啟動hystrix
啟動microservicecloud-provider-dept-hystrix-8001
(7).監控測試
多次重新整理http://localhost:8001/dept/get/1,然後通過Hystrix Dashboard檢視。
[1].填寫監控地址
http://localhost:8001/hystrix.stream |
Delay:該引數用來控制伺服器上輪詢監控資訊的延遲時間,預設為2000毫秒,可以通過配置該屬性來降低客戶端的網路和CPU消耗。
Title:該引數對應了頭部標題Hystrix Stream之後的內容,預設會使用具體監控例項的URL,可以通過配置該資訊來展示更合適的標題。
[2].監控結果
7色、1圈、1線
實心圓:共有兩種含義。它通過顏色的變化代表了例項的健康程度,它的健康度從綠色<黃色<橙色<紅色遞減。該實心圓除了顏色的變化之外,它的大小也會根據例項的請求流量發生變化,流量越大該實心圓就越大。所以通過該實心圓的展示,就可以在大量的例項中快速的發現故障例項和高壓力例項
曲線:用來記錄2分鐘內流量的相對變化,可以通過它來觀察到流量的上升和下降趨勢。
[3].複雜監控結果
參考文件: