广告位联系
返回顶部
分享到

深入分析Spring Cloud负载均衡器架构选型

java 来源:互联网 作者:佚名 发布时间:2024-12-27 22:29:17 人浏览
摘要

我们这次项目主要从RestTemplate 和 Feign 进行选型分析。 一、Spring Cloud Feign分析 Feign是另外一种客户端负载均衡实现。 我在该模块写了Feign Client的示例代码。 【1】spring-cloud-web-demo-api为服务的

我们这次项目主要从RestTemplate 和 Feign 进行选型分析。

一、Spring Cloud Feign分析

Feign是另外一种客户端负载均衡实现。

我在该模块写了Feign Client的示例代码。
【1】spring-cloud-web-demo-api为服务的sdk模块
【2】spring-cloud-web-demo-service为提供接口服务的模块
【3】spring-cloud-web-demo-client为模拟调用服务的模块

首先在spring-cloud-web-demo-api模块,定义Feign API。spring-cloud-web-demo为spring-cloud-web-demo-service暴露的服务名。

1

2

3

4

5

6

@FeignClient(value = "spring-cloud-web-demo")

public interface UserFeign {

    @GetMapping(value = "/user/getUserById", produces = "application/json;charset=utf-8")

    Object getUserById(@RequestParam(value = "id", required = false) Long id);

    //省略

}

然后通过ClientAutoConfiguration自动装配。(client直接引入api包就可以使用,不需要再EnableFeignClients)

1

2

3

4

5

6

@Configuration

@EnableFeignClients("net.teaho.demo.spring.cloud.web.api")

public class ClientAutoConfiguration {

}

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

net.teaho.demo.spring.cloud.web.api.config.ClientAutoConfiguration

在service模块如以往Spring MVC般实现api模块接口即可。

1

2

3

4

5

6

7

8

9

@RestController

public class UserController implements UserFeign {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Override

    public Object getUserById(Long id) {

        return "{\"id\":1, \"name\": \"test\"}";

    }

    //省略

}

在Client模块,注入bean后直接调用。

1

2

3

4

5

6

7

8

9

@Component

@Slf4j

public class TestService {

    @Autowired

    private RestTemplate restTemplate;

    public Object getOneUser(){

        return userController.getUserById(1L);

    }

}

二、RestTemplate分析

写了具有客户端负载均衡能力的RestTemplate的请求代码。

类似这样定义:

1

2

3

4

5

@Bean

@LoadBalanced

public RestTemplate restTemplate() {

    return new RestTemplate();

}

RestTemplate究竟是如何利用注册中心实现客户端负载均衡的呢?

实现方式: 就是将上面所说的LoadBalancerInterceptor负载均衡拦截器加到标注了@LoadBalanced的RestTemplate实例中。 LoadBalancerInterceptor拦截器会在执行过程中获取并设置适合的目标请求实例,重新构造请求URI。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// 将配置中标注了@LoadBalanced的RestTemplate注入到这里

@LoadBalanced

@Autowired(required = false)

private List<RestTemplate> restTemplates = Collections.emptyList();

//将注册的RestTemplateCustomizer(RestTemplate自定义器)集合处理上面的restTemplates集合

@Bean

