前言

书接上文,feign接口是如何注册到容器想必已然清楚,现在我们着重关心一个问题,feign调用服务的时候是如何抉择的?上一篇主要是从读源码的角度入手,后续将会逐步从软件构架方面进行剖析。

一、ReflectiveFeign.FeignInvocationHandler

从上文知道feign接口调用实质上是调用的对应的动态代理接口的InvocationHandler,跟踪源码发现默认的InvocationHandler实现就是FeignInvocationHandler。现在我们看一下这个FeignInvocationHandler.invoke(...)方法。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// dispath 是缓存的method 以及 method对应的MethodHandler
return dispatch.get(method).invoke(args);
}

从代码中可以看到他是直接从缓存中拿到对应的MethodHandler,然后调用的MethodHandler的invoke方法。我们看一下MethodHandler都有哪些实现:

可以看到就两个实现, DefaultMethodHandler处理的是feign接口中的Default修饰的方法。我们调用的远程接口用的是SynchronousMethodHandler实现。那么可以看到我们最终对feing接口的某个方法的调用实际上调用的是SynchronousMethodHandler.invoke(...)方法。跟踪代码发现,最终调用的是SynchronousMethodHandler持有的Client的实例的execute方法。那么我们看一下Client都有那些实现:

这里跟踪SynchronousMethodHandler的创建过程发现Client的创建是按照如下逻辑进行的(FeignClientFactoryBean.loadBalance):


protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
} throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

从上述代码可以看到,他从context中获取到client,然后通过client获取执行。想必feign肯定是自动装备了一个Client,我们看一下他的默认配置:

显然配置必定是从FeignAutoConfiguration 或者 FeignRibbonClientAutoConfiguration进行配置的,查看这两个类最终发现Client是通过FeignRibbonClientAutoConfiguration进行注入的(通过@Import引入的DefaultFeignLoadBalancedConfiguration进行注入):


@Configuration
class DefaultFeignLoadBalancedConfiguration { @Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
} }

所以我们调用feign接口的某一个方法,最终调用的LoadBalancerFeignClient.execute()方法。那么负载均衡相关逻辑应该是在此接入的。

二、LoadBalancerFeignClient 做了些什么

先看核心代码,注意注释部分:


public Response execute(Request request, Request.Options options) throws IOException {
try {
// URL 处理
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
// 获取调用服务配置
IClientConfig requestConfig = getClientConfig(options, clientName); // 创建负载均衡客户端,执行请求
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}

从上面的代码可以看到,lbClient(clientName) 创建了一个负载均衡的客户端,它实际上就是生成的如下所述的类:


public class FeignLoadBalancer extends
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse>

熟悉ribbon的朋友应该知道AbstractLoadBalancerAwareClient 就是Ribbon负载均衡调用的父类。具体的负载均衡实现策略,下一章在详细描述。至此我们可以得出结论:feign集成负载均衡是通过将FeignLoadBalancer作为调用feign接口的实际执行者,从而达到负载均衡的效果。可以看到这里与Ribbon高度的解耦,相当于我们获取了服务名、调用地址、调用参数后,最终交由一个执行器去调用。执行器并不关心参数从何而来,这里基于Ribbon提供的执行器实现只是更具传递的服务名找到了一个正确的实例去调用而已。

三 、 小结

至此我们可以看到初步职责划分: 代理对象、请求与响应解析、执行器三个职能部门。

1) 代理对象职责是: 将feign接口中方法的调用转接到对FeignInvocationHandler的invoke调用,在invoke函数中通过方法名称找到对应的SynchronousMethodHandler。

2) 执行器: 负载根据请求与详情解析出的调用信息(调用服务名、调用地址、调用参数)发起调用,他不关心参数是如何来的

一个系统的好坏、可扩展性高低很大程序上取决于系统中各职能部门划分是否清晰以及各个职能部分的权限是否越界。

