前言
高可用相关的技术以及架构,对于大型复杂的分布式系统,是非常重要的。而高可用架构中,非常重要的一个环节,就是如何将分布式系统中的各个服务打造成高可用的服务,从而足以应对分布式系统环境中的各种各样的问题,比如服务间的调用超时,或者服务间的调用失败,避免整个分布式系统被某个服务的故障给拖垮。而要解决这些棘手的分布式系统可用性问题,就涉及到了高可用分布式系统中的很多重要的技术,包括资源隔离,限流与过载保护,熔断,优雅降级,容错,超时控制,监控运维,等等。
什么是断路器
“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
在Spring Cloud中使用了Hystrix 来实现断路器的功能,Hystrix能够完美的解决分布式系统架构中,打造高可用服务面临的一系列技术难题,包括资源隔离,限流与过载保护,熔断,优雅降级,容错,超时控制,监控运维等等。
雪崩效应
如果单个服务C出现问题,服务B调用服务C就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务B瘫痪。服务与服务之间的依赖性,故障会传播,那么服务A调用服务B也会瘫痪,这样会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
如何防止血崩效应的发生
spring cloud Hystrix是防雪崩利器,主要解决方案包括服务降级、依赖隔离、服务熔断、服务监控等功能
服务降级
概念
当服务器压力剧增的时候,根据当前业务情况以及流量,对一些服务和页面有策略的降级,以此缓解服务器资源的压力以保障核心任务的正常运行,同时也保证了大部分客户能得到正常的响应。
核心
优先核心服务,给核心服务不可用或若可用
自动降级使用场景
(1)、超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
(2)、失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
(3)、故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
(4)、限流降级:当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。
实现
1、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2、启动类添加注解@EnableCircuitBreaker
3、使用@HystrixCommand注解指定回调方法,即降级方法。
@RestController
public class HystrixController {
@GetMapping("/getProductInfoList")
//使用hystrix 实现容错机制,请求失败后执行Back,称之为“服务降级”
@HystrixCommand(fallbackMethod = "fallback")
public String getProductInfoList(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject ("http://127.0.0.1:8005/product/list",Arrays.asList("12222222222000"),String.class);
}
//回退方法
private String fallback(){
return "太拥挤了,请稍后再试~~";
}
}
4、当目标服务出错时,会触发降级调用fallback。降级不一定就是发生在目标服务不能提供正常服务了,触发降级;也可以用到自己服务内部的降级,比如抛出一个异常,触发降级,这样的话如果并发数太高,数据库连接数太多了,就可以抛一个异常,这样来触发降级。
5、fallback也可以调用在类上,使用@DefaultProderties注解,如果在每个方法上都调用一次,会需要写多次
@RestController
@DefaultProderties(defaultFallback = "defaultFallback")
public class HystrixController {
@GetMapping("/getProductInfoList")
public String getProductInfoList(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/list",Arrays.asList("12222222222000"),String.class);
}
private String defaultfallback(){
return "太拥挤了,请稍后再试~~";
}
}
依赖隔离
类似于docker的舱壁模式,实现进程隔离,保证容器之间互不影响。Hystrix自动实现依赖隔离,是线程池隔离。
线程池隔离:使用一个线程池来存储当前的请求,也就是说发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。但是有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理,最主要的是Hystrix自动实现了依赖隔离。
服务熔断
概念
服务熔断和保险丝的使用原理很像,保险系在电压达到一定值后,会出现保险丝为控制风险,保护电器而自动烧掉,切断电源。相应的,服务熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。
熔断机制
可以看到服务熔断机制有三个状态:熔断关闭,熔断打开,熔断半开。
- 但请求成功或者失败次数低于开关阈值时,熔断处于关闭状态;
- 当请求失败次数达到开关阈值,熔断器会变成变成打开状态,打开状态会设置一定时间的休眠窗,比如10秒,10秒内服务降级逻辑会成为主逻辑,10秒后,熔断器会处于半开状态;
- 半开状态会释放一部分请求,如果释放的请求成功,熔断器关闭,服务恢主逻辑复,如果释放的请求依然失败,熔断器继续进入打开状态,休眠期计时。
实现
@RestController
@DefaultProderties(defaultFallback = "defaultFallback")
public class HystrixController {
//熔断设置
@HystrixCommand(commandProperties = {
@HystrixProerty(name="circuitbreaker.enable",value="true"), //设置熔断
@HystrixProerty(name="circuitbreaker.requestVolumethreshold",value="10"), //滚动窗口,熔断器最小请求次数
@HystrixProerty(name="circuitbreaker.sleepWindowInMilliseconds",value="10000"), //休眠窗,设置10秒,休眠窗内降级逻辑执行
@HystrixProerty(name="circuitbreaker.errorThreshouldPercentage",value="60") //熔断器开关阈值,在滚动窗口内如果失败次数高于60%,熔断器打开
})
@GetMapping("/getProductInfoList")
public String getProductInfoList(@RequestParam("number") Integer number){
if(number % 2== 0){
return "success";
}
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject("http://127.0.0.1:8005/product/list",Arrays.asList("12222222222000"),String.class);
}
private String fallback(){
return "太拥挤了,请稍后再试~~";
}
}
熔断参数
- Circuit Breaker:断路器
- sleepWindowInMilliseconds:10s;表示10s秒的休眠时间窗,10s后休眠时间窗到期,熔断器设置为半打开状态,如果请求恢复,设置为关闭状态,如果请求没有恢复,则设置为打开状态
- **requestVolumethreshold:**断路器的最小请求数
- **errorThreshouldPercentage:**断路器打开的错误百分比条件,在时间滚动窗口中如果发生了10次调用,如果有7次是错误的,超过了60%,这个时候断路器就会设置为打开状态
demo说明
熔断器工作流程图
降级与熔断对比
共性
目的: 目的一致,都是从系统的可用性、可靠性着想。放了防止系统的整体缓慢甚至奔溃而采用的技术手段。
最终表现: 表现类似,最终都是给用户一种当前服务不可用或者不可达的感觉
粒度: 大多都是在服务级别,当然也有一些在持久层层面的应用
自治: 基本都是靠系统达到某一临界条件时,实现自动的降级与熔断,人工降级并不是那么稳妥。
区别
触发原因: 服务熔断一般指某个服务的下游服务出现问题时采用的手段,而服务降级一般是从整体层面考虑的。
管理目标层次: 熔断是一种框架级的处理,每一个微服务都需要。而降级一般需要对业务有层级之分,降级一般都是从外围服务开始的。
实现方式: 代码级别实现有差异
服务监控
Hystrix还提供给我们一个监控功能Hystrix-dashboard,借助hystrix-dashboard可对监控进行图形化展示,就能实时的观察我们的服务调用情况。
1、引入hystrix-dashboard依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
2、启动类配置@EnableHystrixDashboard注解(同时配置@EnableDiscoveryClient向注册中心注册,方便管理)
3、配置文件设置端口
server:
port: 8087
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
spring:
application:
name: hystrix-dashboard
4、监控界面如下