微服务使用 Hystrix 实现服务降级
一、为什么要服务降级
设想一个场景,很多人都在请求一个接口,此时这个接口就会变得非常繁忙,调用者就要等待很长时间,而微服务是互相调用的,所以微服务的调用者即客户端回等待很长时间,给用户造成不好的使用体验。
而服务降级,就是指在服务端发生超时或者出错的时候,客户端要及时的屏蔽这些不好的信息,给用户一个良好的体验,比如在请求超时的时候,固定如果超过 3 秒就提示用户系统繁忙请稍后再试。
二、服务降级具体实现
2.1、服务端
如果实在服务端,即服务的提供方,我们可以在系统业务类上加一个 HystrixCommand
注解。
同时在主启动类上加一个 @EnableCircuitBreaker
注解。
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args);
}
}
业务类:
/**
* HystrixCommand 服务降级:
* 在微服务调用中的服务端
* 发生超时或者出现异常之后调用兜底的方法处理;
*
* @param id ID
* @return 返回提示信息
*/
@HystrixCommand(
fallbackMethod = "paymentInfoTimeOutHandler",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfoTimeOut(Integer id) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: " + Thread.currentThread().getName() + " paymentInfo_OK, id: " + id + "t" + "耗时3秒钟!";
}
/**
* 具体的处理方法
*
* @param id ID
* @return 返回提示信息
*/
public String paymentInfoTimeOutHandler(Integer id) {
return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeOutHandler, id: " + id + "t" + "系统繁忙请稍后再试!";
}
该注解的作用是,先设置一个兜底的方法来应对突发情况,如果超过了设置的时间或者出现了异常就触发这个方法。
此时我们如果再去访问超时的接口就会收到提示信息。
2.2、客户端
对于使用 Feign
进行调用的客户端,我们也可以在控制器上加上类似的方法。
/**
* 客户端服务降级:
* 请求不到信息,这几出提示信息
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") //3秒钟以内就是正常的业务逻辑
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfoTimeOut(id);
log.info("*******result:" + result);
return result;
}
/**
* 兜底方法
*
* @return 返回提示信息
*/
public String paymentTimeOutFallbackMethod() {
return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
}
同时也要在主启动类上加上一个注解 @EnableHystrix
:
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain80.class, args);
}
}
这样配置之后客户端可以更好的保护自己。
三、简化代码
对于上述代码有两个问题:
- 每个业务方法对应一个兜底的方法,代码膨胀;
- 应该让统一和自定义的方法分开。
对此我们可以使用 @DefaultProperties(defaultFallback = "")
注解表示不配置兜底方法 默认触发的方法 :
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "paymentGlobalFallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
/**
* 客户端服务降级:
* 请求不到信息,这几出提示信息
*
* @param id
* @return
*/
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfoTimeOut(id);
log.info("*******result:" + result);
return result;
}
/**
* 兜底方法
*
* @return 返回提示信息
*/
public String paymentGlobalFallbackMethod() {
return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
}
}
其中 PaymentHystrixService
是 Feign 调用服务的接口类入口。
如上,我们在 @HystrixCommand
注解中没有配置兜底方法,但是最后程序回去执行 paymentGlobalFallbackMethod
方法,这就是默认配置。
如果我们想自定义方法可以重写在注解中配置自己的兜底方法。
此时还有一个问题就是兜底方法和调用方法出现在一起了,从而导致了代码膨胀。
我们可以使用一个类 PaymentFallbackService
去继承我们的 Feign
的客户端接口,这样服务降级系统就会自动去调用已经实现的方法作为兜底方法。
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}
别忘了在 yml
中配置:
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
此时可以直接在 @FeignClient
注解中加一个属性 fallback
设置为我们的实现类。
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {......}
- Android基础总结(12)——XML和JSON解析
- 【Python环境】scikit-learn的线性回归模型
- Android基础总结(8)——服务
- 你需要每天写代码吗?
- Java基础——多线程
- No.010 Regular Expression Matching
- JavaScript依赖注入的实现思路
- No.011 Container With Most Water
- No.009 Palindrome Number
- Windows DNS API RCE漏洞分析及PoC构造
- 爬虫采集去重优化浅谈
- Android基础总结(10)——手机多媒体的运用:通知、短信、相机、视频播放
- Android基础总结(9)——网络技术
- 【Python环境】基于 Python 和 Scikit-Learn 的机器学习介绍
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法