Spring Boot 进阶 | 01 使用Feign作为HTTP客户端调用远程HTTP服务

2022年6月3日14:19:42

Feign 简介

Spring Cloud 的 Feign 支持的一个中心概念就是命名客户端。Feign客户端使用@FeignClient 注册组合成组件,按需调用远程服务器。使用FeignClientsConfiguration 创建一个新的集合作为每个命名客户端的ApplicationContext,包含feign.Decoderfeign.Encoderfeign.Contract

可以使用JerseyCXF 这些来写一个 Rest 或 SOAP 服务的java客服端,也可以直接使用Apache HttpClient 来实现。但是 Feign 的目的是尽量的减少资源和代码来实现和 HTTP API 的连接。通过自定义的编码解码器以及错误处理,你可以编写任何基于文本的 HTTP API。

Feign 通过注解注入一个模板化请求进行工作。只需在发送之前关闭它,参数就可以被直接的运用到模板中。然而这也限制了 Feign,只支持文本形式的API,它在响应请求等方面极大的简化了系统。同时,它也是十分容易进行单元测试的。

Spring Cloud 应用在启动时,Feign 会扫描标有@FeignClient 注解的接口生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate 对象,该对象封装了 HTTP 请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign 的模板化就体现在这里。

maven依赖

<dependencies>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>

Feign客户端接口(消费者)

使用@FeignClient 请发起请求:

@FeignClient(value="user", url="${addr.url}")publicinterfaceUserClient{// Feign 独有的注解方式@RequestLine("GET /user/index")
        Stringindex();@RequestMapping(value="/get0/{id}", method= RequestMethod.GET)
        UserfindById(@PathVariable("id") Long id);@RequestMapping(value="/get1", method= RequestMethod.GET)
        Userget1(@RequestParam("id") Long id,@RequestParam("name") String name);@RequestMapping(value="/get2", method= RequestMethod.GET)
        Userget2(@RequestParam Map<String, Object> map);@RequestMapping(value="/hello2", method=RequestMethod.GET)
        Userhello2(@RequestHeader("name") String name,@RequestHeader("age") Integer age);@RequestMapping(value="/hello3", method=RequestMethod.POST)
        Stringhello3(@RequestBody User user);}

当前工程中有和 Feign Client 中一样的 Endpoint 时,Feign Client 的类上不能用 @RequestMapping 注解,否则当前工程该 endpoint http 请求且使用 accpet 时会报404。但是如果不包含 Accept header 时,请求是可以的。
不在 Feign Client 上使用 @RequestMapping 注解,无论是否包含Accept,请求都是可以的:

@FeignClient(name="card", url="http://localhost:7913", 
	fallback= CardFeignClientFallback.class,
	configuration= FeignClientConfiguration.class)publicinterfaceCardFeignClient{@RequestMapping(value="/v1/card/balance", method= RequestMethod.POST, 
                produces= MediaType.APPLICATION_JSON_VALUE)
    Infoinfo();}

Feign将方法签名中方法参数对象序列化为请求参数放到 HTTP 请求中的过程,是由编码器 Encoder 完成的。同理,将 HTTP 响应数据反序列化为 Java 对象是由解码器 Decoder 完成的。
默认情况下,Feign会将标有 @RequestParam 注解的参数转换成字符串添加到 URL 中,将没有注解的参数通过 Jackson 转换成 json 放到请求体中。注意,如果在 @RequetMapping 中的 method 将请求方式指定为 POST ,那么所有未标注解的参数将会被忽略,例如:

@RequestMapping(value="/group/{groupId}", method= RequestMethod.GET)voidupdate(@PathVariable("groupId") Integer groupId,@RequestParam("groupName") String groupName, 
                             DataObject obj);

此时因为声明的是 GET 请求没有请求体,所以 obj 参数就会被忽略。
在 Spring Cloud 环境下,Feig n的 Encoder 只会用来编码没有添加注解的参数。如果你自定义了Encoder, 那么只有在编码 obj 参数时才会调用你的 Encoder。对于 Decoder, 默认会委托给 SpringMVC 中的MappingJackson2HttpMessageConverter 类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder 才会被调用。ErrorDecoder 的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用 Feign 接口的地方被捕获到。我们目前就通过 ErrorDecoder 来使 Feign 接口抛出业务异常以供调用者处理。