public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(

        final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {

    return () -> restTemplateCustomizers.ifAvailable(customizers -> {

        for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {

            for (RestTemplateCustomizer customizer : customizers) {

                customizer.customize(restTemplate);

            }

        }

    });

}

三、技术选型

最终选择使用OpenFeign,下面说说原因。

和RestTemplate比起来,OpenFeign显得更适合Spring Boot微服务。

Open Feign相当于(HTTP)RPC,相比起RestTemplate,它直接显式将API声明以JAVA接口形式标识出来。 并且因为底层用的动态代理,它还可以(无感知地)替换底层实现。比如,github上就有替换底层逻辑的repo – Open Feign+Dubbo的RPC实现。

通过sdk包的形式,方便了调用,不需要像RestTemplate一样,客户端自行拼接上一串请求参数。在代码编写上也清晰。

要使用就必须知道OpenFeign是怎么实现的呢?

四、OpenFeign 初始化分析

流程图如下:

看看前面例子里我们引入的OpenFeign的东西
【1】@EnableFeignClients(“net.teaho.demo.spring.cloud.web.api”)
【2】@FeignClient(value = “spring-cloud-web-demo”) 还有自动装配引入的
【3】FeignRibbonClientAutoConfiguration
【4】FeignClientsConfiguration

我们就从这两个注解开始分析源码。
【1】首先看@FeignClient注解。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

//给接口标注成一个REST调用方

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface FeignClient {

    //服务名,可以带协议前缀,也可以用${property.name}关联一个配置值。

    @AliasFor("name")

    String value() default "";

    @Deprecated

    String serviceId() default "";

    //bean name

    String contextId() default "";

    @AliasFor("value")

    String name() default "";

    /**

     * Sets the <code>@Qualifier</code> value for the feign client.

     */

    String qualifier() default "";

    //直接指定一个地址,比如http://localhost:12345,一般用于调试

    String url() default "";

    boolean decode404() default false;

    /**

     * A custom <code>@Configuration</code> for the feign client. Can contain override

     * <code>@Bean</code> definition for the pieces that make up the client, for instance

     * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.

     *

     * @see FeignClientsConfiguration for the defaults

     */

    //可用于覆盖FeignClient默认设置

    Class<?>[] configuration() default {};

    //回滚类,像我的例子中定义的回滚类必须实现UserFeign接口,看https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback

    Class<?> fallback() default void.class;

    //如果需要对异常做诊断可用此属性,https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-feign-hystrix-fallback

    Class<?> fallbackFactory() default void.class;

    //路径前缀

    String path() default "";

    //标记bean是否为primary

    boolean primary() default true;

}

【2】接下来重点关注@EnableFeignClients注解是如何扫描FeignClient接口的。

1

2

3

4

5

6

7

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(FeignClientsRegistrar.class)

public @interface EnableFeignClients {

    //省略

}

嗯,发现没有,就是FeignClientsRegistrar做处理的。来分析下重点方法registerFeignClients和registerFeignClient

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,

        ResourceLoaderAware, EnvironmentAware {

    public void registerFeignClients(AnnotationMetadata metadata,

            BeanDefinitionRegistry registry) {

        //classPath扫描器

        ClassPathScanningCandidateComponentProvider scanner = getScanner();

        scanner.setResourceLoader(this.resourceLoader);

        //ClassPathScanningCandidateComponentProvider扫描的basePackage集合

        Set<String> basePackages;

        Map<String, Object> attrs = metadata

                .getAnnotationAttributes(EnableFeignClients.class.getName());

        //扫描器用于扫描标注了@FeignClient类的拦截器

        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(

                FeignClient.class);

        final Class<?>[] clients = attrs == null ? null

                : (Class<?>[]) attrs.get("clients");

        //clients属性为空,以@EnableFeignClients的value、basePackage等为根包扫描

        if (clients == null || clients.length == 0) {

            scanner.addIncludeFilter(annotationTypeFilter);

            basePackages = getBasePackages(metadata);

        }

        //@EnableFeignClients的clients属性不为空,解析clients的类和根包

        else {

            final Set<String> clientClasses = new HashSet<>();

            basePackages = new HashSet<>();

            for (Class<?> clazz : clients) {

                basePackages.add(ClassUtils.getPackageName(clazz));

                clientClasses.add(clazz.getCanonicalName());

            }

            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {

                @Override

                protected boolean match(ClassMetadata metadata) {

                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");

                    return clientClasses.contains(cleaned);

                }

            };

            scanner.addIncludeFilter(

                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));

        }

        //1.根据basePackage找到目标@FeignClient接口

        //2.检查是否为接口

        //3.将找到的接口注册为FeignClientFactoryBean

        for (String basePackage : basePackages) {

            Set<BeanDefinition> candidateComponents = scanner

                    .findCandidateComponents(basePackage);

            for (BeanDefinition candidateComponent : candidateComponents) {

                if (candidateComponent instanceof AnnotatedBeanDefinition) {

                    // verify annotated class is an interface

                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;

                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();

                    Assert.isTrue(annotationMetadata.isInterface(),

                            "@FeignClient can only be specified on an interface");

                    Map<String, Object> attributes = annotationMetadata

                            .getAnnotationAttributes(

                                    FeignClient.class.getCanonicalName());

                    String name = getClientName(attributes);

                    registerClientConfiguration(registry, name,

                            attributes.get("configuration"));

                    registerFeignClient(registry, annotationMetadata, attributes);

                }

            }

        }

    }

    private String getClientName(Map<String, Object> client) {

        if (client == null) {

            return null;

        }

        String value = (String) client.get("contextId");

        if (!StringUtils.hasText(value)) {

            value = (String) client.get("value");

        }

        if (!StringUtils.hasText(value)) {

            value = (String) client.get("name");

        }

        if (!StringUtils.hasText(value)) {

            value = (String) client.get("serviceId");

        }

        if (StringUtils.hasText(value)) {

            return value;

        }

        throw new IllegalStateException("Either 'name' or 'value' must be provided in @"

                + FeignClient.class.getSimpleName());

    }

    private void registerFeignClient(BeanDefinitionRegistry registry,

            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {

        String className = annotationMetadata.getClassName();

        BeanDefinitionBuilder definition = BeanDefinitionBuilder

                .genericBeanDefinition(FeignClientFactoryBean.class);

        validate(attributes);

        definition.addPropertyValue("url", getUrl(attributes));

        definition.addPropertyValue("path", getPath(attributes));

        String name = getName(attributes);

        definition.addPropertyValue("name", name);

        String contextId = getContextId(attributes);

        definition.addPropertyValue("contextId", contextId);

        definition.addPropertyValue("type", className);

        definition.addPropertyValue("decode404", attributes.get("decode404"));

        definition.addPropertyValue("fallback", attributes.get("fallback"));

        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));

        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";

        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

        boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);

        if (StringUtils.hasText(qualifier)) {

            alias = qualifier;

        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,

                new String[] { alias });

        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

    }

}

