文章目录
一、什么是Feign
它是Netflix开发的一个声明式、模板化的HTTP客户端, Feign的目标是帮助Java工程师更快捷、优雅地调用HTTP API//RESTful api。
OpenFeign是Java应用中编写Web服务的客户端的利器,基于第三方HTTP客户端类库,声明式地构建Web服务的客户端应用,以调用Web服务。OpenFeign在Java应用中,负责处理与远程Web服务的请求响应,最大限度降低编码复杂性。
另外,Feign被无缝集成到了SpringCloud微服务框架,使用Feign后,可以非常方便项目SpringCloud微服务技术。
如果项目使用了SpringCloud技术,那就就可以更加方便的声明式使用Feign。如果没有使用SpringCloud,使用Feign也非常之简单。Netflix Feign目前改名为OpenFeign(即:OpenFeign曾用名Netflix Feign)。
Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket。Feign最初是为了降低统一绑定Denominator到HTTP API的复杂度,不区分是否支持Restful。Feign旨在通过最少的资源和代码来实现和HTTP API的连接。通过可定制的解码器和错误处理,可以编写任意的HTTP API。
Feign的中文名称翻译过来是伪装
那么Feign伪装的是什么呢?答案很明确,Feign伪装的是服务提供者。
二、为什么使用Feign
在Feign之前,我们在进行HTTP调用的时候,更多的是选择使用原生的Apache HTTP Client 库来调用。所以在项目中会存在诸如HtppUtil之类的公共方法。
如果有很多系统,那么系统间的交互就会很多,那么这种HTTP调用就会非常多,那么你就会发现其实很多的调用方做了很多重复的事情。
Map<String, Object> resMap = HttpUtil.sendPostRequest("url", JsonUtil.toJSONString(resMap));
String content = (String) resMap.get("content");
JSONObject resJson = JSONObject.parseObject(content);
if (JsonUtil.CODE_SUCCESS.equals(resJson.getString("code")))
{
// Do Something
return resultMap;
}
else
{
return null;
}
有已下重复工作:
- 如果是POST请求,那么POST请求的序列化需要调用方来做
- 如果是GET请求,那么如果在URL中有参数,需要调用方手动拼凑
- HTTP请求的返回结果需要调用方来序列化
- 如果是POST请求,那么还需要区分请求是application/x-www-form-
- urlencoded形式的还是application/json格式的
针对以上的问题,其实都是可以通过优化HttpUtil中的提供的方法来实现的。但是也会导致方法签名很复杂,而且会同时存在各种重载的静态方法,搞得调用者该一脸懵逼。
- 如上所述,优化去除一些重复工作,使代码简洁。
三、Spring Cloud Feign设计原理
Spring Cloud Feign设计原理
参考URL: https://www.jianshu.com/p/8c7b92b4396c
四、Feign性能优化
OpenFeign默认使用Java的HttpURLConnection作为HTTP请求客户端。
OpenFeign也可以直接使用已有公共第三方HTTP客户端类库,如Apache HttpComponents, OKHttp,编写Java客户端以访问HTTP服务。
默认情况下Feign底层是使用HttpURLConnection发送请求的,众所周知HttpURLConnection是没有使用连接池的,所以可以针对这点进行优化。例如,将底层的http请求客户端为更换为Apache的HttpClient或者OkHttp等使用了连接池的http客户端,据测试使用了连接池后可以提升15%左右的性能。
Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接,这也就是为什么默认情况下Feign的性能很差的原因。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端,我们项目内部使用的就是OkHttp3作为Http 客户端。
使用 OkHttp 来发送 Feign 的请求
OkHttpClient 使用 OkHttp 来发送 Feign 的请求,OkHttp 支持 SPDY (SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),并有更好的控制http请求。
要让 Feign 使用 OkHttp ,你需要将 OkHttp 加入到你的环境变量中区,然后配置 Feign 使用 OkHttpClient,如下:
GitHub github = Feign.builder()
.client(new OkHttpClient())
.target(GitHub.class, "https://api.github.com");
Maven依赖:
<!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-gson -->
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>8.18.0</version>
</dependency>
五、 传统Spring项目使用Feign声明式服务调用思路
在纯spring环境中,网上有2中思路,一个中直接原生使用,一种是模仿spring boot 写自定义注解修改 接口类,让动态代理,让spring实例化管理该接口类,在其他服务中可利用@Autowire注入 该接口即可使用。
这里我暂时采用思路1,感觉原生够用。后一种改造比较多,还不如切换spring boot框架。
思路1:原生feign使用即可
Spring MVC项目使用Feign声明式服务调用
参考URL: https://www.liangzl.com/get-article-detail-137662.html
[推荐]Feign-独立使用-实战
参考URL: https://www.cnblogs.com/crazymakercircle/p/10243749.html
思路2:自定义类似@FeignClient注解
[强烈推荐-可完全参考]在纯Spring环境中使用Feign来进行声明式HTTP调用
参考URL: jianshu.com/p/48ba120641fb
之前,公司使用spring cloud,记得使用@feign http访问其它微服务非常好用。但是传统的spring项目如何使用该组件呢?
这里的传统 Spring项目指的是没有使用 spring boot的 spring项目,例如 ssm。
经过网上信息分析总结,Spring 不能像我们在spring boot中那样很直接的使用
@FeignClient的接口运行时都会生成代理类,由代理类去调用远程服务。
如果想spring boot中那样,使用注解,业务直接注入那种方式使用,在Spring环境下,我们还需要做一些工作。
否则查看官方demo https://github.com/OpenFeign/feign
类似如下,这个 Feign.builder()这种,在使用时实例化你的feign接口实例化一个其对象,来调用。这种方式可以用来也可以用来单元测试。
六、独立使用(原生)Feign具体过程
官网: https://github.com/OpenFeign/feign
Spring MVC项目使用Feign声明式服务调用
参考URL: https://www.liangzl.com/get-article-detail-137662.html
1. maven引入Feign
Spring整合Feign的具体实现
需要的maven引入
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.1.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
2. 封装返回实体
@Data
public class Response<T> {
private Integer code;
private String msg;
private T data;
}
3. 创建接口,声明接口方法
import feign.Param;
import feign.RequestLine;
public interface RemoteService {
@RequestLine("GET /users/list?name={name}")
String getOwner(@Param(value = "name") String name);
}
通过@RequestLine指定HTTP协议及URL地址
4. 原生feign调用
RemoteService remoute = Feign.builder().decoder(new JacksonDecoder())
.requestInterceptor(template -> template.header("appid", appid))
.target(RemoteService.class, “http://www.baidu.com/”);
Response<String> response = remote.getOwner(body.toJSONString());
七、feign使用总结
1. 使用 feign时设置header信息
方法一:
@FeignClient(url = "XX_url", value = "XXService")
public interface XXService {
@RequestMapping(value = "/xx", method = RequestMethod.POST)
@Headers({"Content-Type: application/json","Accept: application/json"})
String sendDing(String params);
}
方法二:通过实现RequestInterceptor接口,完成对所有的Feign请求,设置Header
demo1
@Component
public class FeginClientConfig {
@Bean
public RequestInterceptor headerInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
// 示例
requestTemplate.header("Content-Type", "application/json");
}
};
}
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
demo2
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
@Component
public class FeignInterceptor implements RequestInterceptor{
public void apply(RequestTemplate requestTemplate){
requestTemplate.header("hotelId", "111111");
}
}
这种方式,是针对所有feign请求进行拦截,设置Header
方法3: Feign实例的 .requestInterceptor方法
网上demo
/**
* @author Liangzhifeng
* date: 2018/9/13
*/
public interface UserInfoFeignClient {
/**
* 根据token获取用户信息
* @param token
* @return
*/
@RequestMapping(value = "/user/info/1.0", method = RequestMethod.POST)
Object getUserInfoByToken(@RequestParam("token") String token);
}
/**
* @author Liangzhifeng
* date: 2018/9/15
*/
@Component
public class AuthorityConfig {
/**
* 授权信息Header的key
*/
public static final String OAUTH_KEY = "token";
/**
* 授权信息Header的值的前缀
*/
public static final String OAUTH_VALUE_PREFIX = "Bearer ";
@Autowired
private Client client;
public UserInfoFeignClient userInfoFeignClient(String token) {
UserInfoFeignClient authorityServiceLoginInvoker = Feign.builder().client(client)
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.contract(new SpringMvcContract())
.requestInterceptor(template -> template.header(OAUTH_KEY, OAUTH_VALUE_PREFIX + token))
.target(UserInfoFeignClient.class, GlobalConstant.AUTHORITY_SERVICE_LINK);
return authorityServiceLoginInvoker;
}
}
总结:经过测试 方法一 @Headers 有一定要求限制,你给的参数必须是常量,如果你想用变量拼接,IDEA 会提醒报错。
方法二:比较适用全局统一的处理情况。
方法三:灵活,可以自由定制。
根据项目情况,大家选择即可。
八、参考
Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)
参考URL: http://blog.didispace.com/spring-cloud-alibaba-2/
Spring之FactoryBean
参考URL: https://www.jianshu.com/p/f4dca40c55c1
spring中BeanDefinitionRegistryPostProcessor的作用
参考URL: https://blog.csdn.net/u013905744/article/details/93635846
【推荐学习一下-作者有demo测试】fastJson和Jackson对比
参考URL: https://www.jianshu.com/p/a3ebb54445be