2023-08-07  阅读(1)
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/268

上一章,我讲解了Hystrix整合Feign时的初始化流程。事实上,Spring Cloud Netflix Hystrix是可以单独使用的,本章我就来讲解Hystrix单独使用时的初始化流程。

首先回顾一下,Hystrix独立使用时,一般就是利用@HystrixCommand注解:

    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    @EnableCircuitBreaker
    public class ServiceBApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServiceBApplication.class, args);
        }
    }
    @Service
    public class SomeService {
        @HystrixCommand(fallbackMethod = "defaultStores",commandProperties = {
          @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
        })
        public Object getStores(Map<String, Object> parameters) {
            //do stuff that might fail
        }
    
        // 降级方法
        public Object defaultStores(Map<String, Object> parameters) {
            return /* something useful */;
        }
    }

为什么加个注解就可以完成Hystrix的使用了?经过前面几个Netfilx组件源码的学习,你应该可以立马联想到一定是Spring Cloud针对@HystrixCommand注解的方法所在的类生成了一个动态代理对象。

一、自动装配

首先,我们来看看Spring Cloud是如何完成Hystrix的自动装配的。从启动类的@EnableCircuitBreaker注解开始。

1.1 @EnableCircuitBreaker

Spring Boot应用启动后,扫描到启动类上的@EnableCircuitBreaker注解:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(EnableCircuitBreakerImportSelector.class)
    public @interface EnableCircuitBreaker {
    }

此时触发EnableCircuitBreakerImportSelector的执行:

    @Order(Ordered.LOWEST_PRECEDENCE - 100)
    public class EnableCircuitBreakerImportSelector extends SpringFactoryImportSelector<EnableCircuitBreaker> {
        @Override
        protected boolean isEnabled() {
            return getEnvironment().getProperty("spring.cloud.circuit.breaker.enabled",
                    Boolean.class, Boolean.TRUE);
        }
    }

真正的执行方法在它的父类SpringFactoryImportSelector<EnableCircuitBreaker>中——selectImports方法。

selectImports的核心功能是扫描注解类,返回需要自动装配的类名称,我们可以在这里自定义查找装配类的逻辑,返回类名称,然后Spring Boot会去自动加载装类。下面方法返回的是EnableCircuitBreaker,也就是说Spring Boot启动后会去spring.factories中找到该类对应的所有自动装配类,并执行:

    public abstract class SpringFactoryImportSelector<T>
            implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
    
        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            if (!isEnabled()) {
                return new String[0];
            }
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                    metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
    
            Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
                    + metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
    
            // 找到所有auto configuration classes
            List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
                    .loadFactoryNames(this.annotationClass, this.beanClassLoader)));
            //...
    
            return factories.toArray(new String[factories.size()]);
        }
    }

1.2 HystrixCircuitBreakerConfiguration

根据上述的步骤,Spring Boot启动并识别EnableCircuitBreaker后,会读取并加载META-INF/spring.factories文件中的对应装配类:

202308072154121791.png

    # spring.factories
    
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\
    org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerAutoConfiguration,\
    org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerAutoConfiguration,\
    org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration
    
    org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
    org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration

可以看到自动装配类就是HystrixCircuitBreakerConfiguration,我们来看看它内部到底做了什么事情?

HystrixCircuitBreakerConfiguration的核心作用是注入了一个 HystrixCommandAspect ,这个切面就是用来处理标注了@HystrixCommand的方法的。

    @Configuration(proxyBeanMethods = false)
    public class HystrixCircuitBreakerConfiguration {
    
        @Bean
        public HystrixCommandAspect hystrixCommandAspect() {
            return new HystrixCommandAspect();
        }
        //...
    }

至此,真相大白了,我们之所以通过@HystrixCommand注解就能使用Hystrix,是因为Spring Cloud为我们自动注入了一个切面对象,这个切面对象负责处理Hystrix相关的逻辑:

202308072154127902.png

二、HystirxCommandAspect切面

我们再来看看HystirxCommandAspect这个切面,我省略了请求合并相关的代码,整个处理流程我用下面这张图表示:

202308072154136373.png

2.1 切面整体逻辑

