一: 使用场景
上一文中我们解析到关于服务降级的知识,了解到服务降级实际上是为了当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。 说白了,就是尽可能的把系统资源让给优先级高的服务。对于一些优先级不高的服务调用时直接返回的"兜底"方案,给使用者返回一个友好的提示而不是程序的错误信息,从而提高用户的使用体验。
但是,在平常的工作中,可能某个服务只是在某段时间内因为网络等问题出现了故障,但是过了这段时间服务就可以正常使用,此时,如果访问还是继续返回服务降级的友好提示的话,显然是不合理的,我们想如果服务正常了,那么它就可以自动切换到正常逻辑处理中,而不需要人为干预,那么此时就设计到了"服务熔断"这个知识点。
二: 什么是服务熔断
(一)定义: 熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路中的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断改节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路
(二)实现方式:
1、在SpringCloud分布式解决方案中,通过Hystrix实现,当开启熔断器后,Hystrix组件会对所有的请求进行监控,如果一段时间内(默认是10s)请求某个服务失败率达到指定的值(默认是50%),则会熔断对该服务的请求,直接返回熔断指定的fallback兜底方法。
2、如果该服务还继续有请求过来,服务熔断一段时间后(默认是5秒),此时熔断的断路器会变成半开的状态,允许某个请求走正常的处理逻辑,如果服务正常处理逻辑没有出错,这该服务的熔断器关闭,后面的所有请求都可以走服务的正常逻辑,如果请求还是失败,则继续制定fallback中的制定方法,且熔断时间重新计时。
(二)与服务降级的区别:
- 触发原因不一样: 服务熔断是因为扇出链路中某个服务调用超时或者长时间没有响应时,为防止资源被消耗完,从而触发了熔断。
- 服务降级则是从系统的整体考虑,当服务器压力太大时,选择将资源给优先级高的服务(这个可以通过我们自己进行策略配置),从而保障了核心服务的稳定运行,优先级低得则可以直接返回兜底方法fallback,给用户更好的使用体验。
- 使用范围不一样: 因为服务熔断是考虑服务之间之间调用出现雪崩效应,所以一般是所有的微服务都需要配置,服务降级则是从系统整体的稳定性出发,所以一般是考虑在客户端进行处理,从而提高使用者的体验,保证核心服务的稳定运行。
- 服务熔断能够通过熔断状态的改变,当检测到该节点微服务调用响应正常后,恢复调用链路。
三: 服务熔断的状态分析
(一)使用: 服务熔断和服务降级都是使用Hystrix中的HystrixCommand注解,通过制定fallback属性作为兜底方法和commandProperties属性制定对应的配置来显示熔断和服务降级
(二)代码(代码直接放在指定的方法中即可):
代码语言:javascript复制// 更多属性直接在HystrixProperty类中查询并添加
@HystrixCommand(fallbackMethod="方法名称(参数要和当前方法一致)",commandProperties={
@HystrixProperty(name="circuitBreaker.enabled",value="true"), // 开启断路器
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="10"), // 窗口期内的请求总次数
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="1000"), // 时间休眠窗
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="60") // 失败率到达这个指标都开启断路器
// 解释:在1000毫秒内,如果请求总数超过10次,且失败率达到百分之60以上,则开启断路器
})
(三)参数解析:
1、快照时间窗: 断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间,默认为最近的10秒
2、请求总数阈值:在快照时间窗时间内,必须满足请求总数阈值才有资格熔断,默认是10,以为着在10秒内,如果hystrix的调用次数不足10次,即使所有的请求都超时或者其他原因失败,断路器都不会打开。
3、错误百分比阈值: 当请求总数在快照时间窗内超过了阈值,比如发生了调用30次,如果30次中有15次发生了超时,则就是超过了50%的错误百分比,在默认设定50%的阈值情况下,这时候会将断路器打开。
4、当开启断路器时,所以得请求都不会进行转发,而是直接进入服务降级指定的fallback方法中,一段时间后(默认是5秒),这个时间断路器时半开状态,会让其中的一个请求进行转发,如果成功,断路器会关闭,如果失败,则继续开启,且熔断时间重新计时。
(三)服务熔断状态图:
-----------熔断类型-----------------
1、熔断关闭: 不会对服务进行熔断
2、熔断打开: 请求不再调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
3、熔断半开: 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
(三)服务熔断案例完整代码
代码语言:javascript复制// 配置文件
server:
port: 8004
spring:
application:
name: cloud-payment-hystrix-breaker8004
eureka:
client:
fetch-registry: true # 从eureka中获取注册的信息
register-with-eureka: true # 注册到eureka中
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ # eureka注册的地址
#defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ # eureka注册的地址
instance:
prefer-ip-address: true # 鼠标移动上去显示ip地址
instance-id: payment8003 # 服务的名称
代码语言:javascript复制//依赖坐标
<dependencies>
<!-- hystrix服务降级-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- hystrix2.0版本不包含HystrixCommand注解,所以需要引入该依赖-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
代码语言:javascript复制// 主启动类
package com.elvis.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient
// 激活Hystrix
@EnableHystrix
public class HystrixBreaker8004 {
public static void main(String[] args) {
SpringApplication.run(HystrixBreaker8004.class,args);
}
}
代码语言:javascript复制// service
package com.elvis.springcloud.service;
public interface HystrixBreakerService {
public String breakerTest(Integer number);
}
// serviceImpl
package com.elvis.springcloud.service.impl;
import com.elvis.springcloud.service.HystrixBreakerService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class HystrixBreakerServiceImpl implements HystrixBreakerService {
@Override
@HystrixCommand(fallbackMethod="fallbackBreakerTest",commandProperties={
@HystrixProperty(name="circuitBreaker.enabled",value="true"), // 开启断路器
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="10"), // 窗口期内的请求总次数
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="1000"), // 时间休眠窗
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="60") // 失败率到达这个指标都开启断路器
// 解释:在1000毫秒内,如果请求总数超过10次,且失败率达到百分之60以上,则开启断路器
})
public String breakerTest(Integer number) {
// 小于0是抛出异常,模拟服务请求出现异常,然后进行熔断
if(number < 0){
throw new RuntimeException();
}
String msg = Thread.currentThread().getName() ":请求服务反馈正常,请求参数为:" number;
return msg;
}
public String fallbackBreakerTest(Integer number) {
String msg = Thread.currentThread().getName() ":请求服务反馈异常,服务熔断,请求参数为:" number;
return msg;
}
}
代码语言:javascript复制// controller
package com.elvis.springcloud.controller;
import com.elvis.springcloud.service.HystrixBreakerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class HystrixBreakerController {
@Autowired
private HystrixBreakerService hystrixBreakerService;
@RequestMapping("/payment/hystrix/breaker/{number}")
public String breakerTest(@PathVariable("number") Integer number){
String s = hystrixBreakerService.breakerTest(number);
log.info(s);
return s;
}
}
四:实现结果展示
五: 小总结
分布式系统中存在多个服务调用时,难免会出现服务之间因为网络等问题而导致服务调用失败的问题,如果不考虑这些问题,有可能出现服务雪崩,导致系统资源被耗尽,所以,存在多个服务调用的时候,服务熔断是不可缺少的。
Hystrix对服务请求的监控还提供了图形化界面,但是由于本文已经比较长,考虑在下一篇文章中再写关于Hystrix的图形化界面监控的细节。