什么是 feign?
来自官网的解释: Feign makes writing java http clients easier https://github.com/OpenFeign/feign
在使用 feign 之前, 我们怎么发送请求?
拿 okhttp 举例:
- public static void post(String url, HashMap<String, String> paramsMap){
- OkHttpClient mOkHttpClient = new OkHttpClient();
- FormBody.Builder formBodyBuilder = new FormBody.Builder();
- Set<String> keySet = paramsMap.keySet();
- for(String key:keySet) {
- String value = paramsMap.get(key);
- formBodyBuilder.add(key,value);
- }
- FormBody formBody = formBodyBuilder.build();
- Request request = new Request
- .Builder()
- .post(formBody)
- .url(url)
- .build();
- try (Response response = mOkHttpClient.newCall(request).execute()) {
- System.out.println(response.body().string());
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- public static void main(String[] args) {
- HashMap<String,String> paramsMap = new HashMap<String, String>() ;
- paramsMap.put("name","小明");
- paramsMap.put("html","<html>...");
- post("https://10.0.4.147:8015/crcc",paramsMap);
- }
有了 feign 之后我们怎么发送请求
- @FeignClient(value = "FooBarService", configuration = FooBarServiceFeignConfiguration.class)
- public interface FooBarService {
- @RequestMapping(value = "/foo", method = RequestMethod.GET)
- String foo(@RequestParam(value = "param1") String param1);
- @RequestMapping(value = "/bar", method = RequestMethod.POST)
- String bar(@RequestParam(value = "param1") String param1, @RequestParam(value = "param2") String param2);
- }
- @Autowired
- FooBarService fooBarService;
- public String foo() {
- return fooBarService.foo("rt");
- }
几行代码就能搞定, 很大程度的节省了工作量, 而且客户端和服务端关于接口的定义只需要写一次
具体的利弊我们这里就不做分析, 在微服务盛行的现在, 服务之间的调用单纯使用 http client 的场景已经基本不存在
spring cloud openfeign 的加载过程
上面的代码为什么接口没有实现类也可以使用, 是不是跟 mybatis 一样使用了代理?
没错, 接口最后都会生成代理实现
(右键新标签打开可查看大图)
spring cloud openfeign 关于代理的生成过程
(右键新标签打开可看大图)
feign 的 REST Client API 思想
JAX-RS 标准
最新的 REST 接口标准为 JAX-RS2.0, 但是标准是供参考不能拿来直接吃的, 具体还是要通过实现了标准的中间件来进行使用
JAX-RS2.0 之 REST Client API
摘自《Java RESTful web Service 实战 (第 2 版)》
为什么 JAX-RS2.0 这么去抽象, 我们这里暂不深入去思考, 先拿来主义
jersey
jersey 是 JAX-RS 标准的参考实现, 是 Java 领域中最纯正的 REST 服务开发框架, 例如 eureka 也是使用 jersey 来做 REST 接口和客户端发送请求, 详见《服务发现之 eureka》
jersey 之 REST Client API
官方文档示例
- ClientConfig clientConfig = new ClientConfig();
- clientConfig.register(MyClientResponseFilter.class);
- clientConfig.register(new AnotherClientFilter());
- Client client = ClientBuilder.newClient(clientConfig);
- client.register(ThirdClientFilter.class);
- WebTarget webTarget = client.target("http://example.com/rest");
- webTarget.register(FilterForExampleCom.class);
- WebTarget resourceWebTarget = webTarget.path("resource");
- WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld");
- WebTarget helloworldWebTargetWithQueryParam =
- helloworldWebTarget.queryParam("greeting", "Hi World!");
- Invocation.Builder invocationBuilder =
- helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE);
- invocationBuilder.header("some-header", "true");
- Response response = invocationBuilder.get();
- System.out.println(response.getStatus());
- System.out.println(response.readEntity(String.class));
feign 与 JAX-RS2.0
feign 主要是作为客户端发送请求, 所以也是参考对照了 JAX-RS2.0 标准
feign 并不是 REST Client, 只是参考了 REST Client 的实现, 具体的目标还是为了更简单的实现 http client 请求
feign 中怎么进行对应呢?
为什么这么去抽象我们这里也暂不深入研究 (更深层的 JAX-RS 为什么这么抽象还未探明)
feign 代理的执行流程和关键对象
代理生成时用到了什么组件, 代理执行时用到了什么组件?
上文我们知道, 所有请求最后都交给了 MethodHandler 来执行, 所以我们重点关注 MethodHandler 即可
(右键新标签打开可查看大图)
MethodHandler 的关键对象和执行请求的流程
1.RequestTemplate.Factory
创建 RequestTemplate 的工厂, 包含 MethodMetadata 和 Encoder 对象
其中 MethodMetadata 是应用初始化时 Contract 解析 @RequestMapping @RequestParam 等注解而来的中间数据
2.Encoder
报文压缩 gzip 等
3.RequestInterceptor
为请求附加一些信息, 类似 spring mvc 的 interceptor 拦截器
4.Target
主要是把 @FeignClient 里的 url 拼接到 RequestTemplate
5.Options
用于请求的参数配置
6.Decoder
解析返回报文, 如果返回 404, 判断 decode404==true 则解析, 否则交给 ErrorDecoder 解析
7.ErrorDecoder
请求错误处理
8.Logger.Level
日志等级, 包含四种 none basic headers full
9.Logger
对应的配置为 LoggerFactory, 记录日志用
10.Retryer
重试, DefaultRetryer 默认会重试 5 次
11.Client
真正执行 http 请求的客户端, 可以配置, 默认由 FeignRibbonClientAutoConfiguration 进行配置结合 ribbon 使用
spring cloud openfeign 的配置
配置的优先级顺序
(右键新标签打开可查看大图)
properties 和 spring bean 可以配置的内容
主要还是配置我们上面 feign 的关键对象, properties 和 spring bean 可配置的项如下
同 ribbon 一样, spring-cloud-openfeign 的配置也是懒加载, 每个 feignclient 都可以有自己个性化的配置, 且配置是懒加载的, 但是为每个接口生成代理的时候已经去注册和使用了相关的配置, 其实懒加载没有用了.
所以只实现了最终目的: 每个 feignclient 都可以有自己个性化的配置
这里的 FeignContext 跟 ribbon 的 SpringClientFactory 同理
- public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
- public FeignContext() {
- super(FeignClientsConfiguration.class, "feign", "feign.client.name");
- }
- }
feign 与 ribbon 对接的关键点
feign 与 ribbon 对接主要还是在 Client 对象上做文章, 将 Client 替换为继承 Ribbon 模板的实现类, 这样就可以对执行请求前后做一些负载逻辑, 详见《负载均衡之 ribbon》
来源: https://www.cnblogs.com/roytian/p/12196790.html