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便可查看