zoukankan      html  css  js  c++  java
  • Spring Cloud(三):Web服务客户端之Feign

    前文介绍了实现客户端负载均衡的Ribbon,但直接使用Ribbon的API来实现服务间的调用相对较为繁琐,服务间的调用能否像本地接口调用一样便捷、透明,更符合编程习惯呢?Feign就是用来干这事的。

    Feign

    Feign是一个声明式的Web服务客户端,让服务之间的调用变得非常简单——定义带@FeignClient注解的接口,本地直接@Autowired 接口,通过调用接口的方法来实现远程服务的调用。

     支持的注解包括Feign注解与JAX-RS(Java API for RESTful Web Services)注解。

     每一个Feign的客户端都包含一系列对应的组件,Spring Cloud通过FeignClientsConfiguration 为每一个命名的Feign客户端创建一个组件集合,包括feign.Decoder,feign.Encoder,feign.Contract等。

    Feign提供的默认bean实现及说明

    Bean类型默认实现类说明
    Decoder ResponseEntityDecoder ResponseEntityDecoder封装了SpringDecoder,解码器,将服务的响应消息进行解码
    Encoder SpringEncoder 编码器
    Logger Slf4jLogger 日志框架
    Contract SpringMvcContract 支持注解契约,使用SpringMvcContract可以对Spring MVC注解提供支持
    Feign.Builder HystrixFeign.Builder 使用断路器来装饰Feign接口
    Client LoadBalancerFeignClient 如果是ribbon则 LoadBalancerFeignClient, 如果是spring cloud LoadBalancer 则 FeignBlockingLoadBalancerClient,默认ribbon

    跟Ribbon类似,可以通过配置类来自定义Feign客户端,如

    @FeignClient(name = "hello-service", configuration = CustomConfiguration.class)
    public interface StoreClient {
        //..
    }
    
    public class CustomConfiguration {
        @Bean
        public Contract feignContract() {
            return new feign.Contract.Default();
        }
        @Bean
        public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
            return new BasicAuthRequestInterceptor("user", "password");
        }
    }

    这样Feign客户端就包含了FeignClientsConfiguration 与CustomConfiguration 中定义的组件,并且后者会覆盖前者(即自定义配置的优先级高于默认配置)。 

    自定义配置类不需要加注解@Configuration,如果加了且被@ComponentScan扫描到,则将成为所有Feign客户端的默认配置

    同样Feign客户端也支持通过配置文件来配置

    feign:
        client:
            config:
                feignName:
                    connectTimeout: 5000
                    readTimeout: 5000
                    loggerLevel: full
                    errorDecoder: com.example.SimpleErrorDecoder
                    retryer: com.example.SimpleRetryer
                    requestInterceptors:
                        - com.example.FooRequestInterceptor
                        - com.example.BarRequestInterceptor
                    decode404: false
                    encoder: com.example.SimpleEncoder
                    decoder: com.example.SimpleDecoder
                    contract: com.example.SimpleContract


    对于应用于所有Feign客户端的全局默认配置,也可以通过两种方式 

    1. 通过@EnableFeignClients 的defaultConfiguration 属性指定默认配置类

    2. 在配置文件中通过名称为default的配置实现

      feign:
          client:
              config:
                  default:
                      connectTimeout: 5000
                      readTimeout: 5000
                      loggerLevel: basic

    优先级同Ribbon, 配置文件>自定义配置类>默认的FeignClientsConfiguration


    案例演示

    本文案例演示基于前面搭建的springcloud-eureka 与 springcloud-eureka-client 两个示例项目。

    1. 新建springcloud-feign项目,pom.xml中加入依赖

      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

       spring-cloud-starter-openfeign 包含了spring-cloud-starter-netflix-ribbon 与 spring-cloud-starter-loadbalancer。

    1. 启动类加上@EnableFeignClients 注解

      @SpringBootApplication
      @EnableFeignClients
      public class FeignApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(FeignApplication.class, args);
          }
      }
    2. 定义Feign client(feign client支持继承)

      @FeignClient("hello-service")
      public interface HelloClient extends BaseHelloClient{
      
          @RequestMapping("hello/param")
          String hello(@SpringQueryMap QueryParam param);
      }
    3. 调用Feign client

      @RestController
      public class FeignController {
      
          @Autowired
          private HelloClient helloClient;
      
          @RequestMapping("feign")
          public String feignTest(){
              return "调用Hello-service返回:" + helloClient.hello();
          }
      
          @RequestMapping("feign/param")
          public String feignTestParam(QueryParam param) {
              return "调用Hello-service返回:" + helloClient.hello(param);
          }
      }

    依次启动三个项目,调用http://localhost:8083/feign 能正常返回调用hello-service的结果。

     本示例项目还通过@SrpingQueryMap 注解实现了Feign对 pojo用于GET请求参数的支持。如果不加@SrpingQueryMap, 则pojo参数是无法通过Feign client传递的,可去掉注解自行验证下。


    一些知识点

    1. 如果需要定制化产生的查询参数map,可以实现并注入一个自定义的 QueryMapEncoder bean

    2. Feign client的日志可通过feign client接口的全路径名进行配置,如logging.level.project.user.UserClient: DEBUG,默认为NONE(即不打印日志)。全局设置

      @Configuration
      public class FooConfiguration {
          @Bean
          Logger.Level feignLoggerLevel() {
              return Logger.Level.FULL;
          }
      }

      可设置的level值

      • NONE:不记录日志 ,默认

      • BASIC:只记录请求方法,url,以及响应状态码与执行时间

      • HEADERS:包括BASIC与请求、响应头

      • FULL:包括请求与响应的headers,body,metadata

    1. Feign默认使用Ribbon来做负载均衡,可通过配置spring.cloud.loadbalancer.ribbon.enabled=false 来使用spring cloud loadbalancer(目前Ribbon处于维护状态,近期内不做更新)

    2. 可通过配置feign.okhttp.enabled=true 或 feign.httpclient.enabled=true 来使用OkHttpClient 或ApacheHttpClient, 默认使用的是JDK 原生的URLConnection 发送HTTP请求,没有连接池

    3. 如果需要在RequestInterceptor 中使用ThreadLocal中的变量, 那么要么禁用Hystrix,要么设置hystrix的线程隔离策略为SEMAPHORE

      feign:
          hystrix:
              enabled: false
      # 或者
      hystrix:
          command:
              default:
                  execution:
                      isolation:
                          strategy: SEMAPHORE
    4. 使用有Hystrix fallback的Feign时,会在ApplicationContext中存在多个同类型bean, 导致@Autowired 失效。为了解决这个问题,Spring cloud netflix 将所有feign实例标为@Primary,如果要关闭该特性, 可将@FeignClient的 primary属性置为false。

      @FeignClient(name = "hello", primary = false)
      public interface HelloClient {
          // ...
      }

    本文示例代码:https://github.com/ronwxy/springcloud-demos


    认真生活,快乐分享
    欢迎关注微信公众号:空山新雨的技术空间

  • 相关阅读:
    字体下载大宝库:30款好看的免费英文字体
    jQuery Mapael – 呈现动态的矢量地图
    Qt:用 __thread 关键字让每个线程有自己的全局变量
    从C++到Qt(舍弃IDE或qmake、cmake等工具的束缚,尝试通过几个例子)
    C++11(及现代C++风格)和快速迭代式开发
    EventBus + Redis发布订阅模式
    并发、并行和高并发
    Span<T>和Memory<T>
    Lucene.Net做一个简单的搜索引擎-全文索引
    技术架构演变
  • 原文地址:https://www.cnblogs.com/spec-dog/p/12217166.html
Copyright © 2011-2022 走看看