feign架构 原理解析
什么是feign?
来自官网的解释:Feign makes writing java http clients easier
在使用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》
feign架构 原理解析的更多相关文章
- RocketMQ架构原理解析(四):消息生产端(Producer)
RocketMQ架构原理解析(一):整体架构 RocketMQ架构原理解析(二):消息存储(CommitLog) RocketMQ架构原理解析(三):消息索引(ConsumeQueue & I ...
- RocketMQ架构原理解析(一):整体架构
RocketMQ架构原理解析(一):整体架构 RocketMQ架构原理解析(二):消息存储(CommitLog) RocketMQ架构原理解析(三):消息索引(ConsumeQueue & I ...
- Tomcat 架构原理解析到架构设计借鉴
Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说如今就没有必要深入学习它了么?学习它我们又 ...
- Scrapy框架的架构原理解析
爬虫框架--Scrapy 如果你对爬虫的基础知识有了一定了解的话,那么是时候该了解一下爬虫框架了.那么为什么要使用爬虫框架? 学习框架的根本是学习一种编程思想,而不应该仅仅局限于是如何使用它.从了解到 ...
- RocketMQ架构原理解析(三):消息索引
一.概述 "索引"一种数据结构,帮助我们快速定位.查询数据 前文我们梳理了消息在Commit Log文件的存储过程,讨论了消息的落盘策略,然而仅仅通过Commit Log存储消息是 ...
- 分布式架构原理解析,Java开发必修课
1. 分布式术语 1.1. 异常 服务器宕机 内存错误.服务器停电等都会导致服务器宕机,此时节点无法正常工作,称为不可用. 服务器宕机会导致节点失去所有内存信息,因此需要将内存信息保存到持久化介质上. ...
- RocketMQ架构原理解析(二):消息存储
一.概述 由前文可知,RocketMQ有几个非常重要的概念: broker 服务端,负责存储.收发消息 producer 客户端1,负责产生消息 consumer 客服端2,负责消费消息 既然是消息队 ...
- NET/ASP.NET Routing路由(深入解析路由系统架构原理)(转载)
NET/ASP.NET Routing路由(深入解析路由系统架构原理) 阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模 ...
- Spring Cloud底层原理解析
概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...
随机推荐
- Java Integer类的缓存
首先看一段代码(使用JDK 5),如下: public class Hello { public static void main(String[] args) { int a = 1000, b = ...
- java什么叫面向对象?
面向对象(Object-Oriented,简称OO)就是一种常见的程序结构设计方法. 面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的 ...
- Vue自定义指令配置修饰符和传参
一和二,请参考https://www.cnblogs.com/zui-ai-java/p/11109213.html 三.index.html <!DOCTYPE html> <ht ...
- Python--day41--守护线程
1,守护线程:守护线程会在主线程结束之后等待其他子线程的结束才结束 拓展--守护进程:守护进程随着主进程代码的执行结束而结束 代码示例:守护线程.py import time from threadi ...
- python类中的双下划线方法
__getitem__,__setitem__和__delitem__ 实现了对象属性的字典化操作. class Person: def __init__(self, name, age, hobby ...
- java用普通类如何实现枚举功能
用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能. 1.私有的构造方法. 2.每个元素分别用一个公有的静态成员变量表示. 可以有若干公有方法或抽象方法.采用 ...
- vue-learning:30 - component - 组件实例的引用方式
组件实例的引用方式 ref / $refs $root $parent $children 扩展查找任意组件实例的方法 在vue开发的项目中,通常会以一棵嵌套的组件树的形式来组织项目. 都存在着一个根 ...
- ssh 简写
<< remotessh remotessh 这一对之间,可以写多个命令,换行即可. 否者就简单的 双引号,里面每个命令用分号隔开. 注意: 在远程服务器上的执行权限. 注意:login密 ...
- 美国权威媒体CRN预测:2020年值得关注的10个新兴云计算趋势
云计算在过去一年里极速发展,其速度之快,让人难以预测未来会发生什么.即使依照这些趋势而新发明的技术在不断变化并且极其脆弱,而推动这些趋势和技术不断发展的企业和组织最终也很有可能发生变化,但不妨碍这些大 ...
- 虚拟DOM学习与总结
虚拟DOM 虚拟DOM简而言之就是,用JS去按照DOM结构来实现的树形结构对象,一般称之为虚拟节点(VNode) 优点:解决浏览器性能问题 ,真实DOM频繁排版与重绘的效率是相当低的,虚拟DOM进行频 ...