可以看到最后注册beanDefinition时,我们看到注册了FeignClientFactoryBean这一FactoryBean。 我们看看工厂bean FeignClientFactoryBean是如何构造对象的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,

        ApplicationContextAware {

//省略

    @Override

    public Object getObject() throws Exception {

        return getTarget();

    }

    <T> T getTarget() {

        //1.获取FeignContext,在FeignAutoConfiguration声明

        FeignContext context = applicationContext.getBean(FeignContext.class);

        //2.构造Feign builder

        Feign.Builder builder = feign(context);

        //3.如果没有设置url参数

        if (!StringUtils.hasText(this.url)) {

            if (!this.name.startsWith("http")) {

                url = "http://" + this.name;

            }

            else {

                url = this.name;

            }

            //4.设置path

            url += cleanPath();

            //5.获取Client(用于执行最终HTTP/HTTPS请求,比如LoadBalancerFeignClient),

            //构造反射实例

            return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,

                    this.name, url));

        }

        //存在url参数,构造非loadBalance的请求实例(target)

        if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {

            this.url = "http://" + this.url;

        }

        String url = this.url + cleanPath();

        Client client = getOptional(context, Client.class);

        if (client != null) {

            if (client instanceof LoadBalancerFeignClient) {

                // not load balancing because we have a url,

                // but ribbon is on the classpath, so unwrap

                client = ((LoadBalancerFeignClient)client).getDelegate();

            }

            builder.client(client);

        }

        Targeter targeter = get(context, Targeter.class);

        return (T) targeter.target(this, builder, context, new HardCodedTarget<>(

                this.type, this.name, url));

    }

    //在FeignContext中获取一些在FeignClientsConfiguration中声明,Feign需要用到的组件

    protected Feign.Builder feign(FeignContext context) {

        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);

        Logger logger = loggerFactory.create(this.type);

        // @formatter:off

        Feign.Builder builder = get(context, Feign.Builder.class)

                // required values

                .logger(logger)

                .encoder(get(context, Encoder.class))

                .decoder(get(context, Decoder.class))

                .contract(get(context, Contract.class));

        // @formatter:on

        configureFeign(context, builder);

        return builder;

    }

    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,

            HardCodedTarget<T> target) {

        //获取Client

        Client client = getOptional(context, Client.class);

        if (client != null) {

            builder.client(client);

            //从Context获取Targeter,Targeter用于生成最终target实例(对应我的例子是被调用的通过反射生成的UserFeign实例)

            Targeter targeter = get(context, Targeter.class);

            return targeter.target(this, builder, context, target);

        }

        throw new IllegalStateException(

                "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");

    }

