0%

OepnFeign-Http组件

OpenFeign

介绍

Feign 就是一个 Http 客户端的模板,目标是减少 HTTP API 的复杂性,希望能将 HTTP 远程服务调用做到像 RPC 一样易用。

Feign 集成 RestTemplate、Ribbon 实现客户端的负载均衡,并对RestTemplate进行封装,开发者只需要声明一个接口并通过注解进行简单的配置即可完成服务调用,更加符合面向接口编程的宗旨。客户端在调用服务端时也不需要再关注请求的方式、地址以及是 forObject 还是 forEntity,结构明了,耦合低,简化开发。

在 Feign 的基础上,衍生出了 Spring Cloud OpenFeign

openFeign 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequestMapping 等。OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

总的就是,openFeign 作为微服务架构下服务间调用的解决方案,是一种声明式、模板化的 HTTP 的模板,使 HTTP 请求就像调用本地方法一样,通过 openFeign 可以替代基于 RestTemplate 的远程服务调用,并且默认集成了 Ribbon 进行负载均衡。

注解 说明
@FeignClient 该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现负载均衡和服务调用。
@EnableFeignClients 该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。
@RequestMapping Spring MVC 注解,在 Spring MVC 中使用该注解映射请求,通过它来指定控制器(Controller)可以处理哪些 URL 请求,相当于 Servlet 中 web.xml 的配置。
@GetMapping Spring MVC 注解,用来映射 GET 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET) 。
@PostMapping Spring MVC 注解,用来映射 POST 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.POST) 。

初始化流程

openFeign

替换 HTTP Client

openFeign 默认使用 JDK 原生的 URLConnection 发送 HTTP 请求,没有连接池,但是对每个地址会保持一个长连接,即利用 HTTP 的 persistence connection。

在生产环境中,因性能问题,通常不使用默认的 http client,替换为如下Client:

ApacheHttpClient

  • 内部有@ConditionalOnClass(ApacheHttpClient.class) ,且参数@ConditionalOnProperty(“feign.httpclient.enabled”, matchIfMissing = true) 因此引入包即可使用

    1
    2
    3
    4
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    </dependency>

OkHttp

  • 内部有@ConditionalOnClass(OkHttpClient.class),因此需引入依赖

    1
    2
    3
    4
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    </dependency>
  • 但同时有条件注解@ConditionalOnProperty(“feign.okhttp.enabled”) ,需在配置文件开启才可使用

    1
    2
    3
    feign:
    okhttp:
    enabled: true

同时引入时状态

根据FeignLoadBalancerAutoConfiguration顺序

HttpClientFeignLoadBalancerConfiguration.class
OkHttpFeignLoadBalancerConfiguration.class
DefaultFeignLoadBalancerConfiguration.class

当加载时没有出现feignClient Bean,则先加载HttpClientFeignLoadBalancerConfiguration,当继续运行加载到OkHttpFeignLoadBalancerConfiguration时,因已加载feignClient Bean,此刻只有HttpClientFeignLoadBalancerConfiguration生效,依次类推。

