微服务架构 | 服务隔离 - [Hystrix]
@
§1 简介
Hystrix 是由 Netflix 开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟、异常,引起资源耗尽导致系统不可用的解决方案。
服务隔离是一个双端(提供方和调用方)都可进行的行为
§2 服务的熔断、降级、限流
服务雪崩
当一个比较底层的服务不可用时,所有依赖此服务的服务都可能不可用,依赖这些服务的服务也会受影响导致不可用。这种一个服务牵连一片服务不可用的情况,称为服务雪崩。
下面情况可能导致服务雪崩:
- 使服务节点彻底不能提供服务的情况
- 服务硬件故障,比如硬盘炸了
- 程序 bug
- 占用服务资源导致不能及时提供服务的情况
- 超出系统能力的并发,比如秒杀时的海量请求并发
- 系统资源耗尽,比如缓存击穿导致的持久化层连接池耗尽
- 重试占用资源,因服务不稳定或用户刷新导致的业务重试占用过多系统资源
服务熔断
当服务中的服务端接口不稳定,出现频繁超时或错误时,可能会引起服务调用雪崩。服务熔断使有故障的服务端及时返回错误,并释放系统资源,提高用户体验和系统性能
熔断会在架构层级屏蔽被熔断的服务,直到满足一定条件再将其恢复
服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对某些不重要的服务,不处理或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作
服务降级不会使服务内部的逻辑不可访问,只是在其不能满足预期时快速响应。
服务降级是双端的,既可以在服务提供方也可以在消费方进行。
这是因为二者都有提供降级的必要:
当服务提供方因自身或其依赖服务异常导致了自身异常/超时时,可以自我限定自己提供服务的响应时长以免调用方等待
当服务消费方因自身和提供方之间的网络出现异常时,服务提供方的降级很可能不会生效(因为可能就没有成功调用,而是请求从调用方出发接着就迷路了),这可能导致消费方很长时间收不到任何响应,因此也需要自己进行降级
服务限流
只允许指定数量的事务进入系统处理,超过的部分将被拒绝服务,排队或者降级处理
什么时候要考虑服务隔离
- 服务提供方超时
- 服务提供方不可用
- 服务调用方相比提供方更高的要求,比如设计上服务3秒返回,但调用方需要1秒
§3 使用
依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
§3.1 服务降级(fallback)
通用配置
启动类
需要 @EnableCircuitBreaker
@SpringBootApplication
@MapperScan
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentProviderApplication {
public static void main(String[] args) {
SpringApplication.run(PaymentProviderApplication.class,args);
}
}
项目全局配置
消费端可以通过如下配置快速开启 Hystrix 支持
feign:
hystrix:
enabled: true
但是,这样开启属于全局配置,相当于给所有 FeignClient 接口都添加了服务隔离配置
默认的全局配置绑定了一个 1 秒的超时(大坑),因此实际项目中还需要结合其他全局配置
feign:
hystrix:
enabled: true
client:
config:
default:
connect-timeout: 100000
read-timeout: 100000
hystrix:
command:
default: #default全局有效,service id指定应用有效
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
enabled: true
isolation:
thread:
timeoutInMilliseconds: 10000 #断路器超时时间,默认1000ms
接口单独配置
可以在 openService(对外接口,可以理解成 controller) 上进行
@RequestMapping(value = "/wait/{time}", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "waitTimeoutFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
@ResponseBody
public CommonResult<String> waitTimeout(@PathVariable("time") Integer time) {
try {
inti = 0;
if(0==time)
i = 1/0;
else
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
log.error("等待中断 {}",time ,this.serverPort, e);
return new CommonResult<>(400,"等待中断" + this.serverPort);
}
}
private CommonResult<String> waitTimeoutFallback(Integer time){
return new CommonResult<>(400,"wait ...... ......" + this.serverPort);
}
也可以在 service/FeignClient 上进行
@HystrixCommand(fallbackMethod = "waitTimeoutFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
@Override
public String waitTimeout(Integer time){
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "wait success";
}
private String waitTimeoutFallback(Integer time) {return "wait ... ... ...";}
效果如下:
注意事项
fallbackMethod 需要返回值和参数表与原接口一致,方法名使用 fallbackMethod 指定标识符
服务全局配置
配置服务全局 fallback 方法
@DefaultProperties(defaultFallback = "waitTimeoutFallbackGlobal")
public class OrderFeignHystrixController {
}
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
//@HystrixCommand
public String waitTimeout(@PathVariable("time") Integer time){
}
public String waitTimeoutFallbackGlobal(){
return"order wait global...... ......";
}
注意:
- 服务全局 fallback 方法必须是无参数的
- 指定服务全局 fallback 方法后,接口的降级条件依然要进行配置,否则使用默认的(比如1秒超时)
配置服务 fallback 类
配置形式如下
@FeignClient(value = "PAYMENT-SERVICE-HYSTRIX",fallback = PaymentOpenfeignClientFallback.class)
public interface PaymentOpenfeignClient {
}
@Component
public class PaymentOpenfeignClientFallback implements PaymentOpenfeignClient{
}
需注意,配置 fallback 类需要开启 项目全局配置
feign:
hystrix:
enabled: true
同时,上述配置造成的问题,比如1秒的全局超时,需要其他全局配置补充
§3.2 服务熔断 (circuitBreaker)
服务熔断的状态
- 断路器默认是 closed 状态
- 在一定的访问阈值内,断路器不会进行判断
- 访问量达到阈值后,断路器会监控服务访问状态,若单位时间内访问失败达到一定比例会触发熔断
- 当触发熔断后,会切换到 open 状态,断路器打开后,所有访问断路器上服务的请求都会被拒绝
- 一定的时间后,断路器会切换到 half open 状态,此状态会放行少量的请求
- 若请求成功,断路器关闭;否则再切换回 open 状态,再持续一定的时间
开启熔断
熔断需要开启 项目全局配置
feign:
hystrix:
enabled: true
接口熔断配置
@RequestMapping(value = "/wait/{time}", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "waitTimeoutFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000"),
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "20000"), // 时间窗口,若断路器打开,窗口期开始时少量放行请求,若通过,断路器关闭否则再持续一个窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")// 失败率达到多少后跳闸
})
@ResponseBody
public CommonResult<String> waitTimeout(@PathVariable("time") Integer time) {
}
配置详解:
- circuitBreaker.enabled 是否开启熔断
- circuitBreaker.requestVolumeThreshold 请求容积阈值,请求数量不够不涉及熔断的判断
- circuitBreaker.sleepWindowInMilliseconds 睡眠窗口,熔断后,断路器会在此窗口时长中拒绝所有请求,直到下一个窗口放行少量,根据其访问是否失败决定关闭断路器还是继续睡一个窗口时长
- circuitBreaker.errorThresholdPercentage 触发断路的失败百分比
TODO
熔断中涉及的概念()
MTTR、MTBF、MTTF
§3.3 常见问题
正确配置了 @HystrixCommand 注解,但就是不能触发 fallback
希望触发 fallback 的异常最终一定要抛出,若进行捕获后组织返回信息,即使里面夹带异常信息也是正常返回的范畴,不会触发 fallback
§4 注解
§5 Dashboard
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>runtime</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>
启动类
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class,args);
}
}
服务配置
被监控的服务上添加配置
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
访问
http://ip:port/hystrix
传送门:
微服务架构 | 组件目录