//省略

}

在非调试情况下(即我们没设置url参数), 我们来看看targeter.target(this, builder, context, target)做了什么。

Targeter接口是构造被请求的代理bean的类。有两个实现类HystrixTargeter、DefaultTargeter。

HystrixTargeter会比默认的多设置一些回滚措施,用到Feign的Contract属性, 我会先从DefaultTargeter说起。

DefaultTargeter会通过Feign.Builder#target(Target target)生成实例。我们来看看代码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public abstract class Feign {

//省略

  public static class Builder {

    private final List<RequestInterceptor> requestInterceptors =

        new ArrayList<RequestInterceptor>();

    private Logger.Level logLevel = Logger.Level.NONE;

    private Contract contract = new Contract.Default();

    private Client client = new Client.Default(null, null);

    private Retryer retryer = new Retryer.Default();

    private Logger logger = new NoOpLogger();

    private Encoder encoder = new Encoder.Default();

    private Decoder decoder = new Decoder.Default();

    private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();

    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();

    private Options options = new Options();

    private InvocationHandlerFactory invocationHandlerFactory =

        new InvocationHandlerFactory.Default();

    private boolean decode404;

    private boolean closeAfterDecode = true;

    private ExceptionPropagationPolicy propagationPolicy = NONE;

    //省略

    public <T> T target(Class<T> apiType, String url) {

      return target(new HardCodedTarget<T>(apiType, url));

    }

    public <T> T target(Target<T> target) {

      return build().newInstance(target);

    }

    //默认实现就是创建一个ReflectiveFeign实例

    public Feign build() {

      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =

          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,

              logLevel, decode404, closeAfterDecode, propagationPolicy);

      ParseHandlersByName handlersByName =

          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,

              errorDecoder, synchronousMethodHandlerFactory);

      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);

    }

  }

//省略

}

在解读ReflectiveFeign前介绍几个概念:
1、InvocationHandlerFactory 是控制反射方法分发的接口,create方法返回InvocationHandler。
2、InvocationHandlerFactory.MethodHandler 最终将对代理类方法调用转换成HTTP请求的地方,请看实现类SynchronousMethodHandler
3、InvocationHandlerFactory.Default 默认实现,作为构造参数传入ReflectiveFeign,create方法创建的是new ReflectiveFeign.FeignInvocationHandler(target, dispatch)。
4、ReflectiveFeign.ParseHandlersByName 作为构造参数传入ReflectiveFeign,核心方法apply(Target key)先将标注了@FeignClient的接口的方法解析出待处理的元数据List, 然后创建出方法名和方法处理器的map映射Map<String, MethodHandler>String是方法名,方法处理器通过SynchronousMethodHandler.Factory#create创建。
5、FeignInvocationHandler 为处理一般方法的处理器
6、DefaultMethodHandler 为处理接口默认方法的处理器

有了以上介绍,接下来简单分析ReflectiveFeign的newInstance方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

public class ReflectiveFeign extends Feign {

  private final ParseHandlersByName targetToHandlersByName;

  private final InvocationHandlerFactory factory;

  private final QueryMapEncoder queryMapEncoder;

  ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,

      QueryMapEncoder queryMapEncoder) {

    this.targetToHandlersByName = targetToHandlersByName;

    this.factory = factory;

    this.queryMapEncoder = queryMapEncoder;

  }

  ..

  /**

   * creates an api binding to the {@code target}. As this invokes reflection, care should be taken

   * to cache the result.

   */

  @SuppressWarnings("unchecked")

  @Override

  public <T> T newInstance(Target<T> target) {

    //创建方法名和方法处理器的map映射

    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();

    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {

      if (method.getDeclaringClass() == Object.class) {

        continue;

      //判断是否为接口的默认方法,DefaultMethodHandler的处理逻辑是直接调用会原接口的default方法

      } else if (Util.isDefault(method)) {

        DefaultMethodHandler handler = new DefaultMethodHandler(method);

        defaultMethodHandlers.add(handler);

        methodToHandler.put(method, handler);

      } else {

        //方法处理map

        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));

      }

    }

    InvocationHandler handler = factory.create(target, methodToHandler);

    //jdk动态代理创建对象

    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),

        new Class<?>[] {target.type()}, handler);

    //将默认方法处理器也绑定到代理对象上

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {

      defaultMethodHandler.bindTo(proxy);

    }

    return proxy;

  }

  static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;

    private final Map<Method, MethodHandler> dispatch;

    //省略

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      //自定义的equals、hashCode和toString的处理

      if ("equals".equals(method.getName())) {

        try {

          Object otherHandler =

              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;

          return equals(otherHandler);

        } catch (IllegalArgumentException e) {

          return false;

        }

      } else if ("hashCode".equals(method.getName())) {

        return hashCode();

      } else if ("toString".equals(method.getName())) {

        return toString();

      }

      //分发调用到对应方法的InvocationHandlerFactory.MethodHandler

      return dispatch.get(method).invoke(args);

    }

    //省略

}

