Feign总结

目录

Feign简介

Feign能干什么

Feign与Ribbon区别

Feign和Open Feign的区别

Feign和Dubbo的区别

Feign的使用

Feign的调用方式

Feign中使用熔断器

Feign的核心原理

Feign远程调用的基本流程

Feign 远程调用的重要组件

远程接口的本地JDK Proxy代理实例

调用处理器 InvocationHandler

默认的调用处理器 FeignInvocationHandler

方法处理器 MethodHandler

Feign 客户端组件 feign.Client

Feign 远程调用的执行流程

与 FeignInvocationHandler 相关的远程调用执行流程


注意:本文参考  

docs/system-design/framework/springcloud/springcloud-intro.md · SnailClimb/JavaGuide - Gitee.com

外行人都能看懂的SpringCloud,错过了血亏!

20000 字的 Spring Cloud 总结,从此任何问题也难不住你

Feign原理 (图解) - 疯狂创客圈 - 博客园

微服务中远程调用Dubbo与Feign对比 - 悬铃木pp - 博客园

Feign简介

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。

它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

为了简化我们的开发,Spring Cloud Feign出现了!它基于 Netflix Feign 实现,整合了 Spring Cloud Ribbon 与 Spring Cloud Hystrix,  除了整合这两者的强大功能之外,它还提
供了声明式的服务调用(不再通过RestTemplate)。

Feign能干什么

Feign旨在使编写Java Http客户端变得更容易。

@Autowired
private RestTemplate restTemplate;
// 这里是提供者A的ip地址,但是如果使用了 Eureka 那么就应该是提供者A的名称
private static final String SERVICE_PROVIDER_A = "http://localhost:8081";@PostMapping("/judge")
public boolean judge(@RequestBody Request request) {String url = SERVICE_PROVIDER_A + "/service1";// 是不是太麻烦了???每次都要 url、请求、返回类型的return restTemplate.postForObject(url, request, Boolean.class);
}

这样每次都调用 RestRemplate 的 API 是否太麻烦,我能不能像调用原来代码一样进行各个服务间的调用呢?

在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。

在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

Feign集成了Ribbon

利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

Feign与Ribbon区别

Ribbon和Feign都是用于调用其他服务的,不过方式不同。

1.启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。

2.服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。

3.调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。

Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,

不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。

Feign和Open Feign的区别

Feign本身不支持Spring MVC的注解,它有一套自己的注解

OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。 OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口, 并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

Feign和Dubbo的区别

Feign与Dubbo功能上有很多类似的地方,因为都是专注于远程调用这个动作。比如注册中心解耦、负载均衡、失败重试熔断、链路监控等。

Dubbo除了注册中心需要进行整合,其它功能都自己实现了,而Feign大部分功能都是依赖全家桶的组件来实现的。Dubbo小而专一,专注于远程调用。而Spring全家桶而言,远程调用只是一个重要的功能而已。

协议支持方面:

Feign更加优雅简单。Feign是通过REST API实现的远程调用,基于Http传输协议,服务提供者需要对外暴露Http接口供消费者调用,服务粒度是http接口级的。通过短连接的方式进行通信,不适合高并发的访问。Feign追求的是简洁,少侵入(因为就服务端而言,在SpringCloud环境下,不需要做任何额外的操作,而Dubbo的服务端需要配置开放的Dubbo接口)。

Dubbo方式更灵活。Dubbo是通过RPC调用实现的远程调用,支持多传输协议(Dubbo、Rmi、http、redis等等),可以根据业务场景选择最佳的方式,非常灵活。默认的Dubbo协议:利用Netty,TCP传输,单一、异步、长连接,适合数据量小、高并发和服务提供者远远少于消费者的场景。Dubbo通过TCP长连接的方式进行通信,服务粒度是方法级的。

从协议层选择看,Dubbo是配置化的,更加灵活。Dubbo协议更适合小数据高并发场景。

通信性能方面:

从通信的性能上来分析,SpringCloud的通信采用Openfeign(feign)组件。

Feign基于Http传输协议,底层实现是rest。从OSI 7层模型上来看rest属于应用层。

在高并发场景下性能不够理想,成为性能瓶颈(虽然他是基于Ribbon以及带有熔断机制可以防止雪崩),需要改造。具体需要改造的内容需要时再研究。