这里使用Spring提供的AOP功能,用了一个@Around横切面:

    @Aspect
    public class HystrixCommandAspect {
    
        private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;
    
        static {
            META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
                    .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory())
                    .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory())
                    .build();
        }
    
        // 定义一个切点,用@HystrixCommand标识
        @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
        public void hystrixCommandAnnotationPointcut() {
        }
    
        // 横切逻辑
        @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
        public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
            // 1. 获取@HystrixCommand标注的方法元数据
            Method method = getMethodFromTarget(joinPoint);
    
            //...
    
            // 2.拿到一个CommandMetaHolderFactory
            MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP
                .get(HystrixPointcutType.of(method));
    
            // 3.MetaHolder包含了各种Hystrix相关元数据
            MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
    
            // 4.HystrixInvokable包含了创建出来的HystrixCommand对象
            HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
            ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
                    metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
    
            Object result;
            try {
                // 5.执行方法
                if (!metaHolder.isObservable()) {
                    result = CommandExecutor.execute(invokable, executionType, metaHolder);
                } else {
                    result = executeObservable(invokable, executionType, metaHolder);
                }
            } catch (HystrixBadRequestException e) {
                throw e.getCause();
            } catch (HystrixRuntimeException e) {
                throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
            }
            return result;
        }
        //...
    }

HystirxCommandAspect这个切面类的核心逻辑如下:

  1. 获取@HystrixCommand标注的方法元数据;
  2. 将这些元数据解析成一个MetaHolder对象,里面包含了各种Hystrix自身的组件、方法信息、代理对象信息;
  3. 基于HystrixCommandFactory工厂,创建一个HystrixInvokable对象,这个对象本质是一个HystrixCommand,里面封装了对原接口方法的调用逻辑;
  4. 最后,执行HystrixCommand.execute(),触发执行,后面就是走Hystrix自身的Command处理逻辑了。

2.2 构建HystrixCommand

我们再来深入下,看看HystirxCommandAspect内部到底是怎样构造一个HystrixCommand对象的,其实核心就是下面的这两行代码:

    // HystrixCommandAspect.java
    
    // 3.MetaHolder包含了各种Hystrix相关元数据
    MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
    
    // 4.HystrixInvokable包含了创建出来的HystrixCommand对象
    HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);

MetaHolder

先来看CommandMetaHolderFactory,它根据AOP的jointPoint信息创建了一个MetaHolder对象:

    private static abstract class MetaHolderFactory {
        public MetaHolder create(final ProceedingJoinPoint joinPoint) {
            // 方法元数据
            Method method = getMethodFromTarget(joinPoint);
            // 被代理的对象
            Object obj = joinPoint.getTarget();
            // 入参
            Object[] args = joinPoint.getArgs();
            // 代理对象自身
            Object proxy = joinPoint.getThis();
            // 调用子类CommandMetaHolderFactory.create方法
            return create(proxy, method, obj, args, joinPoint);
        }
    }
    
    private static class CommandMetaHolderFactory extends MetaHolderFactory {
        @Override
        public MetaHolder create(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
            HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
            ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
    
            // 这里创建一个了MetaHolder.Builder对象,顾名思义就是MetaHolder的构建工具类
            MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
            if (isCompileWeaving()) {
                builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
            }
            // 设置MetaHolder.Builder的各种属性
            return builder.defaultCommandKey(method.getName())
                .hystrixCommand(hystrixCommand)
                .observableExecutionMode(hystrixCommand.observableExecutionMode())
                .executionType(executionType)
                .observable(ExecutionType.OBSERVABLE == executionType)
                .build();
        }
    }

我们重点关注最后一行,MetaHolder.Builder这个对象内部包含了各种各样的元数据信息,这样后面Hystrix执行时就可以知道代理对象的所有信息:

    // MetaHolder.java
    
    public static final class Builder {
        private static final Class<?>[] EMPTY_ARRAY_OF_TYPES= new Class[0];
        private HystrixCollapser hystrixCollapser;
        private HystrixCommand hystrixCommand;
        private DefaultProperties defaultProperties;
        private Method method;
        private Method cacheKeyMethod;
        private Method fallbackMethod;
        private Method ajcMethod;
        private Object obj;
        private Object proxyObj;
        private Closure closure;
        private Object[] args;
        private String defaultGroupKey;
        // 方法名称作为commandKey
        private String defaultCommandKey;
        private String defaultCollapserKey;
        private String defaultThreadPoolKey;
        private ExecutionType executionType;
        private ExecutionType collapserExecutionType;
        private ExecutionType fallbackExecutionType;
        private boolean extendedFallback;
        private boolean fallback;
        private boolean extendedParentFallback;
        private boolean defaultFallback;
        private boolean observable;
        // JoinPoint信息
        private JoinPoint joinPoint;
        private ObservableExecutionMode observableExecutionMode;
        //...
    }