初始化完成。

五、OpenFeign 执行分析

上图是OpenFeign构造的代理对象被调用时的时序图。
1、代理对象被执行
2、找到对应SynchronousMethodHandler进行方法调用。
3、构造RequestTemplate
4、LoadBalancerFeignClient执行负载请求
5、FeignLoadBalancer通过ILoadBalancer选择合适Server,通过Server重组URI,通过RibbonRequest持有的Client执行实际HTTP请求包装成Response。
6、SynchronousMethodHandler通过Decoder将请求响应用Decoder解码成最终结果。

下面介绍执行过程中涉及到源码中的部分组件。
1、RequestTemplate 是一个HTTP请求内容的抽象。
2、RequestTemplate.Factory 将方法参数解析成RequestTemplate。
3、Retryer 我在上面的时序图没有标注出来,实际上它在SynchronousMethodHandler的执行中控制重试逻辑。
4、RequestInterceptor 在SynchronousMethodHandler发起执行中,会使用该拦截器对RequestTemplate进行处理。这是一个拓展点。
5、Logger 执行请求时打日志(在debug时打)。默认为Logger.Level.NONE即不打日志,可以增加bean覆盖。

  • Logger.Level.NONE 不打印信息
  • Logger.Level.BASIC 打印请求url和响应码。
  • Logger.Level.HEADERS 打印BASIC信息外加header信息
  • Logger.Level.FULL 打印所有

6、LoadBalancerFeignClient Client接口的实现类,是具有负载均衡能力的Client。Client接口为执行HTTP的接口,Client.Default是最终发出HTTP请求的类。

7、FeignLoadBalancer FeignLoadBalancer通过ILoadBalancer选择合适Server,通过Server重组URI,通过RibbonRequest持有的Client执行实际HTTP请求包装成Response。
8、LoadBalancerCommand ribbon的rxJava实现,执行负载流程逻辑的组件。
9、ILoadBalancer ribbon的负载均衡器抽象。

熔断: 在FeignClientsConfiguration中, 当配置了feign.hystrix.enabled,Feign Builder使用HystrixFeign.builder()。

所以build的时候新建HystrixInvocationHandler和HystrixDelegatingContract实例。

1

2

3

4

5

6

7

8

9

10

11

12

Feign build(final FallbackFactory<?> nullableFallbackFactory) {

   super.invocationHandlerFactory(new InvocationHandlerFactory() {

     @Override

     public InvocationHandler create(Target target,

                                     Map<Method, MethodHandler> dispatch) {

       return new HystrixInvocationHandler(target, dispatch, setterFactory,

           nullableFallbackFactory);

     }

   });

   super.contract(new HystrixDelegatingContract(contract));

   return super.build();

}

来看看HystrixInvocationHandler的hystrix调用代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

final class HystrixInvocationHandler implements InvocationHandler {

   //省略

  @Override

  public Object invoke(final Object proxy, final Method method, final Object[] args)

