複雜分佈式體系結構中的應用程序有數十個依賴關係,每一個依賴關係在某些時候將不可避免地失敗。不作任何處理的狀況下,很容易致使服務雪崩。java
對於高流量的應用來講,單一的後端依賴可能會致使全部服務器上的全部資源都在幾分鐘內飽和。比失敗更糟糕的是,這些應用程序還可能致使服務之間的延遲增長,備份隊列、線程和其它系統資源緊張,致使整個系統發生更多的級聯故障。這些都表示須要對故障和延遲進行隔離和管理,以避免由於單個依賴關係的失敗對整個應用程序或系統形成影響。mysql
「斷路器」自己是一種開關裝置,當某個服務單元發生故障以後,經過斷路器的故障監控(相似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應,而不是長時間的等待或者拋出調用方沒法處理的異常,這樣就保證了服務調用方的線程不會被長時間、沒必要要地佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。web
服務熔斷是應對服務雪崩的一種微服務鏈路保護機制。通常是某個服務故障或者異常引發,相似現實世界中的「保險絲」,當某個異常條件被觸發,直接熔斷整個服務,而不是一直等到此服務超時。spring
當扇出鏈路的某個微服務不可用或響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回「錯誤」地響應信息。 當檢測到該節點微服務調用響應正常後恢復調用鏈路。在 SpringCloud 中的熔斷機制是經過 Hystrix 實現。Hystrix 會監控微服務間的調用情況,當失敗的調用到必定閾值,缺省是 5 秒內 20 次調用失敗就會啓動熔斷機制。啓用熔斷機制的註解是 @HystrixCommand 。sql
Hystrix 是一個用於處理分佈式系統的延遲和容錯的開源庫,在分佈式系統裏,許多以來不可避免的會調用失敗,好比超時、異常等,Hystrix 可以保證在一個依賴出問題的狀況下,不會致使總體服務失敗,避免級聯故障,以提升分佈式系統的彈性。數據庫
一、複製 "microservicecloud-provider-dept-8001" 子工程更名爲 "microservicecloud-provider-dept-hystrix-8001",新增斷路器依賴:後端
<!--Hystrix 斷路器--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
二、修改微服務實例 Id:服務器
server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml # mybatis 配置文件路徑 type-aliases-package: zze.springcloud.entities # 全部 Entity 別名類所在包 mapper-locations: - classpath:mybatis/mapper/**/*.xml # mapper 映射文件 spring: application: name: microservicecloud-provider-dept # 當前微服務名稱 datasource: type: com.alibaba.druid.pool.DruidDataSource # 數據源操做類型 driver-class-name: org.gjt.mm.mysql.Driver # mysql 驅動包 url: jdbc:mysql:///springcloud_8001 # 數據庫鏈接 root username: root password: root dbcp2: min-idle: 5 # 數據庫鏈接池的最小維持鏈接數 initial-size: 5 # 初始化鏈接數 max-total: 5 # 最大鏈接數 max-wait-millis: 200 # 等待鏈接獲取的最大超時時間 eureka: client: # 將當前工程做爲 Eureka 客戶端 service-url: # 單機版 # defaultZone: http://localhost:7001/eureka # Eureka 服務端地址 defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka instance: instance-id: microservicecloud-provider-dept-hystrix-8001 prefer-ip-address: true # 訪問路徑顯示 IP info: host: ${java.rmi.server.hostname} port: ${server.port} app.name: microservicecloud-provider-dept-8001 build.artifactId: ${project.artifactId} build.version: ${project.version}
三、修改 Controller,給 get 方法添加上熔斷註解,指定回調方法:mybatis
package zze.springcloud.controller; 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.*; import zze.springcloud.entities.Dept; import zze.springcloud.service.DeptService; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/dept") public class DeptController { @Autowired private DeptService deptService; @PostMapping("/add") public boolean add(@RequestBody Dept dept) { return deptService.add(dept); } @GetMapping("/get/{id}") @HystrixCommand(fallbackMethod = "fallback_get") // fallbackMethod 指定回調方法名,get 方法異常時會調用指定的方法 public Dept get(@PathVariable Long id) { Dept dept = deptService.get(id); if(null == dept){ throw new RuntimeException("沒有 id 爲" + id + "的這個部門"); } return dept; } /** * get 方法熔斷回調 */ private Dept fallback_get(@PathVariable Long id){ return new Dept().setDeptNo(id).setDeptName("該部門不存在").setDbSource("數據源中沒有這個部門"); } @GetMapping("/list") public List<Dept> list() { return deptService.list(); } }
四、修改主啓動類,使用註解添加 Hystrix 服務熔斷支持:架構
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker // Hystrix 熔斷功能支持 public class Application_8001 { public static void main(String[] args) { SpringApplication.run(Application_8001.class, args); } }
五、測試:
一、啓動 microservicecloud-provider-dept-hystrix-8001 服務 二、訪問 http://localhost:8001/dept/get/1 時正常返回數據 三、訪問 http://localhost:8001/dept/get/11 時執行了熔斷方法,返回了自定義的數據信息:
服務端(被調用端)操做,服務熔斷實際上相似於異常處理,讓服務在發生不可預料的異常時可控的返回一個客戶端可處理的結果。
什麼是服務降級?當服務器壓力劇增的狀況下,根據實際業務狀況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務器資源以保證核心交易正常運做或高效運做。
所謂降級,通常是從總體負荷考慮。就是當某個服務熔斷以後,對應服務將再也不被調用,此時客戶端能夠本身準備一個本地的回調,返回一個缺省值。這樣作,雖然服務水平降低了,但好歹能用,比直接掛掉要強。
服務降級主要用於什麼場景呢?當整個微服務架構總體的負載超出了預設的上限閾值或即將到來的流量預計將會超過預設的閾值時,爲了保證重要或基本的服務能正常運行,咱們能夠將一些不重要或不緊急的服務或任務進行服務的延遲使用或暫停使用。
一、修改 "microservicecloud-provider-dept-8001" 的 Controller 模擬異常狀況:
package zze.springcloud.controller; 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.*; import zze.springcloud.entities.Dept; import zze.springcloud.service.DeptService; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/dept") public class DeptController { @Autowired private DeptService deptService; @PostMapping("/add") public boolean add(@RequestBody Dept dept) { return deptService.add(dept); } @GetMapping("/get/{id}") public Dept get(@PathVariable Long id) { Dept dept = deptService.get(id); if(null == dept){ throw new RuntimeException("沒有 id 爲" + id + "的這個部門"); } return dept; } @GetMapping("/list") public List<Dept> list() { return deptService.list(); } @Autowired private DiscoveryClient discoveryClient; /** * 獲取全部註冊到 EurekaServer 的服務信息 * * @return */ @GetMapping("/discovery") public Object discovery() { Map<String, Object> map = new HashMap<>(); // 獲取全部註冊到 EurekaServer 的微服務名稱,對應 spring.application.name List<String> services = discoveryClient.getServices(); for (String service : services) { // 獲取對應服務全部實例 List<ServiceInstance> instances = discoveryClient.getInstances(service); map.put(service, instances); } return map; } }
二、修改 "microservicecloud-consumer-dept-feign" 工程,新增斷路器依賴,新增熔斷回調工廠類:
package zze.springcloud.service.fallback; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; import zze.springcloud.entities.Dept; import zze.springcloud.service.DeptClientService; import java.util.List; /** * 當 DeptClientService 的某方法調用異常時會回調下面實現 DeptClientService 的方法 */ @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).setDeptName("該部門不存在").setDbSource("數據源中沒有這個部門"); } @Override public List<Dept> list() { return null; } @Override public boolean add(Dept dept) { return false; } }; } }
三、修改 Feign 客戶端接口類,指定回調工廠類:
package zze.springcloud.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import zze.springcloud.entities.Dept; import zze.springcloud.service.fallback.DeptClientServiceFallBackFactory; import java.util.List; /* value 表示要調用的微服務名稱 fallbackFactory 指定調用失敗時執行的熔斷回調工廠類 */ @FeignClient(value = "microservicecloud-provider-dept",fallbackFactory = DeptClientServiceFallBackFactory.class) @RequestMapping("/dept") public interface DeptClientService { @GetMapping("/get/{id}") public Dept get(@PathVariable("id") Long id); @GetMapping("/list") public List<Dept> list(); @PostMapping("/add") public boolean add(Dept dept); }
四、修改配置文件,啓用 Feign 的熔斷功能:
server: port: 80 eureka: client: service-url: defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka instance: instance-id: microservicecloud-provider-dept prefer-ip-address: true # 訪問路徑顯示 IP spring: application: name: microservicecloud-consumer-dept # 啓用 feign 的熔斷功能 feign: hystrix: enabled: true
五、測試:
一、啓動 700一、700二、7003 Eureka 集羣 二、啓動 Provider 服務 microservicecloud-provider-dept-8001 三、啓動 Consumer 服務 microservicecloud-consumer-dept-80 四、訪問 http://localhost/consumer/dept/get/1 ,正常返回信息 五、關閉 8001 Provider 服務,再次訪問 http://localhost/consumer/dept/get/1 ,返回熔斷回調工廠類中自定義的信息:
消費端(調用端)操做,從上面測試結果中能夠看到即便 Provider 服務掛了,Consumer 也能夠返回可處理的數據而不是隨之掛掉。
HystrixDashboard 是 Hystrix 提供的準實時的調用監控,Hystrix 會持續地記錄全部經過 Hystrix 發起的請求的執行信息,並以統計報表和圖形的形式展現給用戶,包括每秒執行多少次請求、多少成功、多少失敗等。Netflix 經過 hystrix-metrics-events-stream 項目實現了對以上指標的監控。SpringCloud 也提供了 HystrixDashboard 的整合,對監控內容轉化成可視化界面。
一、新建名爲 "microservicecloud-consumer-hystrix-dashboard" 的子工程做爲監控微服務工程,添加相關依賴:
<!--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>
二、新建配置文件,配置佔用端口:
server:
port: 9001
三、新建主啓動類,使用註解添加監控支持:
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard // 開啓監控支持 public class Application_9001 { public static void main(String[] args) { SpringApplication.run(Application_9001.class, args); } }
四、保證全部的 Provider 服務都添加了監控依賴:
<!--監控信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
五、測試監控服務自身是否正常:
啓動 microservicecloud-consumer-hystrix-dashboard 服務,訪問 http://localhost:9001/hystrix 若是出現下面頁面則測試經過:
六、監控測試:
一、啓動 700一、700二、7003 Eureka 集羣 二、啓動 microservicecloud-provider-dept-hystrix-8001 帶有熔斷功能的 Provider 服務 三、啓動 microservicecloud-consumer-dept-80 消費者服務 四、啓動 microservicecloud-consumer-hystrix-dashboard 監控服務 五、訪問監控服務提供的可視化 web 頁,http://localhost:9001/hystrix: 六、隨後出現以下界面:
七、訪問被監控的服務,會發現曲線會根據訪問頻率發生變化