当都没有feignClient Bean时,才会加载Default方法DefaultFeignLoadBalancerConfiguration

  • FeignLoadBalancerAutoConfiguration

    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
    package org.springframework.cloud.openfeign.loadbalancer;


    /**
    * An autoconfiguration that instantiates {@link BlockingLoadBalancerClient}-based
    * implementations of {@link Client}. In order to use this load-balancing mechanism, the
    * Ribbon-based implementation has to be disabled by setting
    * <code>spring.cloud.loadbalancer.ribbon.enabled</code> to <code>true</code>.
    *
    * @author Olga Maciaszek-Sharma
    * @since 2.2.0
    */
    @ConditionalOnClass(Feign.class)
    @ConditionalOnBean(BlockingLoadBalancerClient.class)
    @AutoConfigureBefore(FeignAutoConfiguration.class)
    @AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
    @EnableConfigurationProperties(FeignHttpClientProperties.class)
    @Configuration(proxyBeanMethods = false)
    // Order is important here, last should be the default, first should be optional
    // see
    // https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
    @Import({ HttpClientFeignLoadBalancerConfiguration.class,
    OkHttpFeignLoadBalancerConfiguration.class,
    DefaultFeignLoadBalancerConfiguration.class })
    public class FeignLoadBalancerAutoConfiguration {

    }
  • HttpClientFeignLoadBalancerConfiguration

    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
    package org.springframework.cloud.openfeign.loadbalancer;


    /**
    * Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
    * object that uses {@link ApacheHttpClient} under the hood.
    *
    * @author Olga Maciaszek-Sharma
    * @since 2.2.0
    */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(ApacheHttpClient.class)
    @ConditionalOnBean(BlockingLoadBalancerClient.class)
    @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
    @Import(HttpClientFeignConfiguration.class)
    class HttpClientFeignLoadBalancerConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @Conditional(OnRetryNotEnabledCondition.class)
    public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
    HttpClient httpClient) {
    ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
    return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnBean(LoadBalancedRetryFactory.class)
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
    havingValue = "true", matchIfMissing = true)
    public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
    HttpClient httpClient,
    List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
    AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
    ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
    return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient,
    loadBalancedRetryFactories.get(0));
    }

    }
  • OkHttpFeignLoadBalancerConfiguration

    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
    package org.springframework.cloud.openfeign.loadbalancer;

    /**
    * Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
    * object that uses {@link OkHttpClient} under the hood.
    *
    * @author Olga Maciaszek-Sharma
    * @since 2.2.0
    */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(OkHttpClient.class)
    @ConditionalOnProperty("feign.okhttp.enabled")
    @ConditionalOnBean(BlockingLoadBalancerClient.class)
    @Import(OkHttpFeignConfiguration.class)
    class OkHttpFeignLoadBalancerConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @Conditional(OnRetryNotEnabledCondition.class)
    public Client feignClient(okhttp3.OkHttpClient okHttpClient,
    BlockingLoadBalancerClient loadBalancerClient) {
    OkHttpClient delegate = new OkHttpClient(okHttpClient);
    return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnBean(LoadBalancedRetryFactory.class)
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
    havingValue = "true", matchIfMissing = true)
    public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
    okhttp3.OkHttpClient okHttpClient,
    List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
    AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
    OkHttpClient delegate = new OkHttpClient(okHttpClient);
    return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient,
    loadBalancedRetryFactories.get(0));
    }

    }
  • DefaultFeignLoadBalancerConfiguration

    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
    package org.springframework.cloud.openfeign.loadbalancer;

    /**
    * Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
    * object that uses {@link Client.Default} under the hood.
    *
    * @author Olga Maciaszek-Sharma
    * @since 2.2.0
    */
    @Configuration(proxyBeanMethods = false)
    class DefaultFeignLoadBalancerConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @Conditional(OnRetryNotEnabledCondition.class)
    public Client feignClient(BlockingLoadBalancerClient loadBalancerClient) {
    return new FeignBlockingLoadBalancerClient(new Client.Default(null, null),
    loadBalancerClient);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnBean(LoadBalancedRetryFactory.class)
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
    havingValue = "true", matchIfMissing = true)
    public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
    List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
    AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
    return new RetryableFeignBlockingLoadBalancerClient(
    new Client.Default(null, null), loadBalancerClient,
    loadBalancedRetryFactories.get(0));
    }

    }

openFeign 默认的超时时间

默认分别是连接超时时间 10秒、读超时时间 60秒,源码在 feign.Request.Options#Options() 这个方法中

openFeign 集成了 Ribbon,如果openFeign没有设置对应得超时时间,那么将会采用Ribbon的默认超时时间(Ribbon 的默认超时连接时间、读超时时间都是是1秒,源码在org.org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute()方法中)。由之产生两种方案,如下:

  • 设置openFeign的超时时间

    1
    2
    3
    4
    5
    6
    7
    feign:
    client:
    config:
    default:
    # 超时设置
    connectTimeout: 1500
    readTimeout: 1500
  • 设置Ribbon的超时时间

    1
    2
    3
    4
    5
    6
    # 全局配置
    ribbon:
    # 建立连接后,服务器读取到可用资源的时间
    ConnectTimeout: 5000
    #建立连接所用的时间,适用于网络状况正常的情况下,两端两端连接所用的时间
    ReadTimeout: 5000

来源:

Feign https://blog.csdn.net/a745233700/article/details/122916856

openFeign 与 Ribbon 的联系:https://www.cnblogs.com/unchain/p/13405814.html

https://developer.51cto.com/article/699142.html

https://www.bilibili.com/video/BV1WU4y1z74p

https://developer.aliyun.com/article/900571

https://www.jb51.net/article/219498.htm

http://c.biancheng.net/springcloud/open-feign.html