Netflix的創造了一個調用的庫Hystrix實現了斷路器圖案。在微服務架構中,一般有多層服務調用。java
當其中有一個系統有延遲, 它會阻塞整個用戶請求web
在高流量的狀況下,一個後端依賴項的延遲可能致使全部服務器上的全部資源在數秒內飽和(即服務雪崩)spring
熔斷機制是對雪崩效應的一種微服務鏈路保護機制sql
熔斷機制的註解是@HystrixCommandapache
咱們是在服務端就行服務熔斷的, 所以在provider中進行操做編程
<!--Hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency>
package com.wang.springcloud.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.wang.springcloud.pojo.Dept; import com.wang.springcloud.service.DeptService; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; 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 java.util.List; //提供RestFul服務! @RestController @ApiModel("Provider Controller") public class DeptController { @Autowired private DeptService deptService; //註冊DiscoveryClient, 注意此時要導入的包是SpringCloud的 //獲取一些配置的信息, 獲得具體的微服務 @Autowired private DiscoveryClient client; @ApiOperation("經過部門編號得到一個部門的信息") @GetMapping("/dept/get/{id}") //只要失敗, 調用對應的方法 @HystrixCommand(fallbackMethod = "hystrixGet") public Dept get(@PathVariable("id") @ApiParam("部門的id") Long id){ Dept dept = deptService.queryById(id); //若是id不存在, 會返回null, 這裏拋出異常 if (dept == null) { throw new RuntimeException("id => " + id + " , 不存在該用戶, 或者信息沒法找到!"); } return dept; } //備選方法 ==> 當查詢的id不存在, 建立對應id的對象, name字段放入提示信息, 失敗時返回咱們建立的對象! public Dept hystrixGet(@PathVariable("id") @ApiParam("部門的id") Long id){ //這裏能夠用鏈式編程, 是由於咱們在pojo的lombok中開啓了鏈式編程的支持 return new Dept() .setDeptno(id) .setDname("id => " + id + " , 沒有對應的信息, null ---- @Hystrix") .setDb_source("This database is not exist in Mysql"); } //註冊進來的微服務, 得到一些信息(獲得配置文件中的info的信息) @ApiOperation("微服務的信息") @GetMapping("/dept/discovery") public Object discovery() { //獲取微服務列表的清單 List<String> services = client.getServices(); System.out.println("discovery => services: " + services); //獲得一個具體的微服務, 經過具體的微服務ID, applicationName(即爲在配置文件中配置的該SpringBoot的名字!) List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT"); for (ServiceInstance instance : instances) { System.out.println( instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri() + "\t" + instance.getServiceId() ); } //返回這個client就能夠了 return this.client; } }
注意後端
使用 @HystrixCommand 註解, 並用 fallbackMethod 屬性指定服務熔斷後調用的方法(此處即拋出異常 ==> 不熔斷的話顯示爲500)服務器
熔斷調用的方法中, 並用寫Mapping的註解, 和調用該方法的服務用同一個url架構
能夠將錯誤信息返回到對象中app
package com.wang.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 //在服務啓動後自動將該服務註冊到Eureka中 @EnableEurekaClient //服務發現, 這樣就能夠監控了 @EnableDiscoveryClient //添加對熔斷的支持(啓用斷路器) @EnableCircuitBreaker public class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class, args); } }
注意
能夠顯示服務的IP地址(最後一行設置爲true)
#Eureka配置, 配置該服務註冊到哪裏(與Server中的url地址一致) eureka: client: service-url: #向集羣發佈, 只須要向全部的Eureka發佈url就能夠了 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: springcloud-provider-dept-hystrix-8001 #修改Eureka上的默認描述信息 prefer-ip-address: true
效果以下
服務降級是發生在客戶端上的, 並且依附於feign, 咱們在使用了feign的consumer上設置
咱們在API中寫降級的工廠類, 從而能夠被其餘模塊調用到
package com.wang.springcloud.service; import com.wang.springcloud.pojo.Dept; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; import java.util.List; //降級 @Component public class DeptClientFallbackFactory implements FallbackFactory { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept queryById(Long id) { return new Dept() .setDeptno(id) .setDname("id => " + id + " 沒有對應的信息, 客戶端提供了降級的信息, 這個服務已經被關閉") .setDb_source("沒有數據"); } @Override public List<Dept> queryAll() { return null; } @Override public Boolean addDept(Dept dept) { return null; } }; } }
注意
開啓服務降級, 只須要在對應的客戶端的配置文件中啓用便可
#開啓降級feign.hystrix feign: hystrix: enabled: true
服務熔斷
服務降級
相同點
目的很一致,都是從可用性可靠性着想,爲防止系統的總體緩慢甚至崩潰,採用的技術手段;
最終表現相似,對於二者來講,最終讓用戶體驗到的是某些功能暫時不可達或不可用;
粒度通常都是服務級別,固然,業界也有很多更細粒度的作法,好比作到數據持久層(容許查詢,不容許增刪改);
自治性要求很高,熔斷模式通常都是服務基於策略的自動觸發,降級雖然說可人工干預,但在微服務架構下,徹底靠人顯然不可能,開關預置、配置中心都是必要手段;
不一樣點
觸發緣由不太同樣,服務熔斷通常是某個服務(下游服務)故障引發,而服務降級通常是從總體負荷考慮;
管理目標的層次不太同樣,熔斷實際上是一個框架級的處理,每一個微服務都須要(無層級之分),而降級通常須要對業務有層級之分(好比降級通常是從最外圍服務開始)
Hystrix提供了一個可視化的流量監控
<?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>SpringCloud</artifactId> <groupId>com.wang</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>SpringCloud-consumer-hystrix-dashboard</artifactId> <!--實體類以及Web--> <dependencies> <dependency> <groupId>com.wang</groupId> <artifactId>SpringCloud-API</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--熱部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> </dependency> <!--Ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Eureka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Hystrix Dashboard--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.6.RELEASE</version> </dependency> </dependencies> </project>
注意
server: port: 9002
注意
package com.wang.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication //開啓Hystrix Dashboard @EnableHystrixDashboard public class DeptConsumerDashboard_9002 { public static void main(String[] args) { SpringApplication.run(DeptConsumerDashboard_9002.class, args); } }
注意
注意, 微服務被Hystrix Dashboard監控須要知足如下兩點
因爲Hystrix Dashboard要求微服務註冊一個URL地址, 咱們在SpringBoot中使用 ServletRegistrationBean 類型來註冊一個Servlet的Bean
package com.wang.springcloud; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; //啓動類 @SpringBootApplication //在服務啓動後自動將該服務註冊到Eureka中 @EnableEurekaClient //服務發現, 這樣就能夠監控了 @EnableDiscoveryClient //添加對熔斷的支持(啓用斷路器) @EnableCircuitBreaker public class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class, args); } //增長一個Servlet的Bean @Bean public ServletRegistrationBean hystrixMetricsStreamServlet() { ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); registrationBean.addUrlMappings("/hystrix/actuator/hystrix.stream"); return registrationBean; } }
注意
啓動Eureka, Provider和DashBoard, 訪問dashboard下的 /hystrix, 輸入微服務的URL便可查看