Feign 在默认情况下使用的是 JDK 原生的 URLConnection 发送 HTTP 请求,没有连接池,但是对每个地址会保持一个长连接,即利用 HTTP 的 persistence connection 。我们可以用 Apache 的 HTTP Client替换 Feign 原始的 http client,从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从 Brixtion.SR5 版本开始支持这种替换,首先在项目中声明 Apache HTTP Client 和 feign-httpclient 依赖:

<!-- 使用Apache HttpClient替换Feign原生httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>com.netflix.feign</groupId><artifactId>feign-httpclient</artifactId><version>${feign-httpclient}</version></dependency>

然后在 application.properties 中添加 feign.httpclient.enabled=true

配置文件application.yml

#feignfeign:hystrix:enabled:truehttpclient:enabled:trueaddr:url: http://10.164.13.166:8080/msg-center/v1/sms/send

配置类Configuration

使用了配置 @Configuration 参数,自己定义 Configuration 类来自定义 FeignClientsConfiguration,并且 Configuration 类的类路径不能在启动类 Application 的扫描路径下,否则会覆盖该项目所有的 Feign 接口的默认配置

package com.spring.feigin.config;import feign.auth.BasicAuthRequestInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@ConfigurationpublicclassEurekaConfiguration{// 配置只允许使用Feign自己的注解url方式:@RequestLine@Beanpublic BasicAuthRequestInterceptorbasicAuthRequestInterceptor(){returnnewBasicAuthRequestInterceptor("zhihao.miao","123456");}// 配置eureka的登录名和密码@Beanpublic BasicAuthRequestInterceptorbasicAuthRequestInterceptor(){returnnewBasicAuthRequestInterceptor("zhihao.miao","123456");}}

定义主体启动类

@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublicclassOrderApplication{publicstaticvoidmain(String[] args){
       SpringApplication.run(OrderApplication.class,args);}}

feign使用Hystrix

添加依赖
<!-- 整合hystrix,其实feign中自带了hystrix,引入该依赖主要是为了使用其中的hystrix-metrics-event-stream,用于dashboard --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-hystrix</artifactId></dependency>
feign接口注解中,增加fallback指定类
@FeignClient(value="${name}",  url="${addr.url}", fallback= UserFallBack.class)
指定类中处理熔断的后续逻辑
@Slf4j@ComponentpublicclassPosMemberClientFallBackimplementsPosMemberClient{@Overridepublic StringaddMember(MemberDTO memberDTO){
        log.warn("调用会员服务失败");return("调用服务失败,熔断”);}}
配置文件
#hystrixhystrix:command:default:execution:isolation:strategy: THREADthread:timeoutInMilliseconds:30000threadpool:default:coreSize:500#缺省为10
修改启动类

在启动类上添加@EnableHystrix 注解

@SpringBootApplication@EnableFeignClients@EnableDiscoveryClient@EnableHystrixpublicclassStartApplication{publicstaticvoidmain(String[] args){
 SpringApplication.run(StartApplication.class, args);}}

配置的fallback class也必须在FeignClient Configuration中实例化,否则会报
java.lang.IllegalStateException: No fallback instance of type class异常。

@ConfigurationpublicclassFooConfiguration{@Bean@Scope("prototype")public Feign.BuilderfeignBuilder(){return Feign.builder();}//实例化fallback@Beanpublic HystrixClientFallbackfb(){returnnewHystrixClientFallback();}}

在 Spring Cloud 中,Feign 和Ribbon 在整合了 Hystrix 后,可能会出现首次调用失败的问题,要如何解决该问题呢?
Hystrix 默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入 fallback 代码。
而首次请求往往会比较慢(因为 Spring 的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了。
解决方案 :
方法一
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
该配置是让Hystrix的超时时间改为5秒
方法二
hystrix.command.default.execution.timeout.enabled: false
该配置,用于禁用Hystrix的超时时间
方法三
feign.hystrix.enabled: fals
该配置,用于索性禁用feign的hystrix。该做法除非一些特殊场景,不推荐使用。

Feign的扩展配置

#Hystrix支持,如果为true,hystrix库必须在classpath中feign:hystrix:enabled:false#请求和响应GZIP压缩支持compression:request:enabled:trueresponse:enabled:true#支持压缩的mime typescompression:request:enabled:truemime-types: text/xml,application/xml,application/jsonmin-request-size:2048# 日志支持logging:level:project:user:UserClient: DEBUG
  • 作者:月码
  • 原文链接:https://blog.csdn.net/u010541670/article/details/80068575
    更新时间:2022年6月3日14:19:42 ,共 6478 字。