概述
分布式框架中,经常需要进行服务间的远程调用,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,这些情况的发生将直接导致对外接口服务也会出现延迟,当消费方大量调用延迟接口或者服务的时候,将造成任务积压,线程资源无法释放,最终通过调用链会蔓延开来,最终拖垮整个系统。
在springcloud中,针对上述的问题提供了Spring Cloud Hystrix容错保护,它也是基于Netflix的开源框架 Hystrix实现的,其具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能。
- 资源隔离:包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
- 降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
- 融断:当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。
- 缓存:提供了请求缓存、请求合并实现。
机制
隔离
- 线程池隔离模式:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
- 信号量隔离模式:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
熔断
正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时,电路被打开进入熔断状态(Open),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态
断路器
- 当满足一定的阀值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
- 到达以上阀值,断路器将会开启
- 当开启的时候,所有请求都不会进行转发
- 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。
降级
当某个服务或者接口出现大量接口调用异常的时候,会将整个服务或者接口熔断,不再调用,这个成为降级
示例
请事先准备好一个简单的springcloud(Greenwich)的项目,一个注册中心,一个消费者,一个服务提供者,消费者消费服务基于Feign远程调用。
由于Spring Cloud Hystrix是消费者端实现服务降级、服务熔断等功能,在消费者服务引入如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
改造Feign调用接口
package com.leolee.msf.feignInterface;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient("database-web")
public interface TestClinet {
@RequestMapping(value = "/test/hello", method = RequestMethod.GET)
public String feignHello();
@RequestMapping(value = "/test/value", method = RequestMethod.GET)
public String feignValue();
}
重点如下:
package com.leolee.msf.service;
import com.leolee.msf.feignInterface.TestClinet;
import com.leolee.msf.service.serviceInterface.TestService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @ClassName TestServiceImpl
* @Description: TODO
* @Author LeoLee
* @Date 2020/8/18
* @Version V1.0
**/
@Service("loginService")
public class TestServiceImpl implements TestService {
@Autowired
private TestClinet testClinet;
@HystrixCommand(fallbackMethod = "fallback")
@Override
public String getFeignValue() {
return testClinet.feignValue();
}
public String fallback() {
return "Hystrix";
}
}
注入Feign接口之后,在调用方法上增加@HystrixCommand(fallbackMethod = "fallback")
fallbackMethod 指向了fallback()方法,当发生超时的时候,将主动放弃远程调用,改为调用本地fallback()方法
相关配置
ribbon:
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 默认为;轮询,这里改为随机
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略
ConnectTimeout: 5000 # 连接超时时间(ms)
ReadTimeout: 5000 # 通信超时时间(ms)
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 7000 # 设置hystrix的超时时间为6000ms
feign.hystrix.enabled=true开启feign容错
Hystrix默认调用超时时间为6000ms,根据实际情况可以自行调整