      throws Throwable {

   //省略

    HystrixCommand<Object> hystrixCommand =

        new HystrixCommand<Object>(setterMethodMap.get(method)) {

          //实际执行

          @Override

          protected Object run() throws Exception {

            try {

              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);

            } catch (Exception e) {

              throw e;

            } catch (Throwable t) {

              throw (Error) t;

            }

          }

          @Override

          protected Object getFallback() {

            if (fallbackFactory == null) {

              return super.getFallback();

            }

            try {

              //用配置的fallbackFactory创建fallback实例

              Object fallback = fallbackFactory.create(getExecutionException());

              Object result = fallbackMethodMap.get(method).invoke(fallback, args);

              //根据fallback对象的returntype解析包装内的结果返回

              if (isReturnsHystrixCommand(method)) {

                return ((HystrixCommand) result).execute();

              } else if (isReturnsObservable(method)) {

                // Create a cold Observable

                return ((Observable) result).toBlocking().first();

              } else if (isReturnsSingle(method)) {

                // Create a cold Observable as a Single

                return ((Single) result).toObservable().toBlocking().first();

              } else if (isReturnsCompletable(method)) {

                ((Completable) result).await();

                return null;

              } else {

                return result;

              }

            } catch (IllegalAccessException e) {

              // shouldn't happen as method is public due to being an interface

              throw new AssertionError(e);

            } catch (InvocationTargetException e) {

              // Exceptions on fallback are tossed by Hystrix

              throw new AssertionError(e.getCause());

            }

          }

        };

    //根据方法的return去返回结果

    if (Util.isDefault(method)) {

      return hystrixCommand.execute();

    } else if (isReturnsHystrixCommand(method)) {

      return hystrixCommand;

    } else if (isReturnsObservable(method)) {

      // Create a cold Observable

      return hystrixCommand.toObservable();

    } else if (isReturnsSingle(method)) {

      // Create a cold Observable as a Single

      return hystrixCommand.toObservable().toSingle();

    } else if (isReturnsCompletable(method)) {

      return hystrixCommand.toObservable().toCompletable();

    }

    return hystrixCommand.execute();

  }

   //省略

}


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • windows java -jar无法启动jar包简单的解决方法

    windows java -jar无法启动jar包简单的解决方法
    1. 问题 我们项目是使用nacos作为注册中心以及配置中心,我们本地使用idea 进行服务配置以及启动发现没有问题,然后我们的服务经过maven
  • 深入分析Spring Cloud负载均衡器架构选型

    深入分析Spring Cloud负载均衡器架构选型
    我们这次项目主要从RestTemplate 和 Feign 进行选型分析。 一、Spring Cloud Feign分析 Feign是另外一种客户端负载均衡实现。 我在该模块写了Feign
  • Java报错java.awt.AWTException: AWT的解决方法
    在Java图形用户界面(GUI)编程中,java.awt.AWTException是一个常见的异常,它通常与AWT(Abstract Window Toolkit)组件相关。这个异常可能在尝试进
  • Maven管理多模块应用的统一版本号实现
    在大型项目中,尤其是涉及多个子模块的Maven项目,统一管理版本号是一个棘手的问题。尤其当项目的子模块多达几十个时,手动修改每个模
  • SpringBoot增量部署发布的实现

    SpringBoot增量部署发布的实现
    由于项目依赖的jar越来越多,Springboot默认的打包方式是将整个项目打包成一个jar包,每次发布时,打包后的jar越来越大,更新一个很小的功
  • SpringBoot将多个Excel打包下载的实现
    在Spring Boot应用中,如果你需要将多个Excel文件打包成一个ZIP文件并提供下载,你可以使用一些Java库来帮助完成这个任务。这里我将展示如何
  • SpringBoot事务传播机制介绍
    在 Spring Boot 开发中,事务是一个至关重要的概念,尤其是在涉及多层业务逻辑或者多个数据库操作时。Spring 提供了强大的事务管理功能,使
  • Java使用JNA调用DLL文件
    1、什么是JNA? JNA(Java Native Access)是一个在 Java 中调用本地代码的开源框架,提供了一种简单、高效的方式来访问本地动态链接库(如.d
  • Java8 CompletableFuture异步编程解读介绍

    Java8 CompletableFuture异步编程解读介绍
    CompletableFuturede介绍 Java 8 引入了 CompletableFuture 类,这是 Java 异步编程的一个重要进展。 CompletableFuture 提供了一种基于未来结果的异步编程模
  • 基于Maven pom文件使用分析
    project Maven 是一个强大的构建和依赖管理工具,pom.xml 文件是 Maven 项目的核心配置文件,用于定义项目的构建、依赖关系、插件、目标等。它
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计