设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback
服务降级 fallback 既可以放在服务端,也可以放在客户端,但是我们一般放在客户端,这里两种都演示一下。
(1) 服务提供者服务降级
//业务类启用 @HystrixCommand
package com.atguigu.springcloud.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class PaymentService {
/**
* 正常访问
* @param id
* @return
*/
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id:"+id+"\t"+"O(∩_∩)O哈哈~";
}
/**
* http://localhost:8001/payment/hystrix/timeout/31
* @HystrixCommand报异常后如何处理:
* 一旦调用服务方法失败并抛出了错误信息后,
* 会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
//设置这个线程的超时时间是3s,3s内是正常的业务逻辑,超过3s调用fallbackMethod指定的方法进行处理
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_Timeout(Integer id){
int timeNumber = 5;
//int age = 10/0;
try{
TimeUnit.SECONDS.sleep(timeNumber);
}catch (InterruptedException e){
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_Timeout,id:"+id+"\t"+"O(∩_∩)O哈哈~"+" 耗时(秒):"+timeNumber;
}
public String paymentInfo_TimeOutHandler(Integer id){
return "线程池:"+Thread.currentThread().getName()+" 系统繁忙,请稍后再试,id:"+id+"\t"+"o(╥﹏╥)o";
}
}
//主启动类激活 @EnableCircuitBreaker
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
启动测试,没有报错,而是执行了fallbackMethod指定的方法
再来测试一下计算错误,也是会调用 fallbackMethod 指定的方法
总结:
如果我们故意制造两个异常:
- int age = 10/0; 运行时异常
- 我们能接受3秒钟,它运行5秒钟,超时异常。
当前服务不可用了,做服务降级,兜底的方案都是paymentInfo_TimeOutHandler
(1) 消费者服务降级
#yml添加配置,开启 hystrix
feign:
hystrix:
enabled: true
//主启动
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
//业务类
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_OK(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallBackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
return paymentHystrixService.paymentInfo_TimeOut(id);
}
public String paymentTimeOutFallBackMethod(@PathVariable("id") Integer id){
return "我是消费者80,对方支付系统繁忙,请稍后再试,o(╥﹏╥)o";
}
}
测试,超时异常:
运行时异常:
PS: 我们自己配置过的热部署方式对Java代码的改动明显,但对@HystrixCommand内属性的修改建议重启微服务。
问题1:
这样如果每个业务方法都对应一个兜底的方法,100个方法就有100个服务降级,会出现代码膨胀问题,我们需要一个统一的 fallbackMethod,统一的和自定义的分开。
解决问题:
@DefaultProperties(defaultFallback = "")
1 : 1 每个方法配置一个服务降级方法,造成代码膨胀
1 : N 除了个别重要核心业务有专属,其他普通的可以通过@DefaultProperties(defaultFallback = “”) 统一跳转到统一处理结果页面
这样通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。
测试
问题2:
现在客户端与服务端关系紧紧耦合,客户端能跑是因为接口调用了微服务的业务逻辑方法,我们如果针对客户端接口做一些处理,把它调用的所有微服务方法进行降级,就可以解决耦合问题。
解决问题:
这个案例服务降级处理是在客户端80完成的,与服务端8001没有关系,只需要为 Feign 客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
package com.atguigu.springcloud.service;
@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";
}
}
接口使用@FeignClient(fallback = xxx.class)指定哪个类来处理异常
测试
先启动服务端8001,在启动客户端80,客户端正常调用微服务
现在关闭8001,客户端自己进行了降级,调用处理异常的方法