Dubbo框架的通信协议采用RPC协议,属于传输层协议,性能上自然比rest高。提升了交互的性能,保持了长连接,高性能。

Dubbo性能更好,比如支持异步调用、Netty性能更好。Dubbo主要是配置而无需改造。

RPC REST
耦合性强耦合松耦合
消息协议二进制 thrift/protobuf文本 xml、jason
通信协议TCPHTTP
接口契约IDLthrift/protobufswagger
开发调试消息不可读可读,可调试
对外开放一般作为内部各个系统的通信框架对接外部系统

使用SpringCloud整合Dubbo,正所谓是强强联合。

负载均衡方面:

Feign默认使用Ribbon作为负载均衡的组件。

Dubbo和Ribbon(Feign默认集成Ribbon)都支持负载均衡策略,但是Dubbo支持的更灵活。

Dubbo和Ribbon对比:

Ribbon的负载均衡策略:随机、规则轮询、空闲策略、响应时间策略。

Dubbo的负载均衡策略:Dubbo支持4种算法,随机、权重轮询、最少活跃调用数、一致性Hash策略。而且算法里面引入权重的概念。

Dubbo可以使用路由策略,然后再进行负载均衡。

Dubbo配置的形式不仅支持代码配置,还支持Dubbo控制台灵活动态配置。

Dubbo负载均衡的算法可以精准到某个服务接口的某个方法,而Ribbon的算法是Client级别的。Ribbon需要进行全局配置,个性化配置比较麻烦。

容错机制方面:

Feign默认使用Hystix作为服务熔断的组件。Hystix提供了服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard)等功能。Feign是利用熔断机制来实现容错的,与Dubbo处理的方式不一样。

Dubbo支持多种容错策略,FailOver、FailFast、Failsafe、FailBack、Aviailable、Broadcast、Forking策略等,以及Mock。也引入了retry次数,timeout等配置参数。Dubbo自带了失败重试的功能。

其他方面(以下方便并未进行详细整理仅做参考):

Dubbo附带了白名单功能、结果缓存、同步和异步调用的功能。

Dubbo支持更多更灵活的并发控制:

客户端配置actives参数,配置单个Cunsumer最大并发请求数,超出则线程阻塞等待,超时报错。

Provider可以配置executes参数来限制最大的并发线程数,超出报错。

Provider可以配置accepts参数来限制最大长连接数来限制最大的连接数。

Provider的通过配置任务线程池的类型和最大线程数来控制并发量,超负载直接丢弃。

路由、流量调度、ABtest方面:

Ribbon需自己实现,应用不灵活。

Ribbon主要通过扩展AbstractLoadBalancerRule负载均衡的方法来实现,在负载均衡的部分还要进行改造升级。

Dubbo更加灵活方便。

Dubbo通过界面化、校本化配置路由规则,可以实现灰度发布、动态流量调度、容量计算等,方案成熟。

另外,Dubbo 还支持多版本调用。

Dubbo支持更完善的监控和管理界面,SC也有Actuator等工具进行监控,但是并不是针对远程调用这一块的

Dubbo支持客户端设置调用结果缓存,支持配置3种策略的结果缓存(LRU、LFU、FIO),但是要自己实现超时管理。

总结

Dubbo支持更多功能、更灵活、支持高并发的RPC框架。

SpringCloud全家桶里面(Feign、Ribbon、Hystrix),特点是非常方便。Ribbon、Hystrix、Feign在服务治理中,配合Spring Cloud做微服务,使用上有很多优势,社区也比较活跃,看将来更新发展。

业务发展影响着架构的选型,当服务数量不是很大时,使用普通的分布式RPC架构即可,当服务数量增长到一定数据,需要进行服务治理时,就需要考虑使用流式计算架构。Dubbo可以方便的做更精细化的流量调度,服务结构治理的方案成熟,适合生产上使用,虽然Dubbo是尘封后重新开启,但这并不影响其技术价值。

如果项目对性能要求不是很严格,可以选择使用Feign,它使用起来更方便。

如果需要提高性能,避开基于Http方式的性能瓶颈,可以使用Dubbo。

