Feign是一种声明式、模板化的REST客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,省略了大量的http请求代码。
开始使用Feign
Spring Cloud提供了完备的Feign配置启动功能。
- 在pom.xml中引入Feign依赖,添加如下内容:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 使用httpclient作为远程调用的实现,如果不引入默认使用jdk的HttpURLConnection -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 在有@Configuration注解的类中增加@EnableFeignClients注解开启功能,比如在springboot启动类中:
@Configuration
@EnableFeignClients
public class MyApplication {}
- 声明接口
@FeignClient(name = "query", url = "http://www.baidu.com")
public interface BaiduClient {
@RequestMapping(value = "/")
Map<String,Object> query(@RequestParam("wd") String wd);
}
- 直接在其他bean中注入接口并调用
@RestController
public class TestController {
@Autowired
private BaiduClient client;
@GetMapping("/query")
public Object name(@RequestParam("wd") String wd) {
return client.query(wd);
}
}
开发配置
Feign通过@FeignClient注解声明一个service,@FeignClient必须声明在inteface上。Feign通过在接口方法上使用注解声明具体的请求url地址、绑定参数。openFeign直接使用了Spring MVC中的地址映射和参数绑定注解,如@RequestMapping,@PathVariable,@RequestParam等。需要注意的是,在定义各参数绑定时@RequestParam、@RequestHeader等可以指定参数名称的注解,他们的value也不能少,在Spring MVC程序中,这些注解会根据参数名称来作为默认值,但是Feign中绑定参数必须通过value属性指明具体的参数名,不然抛出口 lllegalStateException 异常, value 属性不能为空。
FeignClient语法
@FeignClient注解的常用属性如下:
- name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
- url: 手动指定@FeignClient调用的地址
- decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出 FeignException
- configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
- fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
- fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
- path: 定义当前FeignClient的统一前缀
Feign请求超时问题
Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了
解决方案有三种,以feign为例。
方法一
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
该配置是让Hystrix的超时时间改为5秒
方法二 hystrix.command.default.execution.timeout.enabled: false
该配置,用于禁用Hystrix的超时时间
方法三 feign.hystrix.enabled: false
该配置,用于索性禁用feign的hystrix。该做法除非一些特殊场景,不推荐使用。
这篇文章对Feign的超时配置做了详细分析,值得学习。
“Spring Cloud中Feign的使用方法以及原理解析”http://www.itersblog.com/archives/24.html
参数绑定
GET请求
对应form表单形式的get请求"GET /get?id=xx&username=yyy",可以按如下方式声明:
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
如果参数比较多,可以通过Map批量声明参数:
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get2(@RequestParam Map<String, Object> map);
POST请求
通常的post请求都是以json格式提交数据的,Feign支持将对象直接转换为json的请求方式:
@RequestMapping(value = "/post", method = RequestMethod.POST)
public User post(@RequestBody User user);
有些历史遗留系统post请求是按from表单的格式提交的,这时需要特殊处理:
@FeignClient(name = "xxx", url = "http://www.itmuch.com/", configuration = TestFeignClient.FormSupportConfig.class)
public interface TestFeignClient {
@PostMapping(value = "/test",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
)
void post(Map<String, ?> queryParam);
class FormSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
// new一个form编码器,实现支持form表单提交
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
}
说明:这里FormSupportConfig内部有@Bean声明,可以不加@Configuration注解。
自定义配置
@FeignClient的configuration()提供了我们自定义请求配置的能力。在实际工作中我们会遇到很多个性化的需求都可以通过这个属性注入。比如加密验签、特殊映射方案处理、超时配置等。
接口安全认证
调用需要安全认证的系统时,我们需要添加认证信息,这时可以通过RequestInterceptor对请求实现拦截和自动注入。feign提供了http basic安全认证拦截器 BasicAuthRequestInterceptor。
@FeignClient(value = "auth-server", configuration = AuthServerClient.AuthServerFeignClientConfiguration.class)
public interface AuthServerClient {
@GetMapping("/oauth/token_key")
Map getTokenKey();
@Configuration
class AuthServerFeignClientConfiguration {
@Value("${authServer.security.clientId}")
private String clientId;
@Value("${authServer.security.clientSecret}")
private String clientSecret;
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor(clientId, clientSecret);
}
}
}
如果服务提供方需要OAuth2.0认证,可以使用spring-cloud-security中的OAuth2FeignRequestInterceptor。
注意事项
自己做Feign扩展和个性化使用时,有一些需要注意的事项,参考下文:
正确的使用方法和性能优化注意事项: http://tietang.wang/2016/09/06/%E5%BE%AE%E6%9C%8D%E5%8A%A1/Feign%E4%BD%BF%E7%94%A8%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/
参考文档
FeignClient注解及参数: https://www.cnblogs.com/smiler/p/10689894.html
Spring Cloud中,如何使用Feign构造多参数的请求:https://www.jianshu.com/p/7ce46c0ebe9d
@FeignClient注解详解: http://www.hxstrive.com/article/569.htm
使用Feign实现Form表单提交: https://www.jianshu.com/p/54b5e82a6d19
Feign实战配置与详细解析: https://my.oschina.net/u/3260714/blog/880050
聊聊feign的RequestInterceptor:https://juejin.im/post/5d2de4bc518825413a51ce4a