前言

书接上文,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. google protocol buffer——protobuf的问题及改进一

    这一系列文章主要是对protocol buffer这种编码格式的使用方式.特点.使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务. 在上一篇文章中,我们完整了 ...

  2. 用于测试SqlAnalyzer1.00的十七个测试用例

    SqlAnalyzer功能:解析SQL语句,具体到字段,表和条件,并将其整理后输出格式化文本. 1. 原文=select a from b 整理后文本= select a from b 2. 原文=s ...

  3. centOS7 设置mysql数据库外网可以访问

    1.查看目前防火墙是否对外开发了3306端口 firewall-cmd --list-all 2.查看3306端口是否开放 firewall-cmd --query-port=3306/tcp no ...

  4. end的用法——print中加end=可以不换行展示

    A=['hello','world',1,2,3]for i in A: print('正常输出i的值:',i) #打印出来的是换行展示hello world 1 2 3 print('加入sep后i ...

  5. jsop之---实现过程

    JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用script标签的src属性(浏览器允许script标签跨域) 跨域访问,非同源访问 <!DOC ...

  6. 内存管理初始化源码3:bootmem

    start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node: 此时 ...

  7. C#开发PACS医学影像处理系统(十三):绘图处理之病灶测量

    接上一篇文章,当我们可以绘制图形标记后,就可以在此操作类上面进行扩展, 比如测量类工具,目前整理出的常用绘图和测量功能如下: 测量工具类:(图形标记类请参考本系列文章:绘图处理之图形标记) 功能 说明 ...

  8. H5C3综合案例

    案例:实现步骤 1. 搭建HTML结构 <section> <div></div> <div></div> <div></ ...

  9. HTML+CSS使用swiper快速生成最简单、最快捷、最易看懂的轮播图

    1.  在网页顶部输入swiper.com.con,进入swiper官网 2.   点击" API文档",获取轮播图代码的地方 3.   点击左侧"swiper初始化&q ...

  10. 【转】Locust性能-零基础入门系列(2) -重写wait_time

    在虚拟模拟的时候,可能对等待时间有更高的要求,比如假如有这么一个场景要求:某任务要求每被执行1次,那么下次的等待时间就1秒钟.这种情况,是可以实现的,这也就体现了Locust的灵活性.可编程性,很多比 ...