微服务降级方案

在微服务调用的中,为了防止服务的雪崩和业务方案的降级,要事先准备好降级方案。

业务降级

比如优惠券的推荐方案,在上线后,有可能发现不是很合适,有大量的客诉。
这时就要回滚到之前的方案。
有两种选择

  • 回滚代码,重新上线,浪费时间和人力
  • 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;
    }

}
复制代码