HystrixInvokable

有了MetaHolder之后,就可以用它来创建HystrixCommand对象了:

    HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);

这里用到了HystrixCommandFactory,顾名思义就是创建HystrixCommand的工厂类:

    // HystrixCommandFactory.java
    
    public HystrixInvokable create(MetaHolder metaHolder) {
        HystrixInvokable executable;
        // 根据不同的元数据,创建不同类型的HystrixCommand对象
        if (metaHolder.isCollapserAnnotationPresent()) {
            executable = new CommandCollapser(metaHolder);
        } else if (metaHolder.isObservable()) {
            executable = new GenericObservableCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
        } else {
            executable = new GenericCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
        }
        return executable;
    }

HystrixCommandFactory内部会根据MetaHolder的信息,创建不同的HystrixCommand对象,一般都是走最后的else分支,也就是创建了一个GenericCommand对象,这个对象内部保存了一个HystrixCommandBuilder:

    executable = new GenericCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));

我们来看下HystrixCommandBuilder的构建逻辑,这是在HystrixCommandBuilderFactory内部完成的,返回一个HystrixCommandBuilder:

    // HystrixCommandBuilderFactory.java
    
    public <ResponseType> HystrixCommandBuilder create(MetaHolder metaHolder, Collection<HystrixCollapser.CollapsedRequest<ResponseType, Object>> collapsedRequests) {
        // 1.校验
        validateMetaHolder(metaHolder);
    
        // 2.创建一个HystrixCommandBuilder
        return HystrixCommandBuilder.builder()
            .setterBuilder(createGenericSetterBuilder(metaHolder))
            .commandActions(createCommandActions(metaHolder))
            .collapsedRequests(collapsedRequests)
            .cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
            .cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
            .ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
            .executionType(metaHolder.getExecutionType())
            .build();
    }
    
    // 设置HystrixCommand的各种数据
    private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
        GenericSetterBuilder.Builder setterBuilder = GenericSetterBuilder.builder()
            .groupKey(metaHolder.getCommandGroupKey())
            .threadPoolKey(metaHolder.getThreadPoolKey())
            .commandKey(metaHolder.getCommandKey())
            .collapserKey(metaHolder.getCollapserKey())
            .commandProperties(metaHolder.getCommandProperties())
            .threadPoolProperties(metaHolder.getThreadPoolProperties())
            .collapserProperties(metaHolder.getCollapserProperties());
        if (metaHolder.isCollapserAnnotationPresent()) {
            setterBuilder.scope(metaHolder.getHystrixCollapser().scope());
        }
        return setterBuilder.build();
    }
    
    // 创建CommandActions,包含了HystrixCommand执行流程
    private CommandActions createCommandActions(MetaHolder metaHolder) {
        // 创建一个正常执行流程的CommandAction
        CommandAction commandAction = createCommandAction(metaHolder);
        // 创建一个降级执行流程的CommandAction
        CommandAction fallbackAction = createFallbackAction(metaHolder);
        return CommandActions.builder().commandAction(commandAction)
            .fallbackAction(fallbackAction).build();
    }
    
    // 创建正常执行流程的CommandAction
    private CommandAction createCommandAction(MetaHolder metaHolder) {
        // 返回一个MethodExecutionAction,内部仅仅就是设置了一下属性值
        return new MethodExecutionAction(metaHolder.getObj(), metaHolder.getMethod(), 
                                         metaHolder.getArgs(), metaHolder);
    }
    
    // 创建降级执行流程的CommandAction
    private CommandAction createFallbackAction(MetaHolder metaHolder) {
        // 解析降级方法元数据
        FallbackMethod fallbackMethod = MethodProvider.getInstance()
            .getFallbackMethod(metaHolder.getObj().getClass(), metaHolder.getMethod(), 
                               metaHolder.isExtendedFallback());
        // 校验
        fallbackMethod.validateReturnType(metaHolder.getMethod());
        CommandAction fallbackAction = null;
        if (fallbackMethod.isPresent()) {
            Method fMethod = fallbackMethod.getMethod();
            Object[] args = fallbackMethod.isDefault() ? new Object[0] : metaHolder.getArgs();
            if (fallbackMethod.isCommand()) {
                //...
                fallbackAction = new LazyCommandExecutionAction(fmMetaHolder);
            } else {
                //...
                fallbackAction = new MethodExecutionAction(fmMetaHolder.getObj(), fMethod, 
                                                           fmMetaHolder.getArgs(), fmMetaHolder);
            }
    
        }
        return fallbackAction;
    }