微服务通信之feign集成负载均衡的更多相关文章

  1. 微服务通信之feign的注册、发现过程

    前言 feign 是目前微服务间通信的主流方式,是springCloud中一个非常重要的组件.他涉及到了负载均衡.限流等组件.真正意义上掌握了feign可以说就掌握了微服务. 一.feign的使用 f ...

  2. Java微服务(二):负载均衡、序列化、熔断

    本文接着上一篇写的<Java微服务(二):服务消费者与提供者搭建>,上一篇文章主要讲述了消费者与服务者的搭建与简单的实现.其中重点需要注意配置文件中的几个坑. 本章节介绍一些零散的内容:服 ...

  3. springCloud搭建微服务集群+Zuul服务器端负载均衡

    概述 最近研究了一下springCloud的微服务集群,主要用到了SpringCloud的服务发现和服务器端负载均衡,所有的项目都是用的springboot,可以和springCloud无缝对接. 技 ...

  4. Spring Cloud微服务开发笔记5——Ribbon负载均衡策略规则定制

    上一篇文章单独介绍了Ribbon框架的使用,及其如何实现客户端对服务访问的负载均衡,但只是单独从Ribbon框架实现,没有涉及spring cloud.本文着力介绍Ribbon的负载均衡机制,下一篇文 ...

  5. 微服务Kong(十)——负载均衡参考

    KONG为请求多个后端服务提供了多种负载均衡方案:一种是简单的基于DNS,另一种是更加动态的环形均衡器,他在不需要DNS服务器的情况下也允许服务注册. 一.基于DNS的负载均衡 当使用基于DNS的负载 ...

  6. 微服务深入浅出(4)-- 负载均衡Ribbon

    Spring Cloud中可以使用RestTemplate+Ribbon的解决方案来将负载均衡以代码的形式封装到客户端中. 通过查阅官方文档可以知道,只需要在程序的IoC容器中注入一个restTemp ...

  7. 微服务通信之feign的配置隔离

    前言 由上文我们知道针对某一个Feign接口,我们可以给他设置特定的配置类.那如果现在有一个服务,我们只想对A服务配置一个拦截器拦截请求而不影响其他服务,那应该怎么做呢? 一.feign接口配置 由前 ...

  8. SpringCloud初体验:三、Feign 服务间调用(FeignClient)、负载均衡(Ribbon)、容错/降级处理(Hystrix)

    FeignOpenFeign Feign是一种声明式.模板化的HTTP客户端. 看了解释过后,可以理解为他是一种 客户端 配置实现的策略,它实现 服务间调用(FeignClient).负载均衡(Rib ...

  9. SpringCloud学习系列之二 ----- 服务消费者(Feign)和负载均衡(Ribbon)使用详解

    前言 本篇主要介绍的是SpringCloud中的服务消费者(Feign)和负载均衡(Ribbon)功能的实现以及使用Feign结合Ribbon实现负载均衡. SpringCloud Feign Fei ...

随机推荐

  1. 阿里面试:dubbo的服务引用过程

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  2. 03 父子组件sync&update

    父组件传给子组件是基本数据类型. 父组件 <template> <el-container class="consele-container"> <e ...

  3. 持续部署入门:基于 Kubernetes 实现蓝绿发布

    前言 软件世界比以往任何时候都更快.为了保持竞争力,需要尽快推出新的软件版本,而不会中断活跃用户访问,影响用户体验.越来越多企业已将其应用迁移到 Kubernetes. 在 Kubernetes 中有 ...

  4. 一位北漂12年IT工程师的年终总结

    Hi,我叫李振良,来自河南周口农村的一个普通家庭,如今来北京已经12年了,我是那种没有大学背景.没有聪明头脑.没有人脉的奋斗青年,但我又是那种不甘于现状,一直想做最好的那个人! 2019年已悄然离去, ...

  5. Activiti7 学习总结

    什么是工作流? 就是通过计算机对业务流程进行自动化处理,实现多个参与者按照预定义的流程去自动执行业务流程 什么是Activiti? Activiti是一个工作流引擎,开源的架构,基于BPMN2.0标准 ...

  6. 关于非标准json格式转变为json对象

    eval('(' + tempData + ')') 只需要这一句

  7. java+swing+mysql图书管理系统

    系统说明:本系统采用eclipse开发,IDEA,eclipse,myeclipse均可运行 界面采用swing实现 数据库:mysql,附sql代码,其余数据库可复制sql代码运行 数据库连接文件m ...

  8. Aggressive cows(POJ 2456)

    原题如下: Aggressive cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20524   Accepted: ...

  9. NGINX 负载均衡的理解

    前言 NGINX是轻量级,也是当前比较流行的web服务器软件.体积小但是功能强大. 这里我按照自己的理解,记录下对NGINX负载均衡的认识.(加权均衡,最小连接) 这里参考了 [https://blo ...

  10. MIPS 架构流水线处理器

    该项目系笔者大二时计算机组成课的课程设计,源代码及完整文档请移步 Github 仓库.