服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心
任务的正常运行。
Hystrix主要是为了解决服务雪崩问题:
- 降级机制:当你的某一个服务出现超时时,资源不足,出现了异常,可以执行一个降级方法,返回一个托底数据
- 隔离:提供了一个Hystrix线程池,信号量,和tomcat的线程池相互隔离。
- 熔断:当你的某一个服务的失败率达到一定的阈值时,自动触发降级
- 缓存:请求缓存的功能
1、降级机制实现
导入依赖 customer
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
添加一个注解(启动类)
@EnableCircuitBreaker
针对某一个接口去编写他的降级方法
@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack")
public Customer findById(@PathVariable Integer id){
int i = 1/0;
return searchClient.findById(id);
}
// findById的降级方法 方法的描述要和接口一致
public Customer findByIdFallBack(Integer id){
return new Customer(-1,"",0);
}
在接口上添加注解
@HystrixCommand(fallbackMethod = "findByIdFallBack")
2、线程隔离
如果使用Tomcat的线程池去接收用户的请求,使用当前线程去执行其他服务的功能,如果某一个服务出现了故障,导致tomcat的线程大量的堆积,导致Tomcat无法处理其他业务功能。
- Hystrix的线程池(只要加上注解hystrixcommand默认),接收用户请求采用tomcat的线程池,执行业务代码,调用其他服务时,采用Hystrix的线程池。
- 信号量,使用的还是Tomcat的线程池,帮助我们去管理Tomcat的线程池。
Hystrix的线程池的配置
配置信息 | name | value |
---|---|---|
线程隔离策略 | execution.isolation.strateg | THREAD 线程池 |
指定超时时间 | execution.isolation.thread.timeoutInMilliseconds | 1000 |
是否开启超时时间配置 | execution.timeout.enabled | true |
超时之后是否中断线程 | execution.isolation.thread.interruptOnTimeout | true |
取消任务后知否 | execution.isolation.thread.interruptOnCancel | false |
代码实现
@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value = "THREAD"),
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Customer findById(@PathVariable Integer id) throws InterruptedException {
System.out.println(Thread.currentThread().getName());
Thread.sleep(300);
return searchClient.findById(id);
}
信号量的配置信息
@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE")
})
public Customer findById(@PathVariable Integer id) throws InterruptedException {
System.out.println(Thread.currentThread().getName());
return searchClient.findById(id);
}
3、断路器
3.1 断路器介绍
马丁福勒断路器论文:https://martinfowler.com/bliki/CircuitBreaker.html
在调用指定服务时,如果说这个服务的失败率达到你输入的一个阈值,将断路器从closed状态,转变为open状态,指定服务时无法被访问的,如果你访问就直接走fallback方法,在一定的时间内,open状态会再次转变为half open状态,允许一个请求发送到我的指定服务,如果成功,转变为closed,如果失败,服务再次转变为open状态,会再次循环到half open,直到断路器回到一个closed状态。
3.2 配置断路器的监控界面
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
在启动类中添加注解
@EnableHystrixDashboard
配置一个Servlet路径,指定上Hystrix的Servlet
@WebServlet("/hystrix.stream")
public class HystrixServlet extends HystrixMetricsStreamServlet {
}
//------------------------------------------------------------
// 在启动类上,添加扫描Servlet的注解
@ServletComponentScan("com.qf.servlet")
添加yml文件配置
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
测试直接访问http://host:port/hystrix
在当前位置输入映射好的servlet路径
3.3 配置断路器的属性
断路器的属性(默认10s秒中之内请求数)
配置信息 | name | value |
---|---|---|
断路器的开关 | circuitBreaker.enabled | true |
失败阈值的总请求数 | circuitBreaker.requestVolumeThreshold | 20 |
请求总数失败率达到%多少时 | circuitBreaker.errorThresholdPercentage | 50 |
断路器open状态后,多少秒是拒绝请求的 | circuitBreaker.sleepWindowInMilliseconds | 5000 |
强制让服务拒绝请求 | circuitBreaker.forceOpen | false |
强制让服务接收请求 | circuitBreaker.forceClosed | false |
具体配置方式
@GetMapping("/customer/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallBack",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "70"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000")
})
4 请求缓存
4.1 请求缓存介绍 相当于我们的学习的mybatis session 缓存
- 请求缓存的声明周期是一次请求
- 请求缓存是缓存当前线程中的一个方法,将方法参数作为key,方法的返回结果作为value
- 在一次请求中,目标方法被调用过一次,以后就都会被缓存。
4.2 请求缓存的实现
创建一个Service,在Service中调用Search服务。
@Service
public class CustomerService {
@Autowired
private SearchClient searchClient;
@CacheResult
@HystrixCommand(commandKey = "findById")
public Customer findById(@CacheKey Integer id) throws InterruptedException {
return searchClient.findById(id);
}
@CacheRemove(commandKey = "findById")
@HystrixCommand
public void clearFindById(@CacheKey Integer id){
System.out.println("findById被清空");
}
}
使用请求缓存的注解
@CacheResult:帮助我们缓存当前方法的返回结果(必须@HystrixCommand配合使用)
@CacheRemove:帮助我们清楚某一个缓存信息(基于commandKey)
@CacheKey:指定哪个方法参数作为缓存的标识
编写Filter,去构建HystrixRequestContext
@WebFilter("/*")
public class HystrixRequestContextInitFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext.initializeContext();
filterChain.doFilter(servletRequest,servletResponse);
}
}
修改Controller
public Customer findById(@PathVariable Integer id) throws InterruptedException {
System.out.println(customerService.findById(id));
System.out.println(customerService.findById(id));
customerService.clearFindById(id);
System.out.println(customerService.findById(id));
System.out.println(customerService.findById(id));
return searchClient.findById(id);
}