重点关注createGenericSetterBuilder和createCommandActions方法:

  1. createGenericSetterBuilder方法:用来设置HystrixCommand的各种属性,比如groupKey,commandKey等等;
  2. createCommandActions方法:创建一个CommandActions对象,它包含了HystrixCommand的执行逻辑。

2.3 GenericCommand

最后,我们来看构造出来的 GenericCommand 是长什么样的:

    public class GenericCommand extends AbstractHystrixCommand<Object> {
        public GenericCommand(HystrixCommandBuilder builder) {
            super(builder);
        }
    
        // 当执行GenericCommand.execute()时,这个方法会被自动调用
        @Override
        protected Object run() throws Exception {
            LOGGER.debug("execute command: {}", getCommandKey().name());
            return process(new Action() {
                @Override
                Object execute() {
                    // CommandAction包含正常的处理流程
                    return getCommandAction().execute(getExecutionType());
                }
            });
        }
    
        // 降级逻辑
        @Override
        protected Object getFallback() {
            // FallbackAction包含降级处理流程
            final CommandAction commandAction = getFallbackAction();
            if (commandAction != null) {
                try {
                    return process(new Action() {
                        @Override
                        Object execute() {
                            MetaHolder metaHolder = commandAction.getMetaHolder();
                            Object[] args = createArgsForFallback(metaHolder, getExecutionException());
                            return commandAction.executeWithArgs(metaHolder.getFallbackExecutionType(), args);
                        }
                    });
                } catch (Throwable e) {
                    LOGGER.error(FallbackErrorMessageBuilder.create()
                            .append(commandAction, e).build());
                    throw new FallbackInvocationException(unwrapCause(e));
                }
            } else {
                return super.getFallback();
            }
        }
    }

GenericCommand就是AbstractHystrixCommand的子类,它的run()方法里面封装了正常的业务处理流程逻辑,getFallback()方法里封装了降级逻辑。


至此,整个HystirxCommandAspect的核心原理就分析完了,我来总结一下它的核心逻辑:

  1. 首先,由于我们用了Spring的AOP功能,所以Spring 会为我们创建一个动态代理对象,当我们调用对象注解的@HystrixCommand方法时,会触发HystirxCommandAspect.methodsAnnotatedWithHystrixCommand()方法的执行;
  2. HystirxCommandAspect会根据joinPoint,获取注解信息和方法元数据,创建一个MetaHolder对象,这个对象里面包含了Hystrix核心组件、代理对象、方法元数据等信息;
  3. 接着,通过HystrixCommandFactory创建一个HystrixInvokable对象(一般情况下为GenericCommand),GenericCommand内部封装方法正常执行的逻辑和降级逻辑;
  4. 最后,调用HystrixInvokable.execute(),触发Hystrix执行流程。

三、总结

本章,我详细分析了在Spring Cloud中使用原生Hystrix时的初始化流程。这个流程的本质就是通过Spring AOP创建代理类,然后在AOP横切逻辑中封装动态创建HystrixCommand的代理,并触发Command的执行,整合的思路和上一章Feign集成的思路是类似的。

从下一章开始,我就会分析Hystrix调用的底层原理,由于Hystrix大量使用了RxJava这个响应式框架,所以整个分析流程我不会像分析Eureka、Ribbon、Feign那样按照调用流程去分析(也没办法那样做)。所以,我会忽略掉RxJava的各种使用,直接讲解Hystrix自身最核心的处理逻辑。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文