Dubbo Spring Cloud的出现,使得Dubbo既能够完全整合到Spring Cloud的技术栈中,享受Spring Cloud生态中的技术支持和标准化输出,又能够弥补Spring Cloud中服务治理这方面的短板。

Feign的使用

Feign的调用方式

Feign通过接口的方法调用Rest服务(之前是Ribbon+RestTemplate),

该请求发送给Eureka服务器(http://MICROSERVICECLOUD-DEPT/dept/list),

通过Feign直接找到服务接口,由于在进行服务调用的时候融合了Ribbon技术,所以也支持负载均衡作用。

在 api 工程 定义一个接口API,

a) 添加@FeignClient 注解,指定微服务名称 MICROSERVICECLOUD-DEPT

b) 指定请求地址 @RequestMapping

下面是一个 部门接口的例子:

@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)public Dept get(@PathVariable("id") long id);@RequestMapping(value = "/dept/list", method = RequestMethod.GET)public List<Dept> list();@RequestMapping(value = "/dept/add", method = RequestMethod.POST)public boolean add(Dept dept);
}

在 客户端的工程 引入 DeptClientService 接口,调用对应的方法。DeptClientService 类似于 mybatis的@Mappper 注解。

下面是一个 部门模块Controller的伪代码:

@RestController
public class DeptController_Consumer {@Autowiredprivate DeptClientService service = null;@RequestMapping(value = "/consumer/dept/get/{id}")public Dept get(@PathVariable("id") Long id) {return this.service.get(id);}@RequestMapping(value = "/consumer/dept/list")public List<Dept> list() {return this.service.list();}@RequestMapping(value = "/consumer/dept/add")public Object add(Dept dept) {return this.service.add(dept);}
}

Feign中使用熔断器

