引入:为什么要使用熔断器
雪崩效应
在电影里面经常出现的场景,在冰山雪地不要大声呼喊,因为声音的震动会导致雪球的滑落,然后引起连锁反应导致整个雪山的崩塌这就是生活中的雪崩。在微服务里面也是一样,服务的调用非常复杂的 ,一个请求往往需要很多的微服务共同完成,可能会形成很长的服务调用链,在整个服务调用链中,某一个服务发生故障会导致调用它的服务跟着异常,然后导致整个调用链调用的异常,甚至导致整个微服务瘫痪 , — 这就是雪崩效应。如下图:
Hystrix介绍
Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题,如雪崩。
Hystrix是处理依赖隔离的框架,将出现故障的服务通过熔断、降级等手段隔离开来
,这样不影响整个系统的主业务(比如你得了传染病是不是要把你关起来隔离呢),同时也是可以帮我们做服务的治理和监控
。
Hystrix的英文是豪猪,中文翻译为 熔断器,其思想来源于我们家里的保险开关,当家里出现短路,保险开关及时切掉电路,保证家里人员的安全,其目的就是起保护作用。
Hystrix其设计原则如下:
- 防止单个服务异常导致整个微服务故障。
- 快速失败,如果服务出现故障,服务的请求快速失败,线程不会等待。
- 服务降级,请求故障可以返回设定好的二手方案数据(兜底数据)。
- 熔断机制,防止故障的扩散,导致整个服务瘫痪。
- 服务监控,提供了Hystrix Bashboard仪表盘,实时监控熔断器状态
Hystrix的功能
资源隔离(流控,限流)
资源隔离包括线程池隔离和信号量隔离
,作用是限制调用分布式服务的资源使用
,某一个调用的服务出现问题不会影响其他服务调用 ,这里可以简单的理解为资源隔离就是限制请求的数量
。
线程池隔离:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
信号量隔离:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
线程池和信号量对比:
线程池 | 线程池 | 信号量 |
---|---|---|
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限) |
服务熔断
熔断机制是对服务链路的保护机制,如果链路上的某个服务不可访问,调用超时,发生异常等,服务会触发降级返回托底数据,然后熔断服务的调用(失败率达到某个阀值服务标记为短路状态),当检查到该节点能正常使用时服务会快速恢复。
简单理解就是当服务不可访问了或者服务器报错了或者服务调用超过一定时间没返回结果,就立马触发熔断机制配合降级返回预先准备的兜底数据返回,不至于长时间的等待服务的相应造成大量的请求阻塞,也不至于返回一些错误信息给客户端,而是返回一些兜底数据。
降级机制
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
简单理解就是服务降级就是当服务因为网络故障,服务器故障,读取超时等原因造成服务不可达的情况下返回一些预先准备好的数据给客户端。
在生活中服务降级到处可见,如在系统故障我们会返回友好的提示“服务暂时不可用”,或者如淘宝双11期间退款服务,留言服务暂时不可用,这个就是为了保证正常的购物流程相关服务没问题,然后人为的关闭一些不重要的服务,配合降级返回一些托底数据返回给用户(比如返回友好的提示信息“暂时不能退款”)。
缓存
提供了请求缓存、请求合并实现 , 在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果。
Hystrix工作机制
正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时达到设定阈值,电路被打开进入熔断状态(Open),这时请求这个服务会触发快速失败(立马返回兜底数据不要让线程死等),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后(withCircuitBreakerSleepWindowInMilliseconds=5s),保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态。
编码实战
这里不用导入依赖和写启动类,因为netflix-eureka-client下有该依赖,启动类就用前面OpenFeign里面的启动类PayApp;这里是直接在前面中的项目中用的。
1.在UserClient接口中进行修改
// @FeignClient - 标记该接口是feign的客户端接口,用来调用用户服务
// userServer被调用服务的服务名
// fallbackFactory表示如果调用失败或发生错误,将使用UserClientFallbackFactory类中的降级逻辑进行处理
@FeignClient(value = "userServer",fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
// 这里的方法与要调用服务的controller中的方法一样
@GetMapping("/user/{id}")
User getById(@PathVariable("id")Long id);
}
2.编写降级类UserClientFallbackFactory
@Component // 交给spring容器进行管理
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
throwable.printStackTrace(); // 在控制台打印异常信息
return new UserClient() {
@Override
public User getById(Long id) {
return new User(1L,"","服务调用失败,请重试!");
}
};
}
}
OpenFeign使用Hystrix
1.开启Hystrix
通过feign.hystrix.enabled=true
开启Hystrix
feign:
hystrix:
enabled: true #开启熔断支持
ribbon:
ReadTimeout: 1000
SocketTimeout: 1000
ConnectTimeout: 1000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
2.Fiegn接口熔断-fallback方式
服务通过Feign接口调用异常或超时需要触发降级,返回托底数据。这里有两种方式,分别是通过@FeignClient(fallback=…) ,以及@FeignClient(fallbackFactory=…) 来指定托底类,区别在于通过fallback的方式编写的托底是没办法打印出异常日志的 ,而fallbackFactory方式是可以打印出异常日志, 我们先来看第一种写法:
@FeignClient(value = "user-server",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
//订单服务来调用这个方法 http://localhost:1020/user/10
// @GetMapping(value = "/user/{id}" )
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
User getById(@PathVariable("id")Long id);
}
提示: fallback = UserFeignClientFallback.class : 该类是当前接口的实现类 ,也是托底数据所在的处理类
托底实现类:
//让Spring扫描到该托底类
@Component
public class UserFeignClientFallback implements UserFeignClient {
//日志打印器
private Logger log = LoggerFactory.getLogger(UserFeignClientFallback.class);
@Override
public User getById(Long id) {
log.info("用户服务不可用");
//托底数据
return new User(-1l,"无此用户","用户服务不可用");
}
}
提示:注意,这里托底类需要交给Spirng管理,类上需要打 @Component 注解 , 托底类需要实现 Feign接口,复写接口中的方法作为托底方法返回托底数据。当Fiegn调用失败就会以托底方法返回的结果返回给用户
3.Fiegn接口熔断-fallbackFactory方式
使用fallbackFactory属性,使用工厂方式指定托底
// @FeignClient - 标记该接口是feign的客户端接口,用来调用用户服务
// userServer被调用服务的服务名
// fallbackFactory表示如果调用失败或发生错误,将使用UserClientFallbackFactory类中的降级逻辑进行处理
@FeignClient(value = "userServer",fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
// 这里的方法与要调用服务的controller中的方法一样
@GetMapping("/user/{id}")
User getById(@PathVariable("id")Long id);
}
托底实现类:
@Component // 交给spring容器进行管理
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
throwable.printStackTrace(); // 在控制台打印异常信息
return new UserClient() {
@Override
public User getById(Long id) {
//这里的数据是托底数据
return new User(1L,"无此用户","用户服务不可用");
}
};
}
}
小结
Hystrix熔断器是一个功能强大的容错机制,可以帮助处理分布式系统中的故障和延迟问题。它通过断路器模式、预定义的降级逻辑和超时机制,提供了可靠性、稳定性和可定制性,使得系统能够更好地应对异常情况并保持高可用性。