复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免地失败。不作任何处理的状况下,很容易致使服务雪崩。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: 六、随后出现以下界面:
七、访问被监控的服务,会发现曲线会根据访问频率发生变化