/*** Feign中使用断路器* 这里主要是处理异常出错的情况(降级/熔断时服务不可用,fallback就会找到这里来)*/
@Component // 不要忘记添加,不要忘记添加
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {@Overridepublic DeptClientService create(Throwable throwable) {return new DeptClientService() {@Overridepublic Dept get(long id) {return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭").setDb_source("no this database in MySQL");}@Overridepublic List<Dept> list() {return null;}@Overridepublic boolean add(Dept dept) {return false;}};}
}

Feign的核心原理

Feign远程调用的基本流程

Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。Feign远程调用的基本流程,大致如下图所示。

还可以参考   Feign远程调用原理 - 简书

从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。

Feign 远程调用的重要组件

在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果。

为了清晰的介绍SpringCloud中Feign运行机制和原理,在这里,首先为大家梳理一下Feign中几个重要组件。

远程接口的本地JDK Proxy代理实例

远程接口的本地JDK Proxy代理实例,有以下特点:

(1)Proxy代理实例,实现了一个加 @FeignClient 注解的远程调用接口;

(2)Proxy代理实例,能在内部进行HTTP请求的封装,以及发送HTTP 请求;

(3)Proxy代理实例,能处理远程HTTP请求的响应,并且完成结果的解码,然后返回给调用者。

下面以一个简单的远程服务的调用接口 DemoClient 为例,具体介绍一下远程接口的本地JDK Proxy代理实例的创建过程。

DemoClient 接口,有两个非常简单的远程调用抽象方法:一个为hello() 抽象方法,用于完成远程URL “/api/demo/hello/v1”的HTTP请求;一个为 echo(…) 抽象方法,用于完成远程URL “/api/demo/echo/{word}/v1”的HTTP请求。具体如下图所示。

DemoClient 接口代码如下:


package com.crazymaker.springcloud.demo.contract.client;
//…省略import@FeignClient(value = "seckill-provider", path = "/api/demo/",fallback = DemoDefaultFallback.class)
public interface DemoClient {/*** 测试远程调用** @return hello*/@GetMapping("/hello/v1")Result<JSONObject> hello();/*** 非常简单的一个 回显 接口,主要用于远程调用** @return echo 回显消息*/@RequestMapping(value = "/echo/{word}/v1", method = RequestMethod.GET)Result<JSONObject> echo(@PathVariable(value = "word") String word);}

注意,上面的代码中,在DemoClient 接口上,加有@FeignClient 注解。也即是说,Feign在启动时,会为其创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器。

如何使用呢?可以通过@Resource注解,按照类型匹配(这里的类型为DemoClient接口类型),从Spring IOC容器找到这个代理实例,并且装配给需要的成员变量。

DemoClient的 本地JDK Proxy 代理实例的使用的代码如下:


package com.crazymaker.springcloud.user.info.controller;
//…省略import
@Api(value = "用户信息、基础学习DEMO", tags = {"用户信息、基础学习DEMO"})
@RestController
@RequestMapping("/api/user")
public class UserController {@Resource
DemoClient demoClient;  //装配 DemoClient 的本地代理实例@GetMapping("/say/hello/v1")@ApiOperation(value = "测试远程调用速度")public Result<JSONObject> hello() {Result<JSONObject> result = demoClient.hello();JSONObject data = new JSONObject();data.put("others", result);return Result.success(data).setMsg("操作成功");}
//…
}

DemoClient的本地JDK Proxy代理实例的创建过程,比较复杂,稍后作为重点介绍。先来看另外两个重要的逻辑组件。

调用处理器 InvocationHandler

大家知道,通过 JDK Proxy 生成动态代理类,核心步骤就是需要定制一个调用处理器,具体来说,就是实现JDK中位于java.lang.reflect 包中的 InvocationHandler 调用处理器接口,并且实现该接口的 invoke(…) 抽象方法。

为了创建Feign的远程接口的代理实现类,Feign提供了自己的一个默认的调用处理器,叫做 FeignInvocationHandler 类,该类处于 feign-core 核心jar包中。当然,调用处理器可以进行替换,如果Feign与Hystrix结合使用,则会替换成 HystrixInvocationHandler 调用处理器类,类处于 feign-hystrix 的jar包中。

默认的调用处理器 FeignInvocationHandler

默认的调用处理器 FeignInvocationHandler 是一个相对简单的类,有一个非常重要Map类型成员 dispatch 映射,保存着远程接口方法到MethodHandler方法处理器的映射。

以前面示例中DemoClient 接口为例,其代理实现类的调用处理器 FeignInvocationHandler 的dispatch 成员的内存结构图如图3所示。

 

为何在图中的Map类型成员 dispatch 映射对象中,有两个Key-Value键值对呢?

原因是:默认的调用处理器 FeignInvocationHandle,在处理远程方法调用的时候,会根据Java反射的方法实例,在dispatch 映射对象中,找到对应的MethodHandler 方法处理器,然后交给MethodHandler 完成实际的HTTP请求和结果的处理。前面示例中的 DemoClient 远程调用接口,有两个远程调用方法,所以,其代理实现类的调用处理器 FeignInvocationHandler 的dispatch 成员,有两个有两个Key-Value键值对。

FeignInvocationHandler的关键源码,节选如下:

package feign;
//...省略importpublic class ReflectiveFeign extends Feign {//...//内部类:默认的Feign调用处理器 FeignInvocationHandlerstatic class FeignInvocationHandler implements InvocationHandler {private final Target target;//方法实例对象和方法处理器的映射private final Map<Method, MethodHandler> dispatch;//构造函数    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {this.target = checkNotNull(target, "target");this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);}//默认Feign调用的处理@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//...//首先,根据方法实例,从方法实例对象和方法处理器的映射中,//取得 方法处理器,然后,调用 方法处理器 的 invoke(...) 方法return dispatch.get(method).invoke(args);}//...}

源码很简单,重点在于invoke(…)方法,虽然核心代码只有一行,但是其功能是复杂的:

(1)根据Java反射的方法实例,在dispatch 映射对象中,找到对应的MethodHandler 方法处理器;

(2)调用MethodHandler方法处理器的 invoke(...) 方法,完成实际的HTTP请求和结果的处理。

补充说明一下:MethodHandler 方法处理器,和JDK 动态代理机制中位于 java.lang.reflect 包的 InvocationHandler 调用处理器接口,没有任何的继承和实现关系。MethodHandler 仅仅是Feign自定义的,一个非常简单接口。

方法处理器 MethodHandler

Feign的方法处理器 MethodHandler 是一个独立的接口,定义在 InvocationHandlerFactory 接口中,仅仅拥有一个invoke(…)方法,源码如下:

//定义在InvocationHandlerFactory接口中
public interface InvocationHandlerFactory {//…//方法处理器接口,仅仅拥有一个invoke(…)方法interface MethodHandler {//完成远程URL请求Object invoke(Object[] argv) throws Throwable;}
//...
}

MethodHandler 的invoke(…)方法,主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果。Feign提供了默认的 SynchronousMethodHandler 实现类,提供了基本的远程URL的同步请求处理。有关 SynchronousMethodHandler类以及其与MethodHandler的关系,大致如图所示。
 

为了彻底了解方法处理器,来读一下 SynchronousMethodHandler 方法处理器的源码,大致如下:

package feign;
//…..省略import
final class SynchronousMethodHandler implements MethodHandler {//…// 执行Handler 的处理
public Object invoke(Object[] argv) throws Throwable {RequestTemplate requestTemplate = this.buildTemplateFromArgs.create(argv);Retryer retryer = this.retryer.clone();while(true) {try {return this.executeAndDecode(requestTemplate);} catch (RetryableException var5) {//…省略不相干代码}}
}//执行请求,然后解码结果
Object executeAndDecode(RequestTemplate template) throws Throwable {Request request = this.targetRequest(template);long start = System.nanoTime();Response response;try {response = this.client.execute(request, this.options);response.toBuilder().request(request).build();}
}
}

 SynchronousMethodHandler的invoke(…)方法,调用了自己的executeAndDecode(…) 请求执行和结果解码方法。该方法的工作步骤:

(1)首先通 RequestTemplate 请求模板实例,生成远程URL请求实例 request;

(2)然后用自己的 feign 客户端client成员,excecute(…) 执行请求,并且获取 response 响应;

(3)对response 响应进行结果解码。

Feign 客户端组件 feign.Client

客户端组件是Feign中一个非常重要的组件,负责端到端的执行URL请求。其核心的逻辑:发送request请求到服务器,并接收response响应后进行解码。

feign.Client 类,是代表客户端的顶层接口,只有一个抽象方法,源码如下:

package feign;/**客户端接口* Submits HTTP {@link Request requests}. 
Implementations are expected to be thread-safe.*/
public interface Client {//提交HTTP请求,并且接收response响应后进行解码Response execute(Request request, Options options) throws IOException;}

由于不同的feign.Client 实现类,内部完成HTTP请求的组件和技术不同,故,feign.Client 有多个不同的实现。这里举出几个例子:

(1)Client.Default类:默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理;

(2)ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类;

(3)OkHttpClient类:内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。

(4)LoadBalancerFeignClient 类:内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。

此外,还有一些特殊场景使用的feign.Client客户端实现类,也可以定制自己的feign.Client实现类。下面对上面几个常见的客户端实现类,进行简要介绍。

一:Client.Default类:

作为默认的Client 接口的实现类,在Client.Default内部使用JDK自带的HttpURLConnnection类实现URL网络请求。

 

在JKD1.8中,虽然在HttpURLConnnection 底层,使用了非常简单的HTTP连接池技术,但是,其HTTP连接的复用能力,实际是非常弱的,性能当然也很低。具体的原因,参见后面的“SpringCloud与长连接的深入剖析”专题内容。

二:ApacheHttpClient类

ApacheHttpClient 客户端类的内部,使用 Apache HttpClient开源组件完成URL请求的处理。

从代码开发的角度而言,Apache HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口。既提高了开发的效率,也方便提高代码的健壮性。

从性能的角度而言,Apache HttpClient带有连接池的功能,具备优秀的HTTP连接的复用能力。关于带有连接池Apache HttpClient的性能提升倍数,具体可以参见后面的对比试验。

ApacheHttpClient 类处于 feign-httpclient 的专门jar包中,如果使用,还需要通过Maven依赖或者其他的方式,倒入配套版本的专门jar包。

三:OkHttpClient类

OkHttpClient 客户端类的内部,使用OkHttp3 开源组件完成URL请求处理。OkHttp3 开源组件由Square公司开发,用于替代HttpUrlConnection和Apache HttpClient。由于OkHttp3较好的支持 SPDY协议(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。),从Android4.4开始,google已经开始将Android源码中的 HttpURLConnection 请求类使用OkHttp进行了替换。也就是说,对于Android 移动端APP开发来说,OkHttp3 组件,是基础的开发组件之一。

四:LoadBalancerFeignClient 类

LoadBalancerFeignClient 内部使用了 Ribben 客户端负载均衡技术完成URL请求处理。在原理上,简单的使用了delegate包装代理模式:Ribben负载均衡组件计算出合适的服务端server之后,由内部包装 delegate 代理客户端完成到服务端server的HTTP请求;所封装的 delegate 客户端代理实例的类型,可以是 Client.Default 默认客户端,也可以是 ApacheHttpClient 客户端类或OkHttpClient 高性能客户端类,还可以其他的定制的feign.Client 客户端实现类型。

LoadBalancerFeignClient 负载均衡客户端实现类,具体如下图所示。

 

Feign 远程调用的执行流程

由于Feign远程调用接口的JDK Proxy实例的InvokeHandler调用处理器有多种,导致Feign远程调用的执行流程,也稍微有所区别,但是远程调用执行流程的主要步骤,是一致的。这里主要介绍两类JDK Proxy实例的InvokeHandler调用处理器相关的远程调用执行流程:

(1)与 默认的调用处理器 FeignInvocationHandler 相关的远程调用执行流程;

(2)与 Hystrix调用处理器 HystrixInvocationHandler 相关的远程调用执行流程。

介绍过程中,还是以前面的DemoClient的JDK Proxy远程动态代理实例的执行过程为例,演示分析Feigh远程调用的执行流程。 

与 FeignInvocationHandler 相关的远程调用执行流程

FeignInvocationHandler是默认的调用处理器,如果不对Feign做特殊的配置,则Feign将使用此调用处理器。结合前面的DemoClient的JDK Proxy远程动态代理实例的hello()远程调用执行过程,在这里,详细的介绍一下与 FeignInvocationHandler 相关的远程调用执行流程,大致如下图所示。

整体的远程调用执行流程,大致分为4步,具体如下:

第1步:通过Spring IOC 容器实例,装配代理实例,然后进行远程调用。

前文讲到,Feign在启动时,会为加上了@FeignClient注解的所有远程接口(包括 DemoClient 接口),创建一个本地JDK Proxy代理实例,并注册到Spring IOC容器。在这里,暂且将这个Proxy代理实例,叫做 DemoClientProxy,稍后,会详细介绍这个Proxy代理实例的具体创建过程。

然后,在本实例的UserController 调用代码中,通过@Resource注解,按照类型或者名称进行匹配(这里的类型为DemoClient接口类型),从Spring IOC容器找到这个代理实例,并且装配给@Resource注解所在的成员变量,本实例的成员变量的名称为 demoClient。

在需要代进行hello()远程调用时,直接通过 demoClient 成员变量,调用JDK Proxy动态代理实例的hello()方法。

第2步:执行 InvokeHandler 调用处理器的invoke(…)方法

前面讲到,JDK Proxy动态代理实例的真正的方法调用过程,具体是通过 InvokeHandler 调用处理器完成的。故,这里的DemoClientProxy代理实例,会调用到默认的FeignInvocationHandler 调用处理器实例的invoke(…)方法。

通过前面 FeignInvocationHandler 调用处理器的详细介绍,大家已经知道,默认的调用处理器 FeignInvocationHandle,内部保持了一个远程调用方法实例和方法处理器的一个Key-Value键值对Map映射。FeignInvocationHandle 在其invoke(…)方法中,会根据Java反射的方法实例,在dispatch 映射对象中,找到对应的 MethodHandler 方法处理器,然后由后者完成实际的HTTP请求和结果的处理。

所以在第2步中,FeignInvocationHandle 会从自己的 dispatch映射中,找到hello()方法所对应的MethodHandler 方法处理器,然后调用其 invoke(…)方法。

第3步:执行 MethodHandler 方法处理器的invoke(…)方法

通过前面关于 MethodHandler 方法处理器的非常详细的组件介绍,大家都知道,feign默认的方法处理器为 SynchronousMethodHandler,其invoke(…)方法主要是通过内部成员feign客户端成员 client,完成远程 URL 请求执行和获取远程结果。

feign.Client 客户端有多种类型,不同的类型,完成URL请求处理的具体方式不同。

第4步:通过 feign.Client 客户端成员,完成远程 URL 请求执行和获取远程结果

如果MethodHandler方法处理器实例中的client客户端,是默认的 feign.Client.Default 实现类性,则使用JDK自带的HttpURLConnnection类,完成远程 URL 请求执行和获取远程结果。

如果MethodHandler方法处理器实例中的client客户端,是 ApacheHttpClient 客户端实现类性,则使用 Apache httpclient 开源组件,完成远程 URL 请求执行和获取远程结果。

通过以上四步,应该可以清晰的了解到了 SpringCloud中的 feign 远程调用执行流程和运行机制。

实际上,为了简明扼要的介绍清楚默认的调用流程,上面的流程,实际上省略了一个步骤:第3步,实际可以分为两小步。为啥呢? SynchronousMethodHandler 并不是直接完成远程URL的请求,而是通过负载均衡机制,定位到合适的远程server 服务器,然后再完成真正的远程URL请求。换句话说,SynchronousMethodHandler实例的client成员,其实际不是feign.Client.Default类型,而是 LoadBalancerFeignClient 客户端负载均衡类型。 因此,上面的第3步,如果进一步细分话,大致如下:(1)首先通过 SynchronousMethodHandler 内部的client实例,实质为负责客户端负载均衡 LoadBalancerFeignClient 实例,首先查找到远程的 server 服务端;(2) 然后再由LoadBalancerFeignClient 实例内部包装的feign.Client.Default 内部类实例,去请求server端服务器,完成URL请求处理。

最后,说明下,默认的与 FeignInvocationHandler 相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求,为啥呢? 大致原因有以下两点:

(1) 没有远程调用过程中的熔断监测和恢复机制;

(2) 也没有用到高性能的HTTP连接池技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/251682.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

feign漫谈

feign的简单使用。 文章目录 一. 什么是feign二. 准备工作三. 如何使用3.1 定义pom文件3.2 定义配置文件及启动类注解3.3 定义feign接口 四. 部署 一. 什么是feign 远程调用框架 二. 准备工作 需要nacos环境&#xff1a; 涉及到feign调用&#xff0c;就没法抛开注册中心&am…

Feign简介

Feign feign是声明式的web service客户端&#xff0c;它让微服务之间的调用变得更简单了&#xff0c;类似controller调用service。Spring Cloud集成了Ribbon和Eureka&#xff0c;可在使用Feign时提供负载均衡的http客户端。 Feign是什么? Feign是一个声明式WebService客户端…

feign的使用入门篇

Feign简介 Feign是一个声明式的Web服务客户端&#xff0c;使用Feign可使得Web服务客户端的写入更加方便。 它具有可插拔注释支持&#xff0c;包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、Spring Cloud增加了对Spring MVC注释的支持&#xff0c;并HttpMessag…

Feign的简介及使用

一、Feign简介 Feign是一个声明式的http客户端&#xff0c;官方地址:https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现http请求的发送&#xff0c;解决代码可读性差&#xff0c;编程体验不统一、参数复杂URL难以维护的问题。 二、使用Feign的步骤 1.引入依赖…

架构愿景: 构建良好软件的关键

在产品开发生命周期的各个阶段&#xff0c;牢记架构愿景&#xff0c;始终坚持每个决策都符合愿景原则&#xff0c;是避免架构腐化的唯一方式。原文: Architecture Vision — A critical ingredient in building well-maintained software 上一篇文章《软件架构: 一切皆有代价》…

Feign原理

是一个HTTP请求调用轻量级框架&#xff0c;可以以Java接口注解的方式调用HTTP请求&#xff0c;而不用像Java中通过封装HTTP请求报文的方式直接调用。 Feign解决了什么问题 在服务调用的场景中&#xff0c;我们经常调用基于HTTP协议的服务&#xff0c;而我们经常使用到的框架可…

Feign是什么?

Feign能干什么&#xff1f; Feign旨在是编写Java Http客户端变得更加容易。 之前使用RibbonRestTemplate时&#xff0c;利用RESTTemplate请求的封装处理&#xff0c;形成了一套模板化的调用方法。但是在实际开发中&#xff0c;有偶遇对于服务依赖的调用可能不止一处&#xff…

Feign 与 OpenFeign

1. 什么是Feign Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件&#xff08;例如 Eureka、Ribbon 以及 Hystrix 等&#xff09;一起整合进 Spring Cloud Netflix 模块中&#xff0c;整合后全称为…

Feign的工作原理

文章目录 Feign的简单介绍Feign的工作原理1.创建远程接口的本地代理实例2.封装Request对象并进行编码3.feign.Client发送请求并对获取结果进行解码 总结 Feign的简单介绍 Feign组件主要用于微服务项目中&#xff0c;用来简化服务之间的远程调用&#xff0c;相信大家对他的使用…

OpenFeign和feign使用简介

1.OpenFeign简介 Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解&#xff0c;比如&#xff1a;FeignClient注解。Feign有可插拔的注解&#xff0c;包括Feign注解和JAX-RS注解。 Feign也支持编码…

Feign详解与实例

基本介绍 Feign是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样&#xff0c;从避免了调用目标微服务时&#xff0c;需要不断的解析/封装json 数据的繁琐。Feign集成了Ribbon。Ribboneureka是面向微服务编程&#xff0c;而Feign是面向接口编程。 Fegin是一个…

FeignClient

FeignClient的调用 注意&#xff1a; 依赖可能会出现冲突&#xff0c;请根据spring-cloud版本选择并修改 1. 引入依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artif…

Feign的介绍

Feign是springcloud里面的一个功能组件&#xff0c;那么它是实现一个什么功能呢&#xff1f; 首先我们可以先从字面意思上去理解一下它&#xff0c;Feign&#xff0c;英文翻译过来就是伪装的意思&#xff0c;实际上它的功能也是和伪装相关的&#xff0c;在我们之前在客户端配置…

@FeignClient使用详解

FeignClient标签的常用属性如下&#xff1a; name&#xff1a;指定FeignClient的名称&#xff0c;如果项目使用了Ribbon&#xff0c;name属性会作为微服务的名称&#xff0c;用于服务发现url: url一般用于调试&#xff0c;可以手动指定FeignClient调用的地址decode404:当发生h…

Feign详解

一. Feign概述 Feign是Spring Cloud提供的声明式、模板化的HTTP客户端&#xff0c; 它使得调用远程服务就像调用本地服务一样简单&#xff0c;只需要创建一个接口并添加一个注解即可。 Spring Cloud集成Feign并对其进行了增强&#xff0c;使Feign支持了Spring MVC注解&#x…

Feign(简介和使用)

1. Feign介绍 通过RestTemplate调用其它服务的API时&#xff0c;所需要的参数须在请求的URL中进行拼接&#xff0c;如果参数少的话或许我们还可以忍受&#xff0c;一旦有多个参数的话&#xff0c;这时拼接请求字符串就会效率低下 Feign是一个声明式的Web Service客户端&#…

什么是Feign?

服务间调用介绍 现有的服务调用方式 利用拼接的方式。 虽然上面有的用不错就很好了 Feign解决了什么问题 Feign的调用方式 Feign体系架构解析-武装到牙齿 上一节我们了解了feign的主要功能&#xff0c;它就像一个自拍杆一样&#xff0c;方便了Eureka的远程调用。可是怎么看…

简单理解Feign的原理与使用

文章目录 SpringCloud 总架构图一、简介1.1、负载均衡的概念2.2、Feign概念 二、入门案例2.1、导入依赖2.2、Feign的客户端2.3、调用Feign2.4、开启Feign功能2.5、启动测试2.6、Feign实现原理简单分析 三、负载均衡(Ribbon)四、熔断器支持五、请求压缩和响应压缩六、配置日志级…

Feign基本使用(超详细)

目录 一、Feign概述 二、Feign入门 1.创建服务提供者(provider) 2.创建feign接口 3、创建服务消费者(consumer) 三、Feign 原理 四、Feign优化 1、开启feign日志 2、feign超时问题 3、http连接池 4、gzip压缩 前言 当我们通过RestTemplate调用其它服务的API时&#xff0c;所…

中外黑客两则

外国一则&#xff1a;丹尼斯麦卡利斯泰尔里奇。挑选他的原因&#xff1a;帅。 &#xff08;英语&#xff1a;Dennis MacAlistair Ritchie&#xff1b;1941年9月9日&#xff0d;2011年10月12日[3][4]&#xff09;&#xff0c;著名的美国计算机科学家&#xff0c;对C语言和其他编…