一、为什么要使用hystrix?
在微服务架构中,存在服务之间的相互调用,如果a服务调用b服务,b服务又调用c服务,当此调用链路中出现问题时,但是并发量又很大的时候就容易产生服务雪崩的现象,而hystrix可以为我们提供服务的降级与熔断等一系列的措施来保证我们的微服务系统的弹性
二、hystrix的三个重要措施
1.服务降级
1.1.使用服务降级的场景
- 程序运行异常
- 服务熔断导致服务降级
- 线程池/信号量 打满
代码如下(示例):
//在客户端
package com.ljj.controller;
import com.ljj.controller.service.OrderFeignService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
//规定全局的fallback方法
@DefaultProperties(defaultFallback = "paymentInfo_global_TimeOutHandler")
public class OrderFeignController {
@Resource
private OrderFeignService orderFeignService;
@GetMapping("consumer/hystrix/ok/{id}")
public String ok(@PathVariable("id") Integer id) {
return orderFeignService.ok(id);
}
/*
单个方法的进行配置
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})*/
@HystrixCommand
@GetMapping("consumer/hystrix/timeout/{id}")
public String timeout(@PathVariable Integer id) {
return orderFeignService.timeout(id);
}
/**
* 服务提供方此时是正常的,但是服务调用需要三秒的时间
* 客服端做了服务降级,所以调用失败,如果服务调用小于1.5s即可成功
* 但是要保证服务内部没有错
*
* @param id
* @return
*/
public String paymentInfo_TimeOutHandler(Integer id) {
return "我是消费线程 port:80:" + Thread.currentThread().getName() + "======1.5s内未得到响应或者服务提供者自身错误,=====服务器忙😂,请勿重复调用/(ㄒoㄒ)/~~" + id;
}
/**
* 全局的fallback貌似不能自定义参数
*
* @return
*/
public String paymentInfo_global_TimeOutHandler() {
return "我是消费线程(global) port:80:" + Thread.currentThread().getName() + "======未得到响应或者服务提供者自身错误,=====服务器忙😂,请勿重复调用/(ㄒoㄒ)/~~";
}
}
//在服务端
package com.ljj.service;
import cn.hutool.core.util.IdUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.concurrent.TimeUnit;
@Service
public class HystrixService {
public String PaymentInfo(Integer id) {
return "当前的支付线程:" + Thread.currentThread().getName() + "****支付流水号:" + id;
}
/**
* 模拟超时演示服务降级
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
})
public String TimeoutPaymentInfo(Integer id) {
//在三秒之内不能完成调用视为超时(或者服务错误不可用),都降级,调用兜底方法
int timeNum = 3;
int a=10/0;
/*try {
TimeUnit.SECONDS.sleep(timeNum);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
return "当前的支付线程:😀" + Thread.currentThread().getName() + "****支付流水号:" + id + "耗时" + timeNum;
}
public String paymentInfo_TimeOutHandler(Integer id)
{
return "当前的支付线程:" + Thread.currentThread().getName() + "已经超时,服务器忙,请勿重复调用/(ㄒoㄒ)/~~"+id;
}
}
2.服务熔断
代码如下(示例):
//=====服务熔断
/**
* 如果在十秒内的十次调用中由六次是调用失败的就开启断路器
*
* circuitBreaker.sleepWindowInMilliseconds:时间窗口期:在时间窗口期内达到调用的次数的阈值,就断路
*
* 当开启断路器后所有的服务都不可以被调用
*
* 当断路五秒后服务会恢复部分调用,但是注意:恢复调用的服务是降级的服务 fallback 此时豪猪会给我们开启一个休眠的时间窗口
*
* 如何切换到主逻辑呢?
*
* 当休眠时间窗到期时,hystrix会给我们进行一次原来的主逻辑的调用如果结果正常返回就关闭断路器
*
* 如果结果仍然由问题,就重新即使休眠时间窗口
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
if(id < 0)
{
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
{
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id;
}
3.服务限流
后面用sential说明之
4.hystrix的工作流程
1、包装请求:
可以使用继承HystrixCommand或HystrixObservableCommand来包装业务方法;
2、发起请求:
使用调用Command的execute来执行一个业务方法调用;
3、缓存处理:
当请求来到后,会判断请求是否启用了缓存(默认是启用的),再判断当前请求是否携带了缓存Key;
如果命中缓存就直接返回;否则进入剩下的逻辑;
4、判断断路器是否打开(熔断):
断路器是Hystrix的设计核心,断路器是实现快速失败的重要手段(断路器打开就直接返回失败);
可以设置断路器打开一定时间后,可以进行尝试进行业务请求(默认是5000毫秒);
5、判断是否进行业务请求(请求是否需要隔离或降级):
是否进行业务请求之前还会根据当前服务处理质量,判断是否需要去请求业务服务;
如果当前服务质量较低(线程池/队列/信号量已满),那么也会直接失败;
线程池或信号量的选择(默认是线程池):
线程池主要优势是客户端隔离和超时设置,但是如果是海量低延迟请求时,频繁的线程切换带来的损耗也是很可观的,这种情况我们就可以使用信号量的策略;
信号量的主要缺点就是不能处理超时,请求发送到客户端后,如果被客户端pending住,那么就需要一直等待;
6、执行业务请求:
当前服务质量较好,那么就会提交请求到业务服务器去;
HystrixObservableCommand.construct() or HystrixCommand.run()
7、健康监测:
根据历史的业务方法执行结果,来统计当前的服务健康指标,为断路器是否熔断等动作作为依据;
8/9、响应失败或成功的处理结果