在微服务调用的中,为了防止服务的雪崩和业务方案的降级,要事先准备好降级方案。
业务降级
比如优惠券的推荐方案,在上线后,有可能发现不是很合适,有大量的客诉。
这时就要回滚到之前的方案。
有两种选择
- 回滚代码,重新上线,浪费时间和人力
- Apollo配置开关,开关关闭,回到之前的逻辑,简单高效,新老接口逻辑耦合在一起
熔断降级
feign调用其他服务,有hystrix降级,超时熔断,保护本服务。
Hystrix整个工作流如下:
1.构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数;
2.执行命令,Hystrix提供了4种执行命令的方法,后面详述;
3.判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动;
4.判断熔断器是否打开,如果打开,跳到第8步;
5.判断线程池/队列/信号量是否已满,已满则跳到第8步;
6.执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步;
7.统计熔断器监控指标;
8.走Fallback备用逻辑
9.返回请求响应
复制代码
从流程图上可知道,第5步线程池/队列/信号量已满时,还会执行第7步逻辑,更新熔断器统计信息,而第6步无论成功与否,都会更新熔断器统计信息。
hystrix隔离策略:线程池(默认),信号量
策略 | 线程切换 | 支持异步 | 支持超时 | 支持熔断 | 限流 | 开销 |
---|---|---|---|---|---|---|
信号量 | 否 | 否 | 否 | 是 | 是 | 小 |
线程池 | 是 | 是 | 是 | 是 | 是 | 大 |
手写超时降级策略
基于spring aop,一个注解加上一个切面类就可以了。使用线程池来执行业务代码,超时降级,执行降级方法,没有熔断功能。
@RestController
public class HelloController {
private Random random = new Random();
@RequestMapping("/hello")
@MyHystrixCommand(fallback = "errorMethod")
public String hello(String message) throws InterruptedException {
int time = random.nextInt(200);
System.out.println("spend time : " + time + "ms");
Thread.sleep(time);
System.out.println("hhhhhhhhhhhhhhhhhhhhhhhhh");
return "hello world:" + message;
}
public String errorMethod(String message) {
return "error message";
}
}
复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyHystrixCommand {
int value() default 100;
String fallback() default "";
}
复制代码
@Aspect
@Component
public class MyHystrixCommandAspect {
ExecutorService executor = Executors.newFixedThreadPool(10);
@Pointcut(value = "@annotation(MyHystrixCommand)")
public void pointCut(){
}
// @Around(value = "pointCut()&&@annotation(hystrixCommand)")
@Around(value = "@annotation(hystrixCommand)")
public Object doPointCut(ProceedingJoinPoint joinPoint, MyHystrixCommand hystrixCommand) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
int timeout = hystrixCommand.value();
Future<?> future = executor.submit(() -> {
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throw new RuntimeException("方法执行异常");
}
});
Object result;
try {
result = future.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
future.cancel(true);
//执行降级方法
if (StringUtils.isBlank(hystrixCommand.fallback())){
throw new RuntimeException("无降级方法");
}
System.out.println("执行降级方法");
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Class[] parameterTypes = methodSignature.getParameterTypes();
Class<?> aClass = joinPoint.getTarget().getClass();
Method method = aClass.getMethod(hystrixCommand.fallback(), parameterTypes);
result = method.invoke(joinPoint.getTarget(), joinPoint.getArgs());
return result;
}
return result;
}
}
复制代码
近期评论