Hystrix断路器
书接上文,继续跟着周阳老师学习SpringCloud的Hystrix,特此整理如下
一、Hystrix
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
1.1 Hystrix被设计的目标
(1)对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。
(2)在复杂的分布式系统中阻止级联故障。
(3)快速失败,快速恢复。
(4)回退,尽可能优雅地降级。
(5)启用近实时监控、警报和操作控制。
1.2 Hystrix是如何实现它的目标的
(1)用一个HystrixCommand 或者 HystrixObservableCommand
(这是命令模式的一个例子)包装所有的对外部系统(或者依赖)的调用,典型地它们在一个单独的线程中执行。
(2)调用超时时间比你自己定义的阈值要长。有一个默认值,对于大多数的依赖项你是可以自定义超时时间的。
(3)为每个依赖项维护一个小的线程池(或信号量);如果线程池满了,那么该依赖性将会立即拒绝请求,而不是排队。
(4)调用的结果有这么几种:成功、失败(客户端抛出异常)、超时、拒绝。
(5)在一段时间内,如果服务的错误百分比超过了一个阈值,就会触发一个断路器来停止对特定服务的所有请求,无论是手动的还是自动的。
(6)当请求失败、被拒绝、超时或短路时,执行回退逻辑。 近实时监控指标和配置变化。
二、服务降级与服务熔断
2.1服务降级主要有三种情况
(1)访问超时
(2)运行错误
(3)宕机
2.2服务熔断
熔断机制是应对雪崩对的一种微服务链路保护机制。当某个微服务链路出错导致不可用或者响应时间过长,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该微服务链路调用响应正常后,回复微服务调用链路。
在SpringCloud中,熔断机制是通过Hystrix实现,Hystrix会监控微服务间调用的状况。当失败的调用达到阈值(默认是5秒内20次调用失败),则会启动熔断机制,熔断机制注解为@HystrixCommand。
三、创建微服务,服务端cloud-provider-hystrix-payment8001
application.yml
server:
port: 8001
eureka:
client:
register-with-eureka: true #是否将自己注册到注册中心,集群必须设置为true配合ribbon
fetch-registry: true #是否从服务端抓取已有的注册信息
service-url:
defaultZone: http://eureka7001.com:7001/eureka # ,http://eureka7002.com:7002/eureka
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 1234
application:
name: cloud-provider-hystrix-payment
PaymentHystrixMain8001
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //激活服务降级注解
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
/**
* 此配置是为了服务监控而配置,与服务器容错本身无关,springcloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream
* 只要在自己的项目里配置上下文的servlet就可以了
*/
@Bean
public ServletRegistrationBean getservlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
PaymentController
@RestController
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
//正常访问,肯定ok的方法
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.papymentInfo_OK(id);
log.info("***********result:"+result);
return result;
}
//超时方法
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut(id);
log.info("***********result:"+result);
return result;
}
// 服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
}
PaymentService
@Service
public class PaymentService {
//正常访问,肯定ok的方法
public String papymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id:"+id+"\t";
}
//超时方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
})
public String paymentInfo_TimeOut(Integer id){
int timeNumber = 3000;
// int timeNumber = 10/0;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (Exception e) {
e.printStackTrace();
}
return "线程池: " + Thread.currentThread().getName() + " PaymenyInfo_TimeOut,id: " + id + "\t" + "O(∩_∩)O哈哈~" + " 耗时" + timeNumber + "毫秒";
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "线程池: " + Thread.currentThread().getName() + " 8001系统繁忙系统报错,请稍后再试id: " + id + "\t" + "( Ĭ ^ Ĭ )";
}
//CircuitBreaker断路器,测试步骤
/*首先访问http://localhost:8001/payment/circuit/1正常返回结果
*然后访问http://localhost:8001/payment/circuit/-1,一直重复刷新访问这个错误结果
*当错误率达到60%.之后再访问http://localhost:8001/payment/circuit/1结果还是错误结果,因为已经熔断了
* 但是,过10000之后会慢慢恢复,重新访问http://localhost:8001/payment/circuit/1结果又恢复正确了
* */
//服务熔断
@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 不能为负数");
}
//hutool工具包,在cloud-api-commons的pom中已经引入
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id不能为负数,请稍后再试,┭┮﹏┭┮ ,id:"+id;
}
}
四、服务端cloud-provider-hystrix-payment8001测试
启动cloud-eureka-server7001、cloud-provider-hystrix-payment8001
访问http://eureka7001.com:7001/
执行http://localhost:8001/payment/hystrix/ok/31
执行http://localhost:8001/payment/hystrix/timeout/3,等待3秒钟后,才有反馈结果
五、创建微服务,消费端cloud-consumer-feign-hystrix-order80
application.yml
server:
port: 80
eureka:
client:
register-with-eureka: false #是否将自己注册到注册中心,集群必须设置为true配合ribbon
service-url:
defaultZone: http://eureka7001.com:7001/eureka
# ,http://eureka7002.com:7002/eureka
##设置feign客户端超时时间
#ribbon:
# ReadTimeout: 5000
# ConnectTimeout: 5000
#用于服务降级,在注解FeignClient中添加FallBack属性值
feign:
hystrix:
enabled: true
OrderhystrixMain80
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderhystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderhystrixMain80.class,args);
}
}
OrderHystrixController
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
// @GetMapping("/consumer/payment/hystrix/timeout/{id}")
// public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
// String result = paymentHystrixService.paymentInfo_TimeOut(id);
// return result;
// }
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
// @HystrixCommand(fallbackMethod = "PaymentTimeOutFallbackMethod",commandProperties = {
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
// })
// 如果HystrixCommand标注的类中执行超时3秒,则执行fallbackMethod的方法PaymentTimeOutFallbackMethod,兜底
//设置服务降级注解,如果不指名具体fallbackMethod,则执行defaultFallback
@HystrixCommand
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
/*故意设置5秒超时
int timeUmber = 5;
try {
TimeUnit.SECONDS.sleep(timeUmber);
} catch (Exception e) {
e.printStackTrace();
}
*/
int age = 10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String PaymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
//下面是全局fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,o(╥﹏╥)o";
}
}
PaymentHystrixService
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
//解耦
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
PaymentFallbackService,统一为接口里面的方法进行异常处理
@Component //该方法统一为接口里面的方法进行异常处理
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id) {
return "-------PaymentFallbackService fall back-paymentInfo_OK,o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-------PaymentFallbackService fall back-paymentInfo_TimeOut,o(╥﹏╥)o";
}
}
PaymentFallbackService
@Component //该方法统一为接口里面的方法进行异常处理
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id) {
return "-------PaymentFallbackService fall back-paymentInfo_OK,o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-------PaymentFallbackService fall back-paymentInfo_TimeOut,o(╥﹏╥)o";
}
}
六、消费端cloud-consumer-feign-hystrix-order80测试
访问http://localhost:80/consumer/payment/hystrix/ok/1
访问http://localhost:80/consumer/payment/hystrix/timeout/1
如果HystrixCommand标注的类中执行超时3秒,则执行fallbackMethod的方法PaymentTimeOutFallbackMethod,兜底。
设置服务降级注解,如果不指名具体fallbackMethod,则执行defaultFallback,配置全局统一Fallback异常。
设置服务降级注解,指名具体fallbackMethod为PaymentTimeOutFallbackMethod
,则执行http://localhost:80/consumer/payment/hystrix/timeout/1