先看示例代码

@RestController
@RequestMapping("/students")
public class StudentController { @Autowired
private TeacherFeignClient teacherFeignClient; @GetMapping("/{id}")
public ResponseEntity<Teacher> getTeacher(@PathVariable("id") int id) {
return ResponseEntity.ok(teacherFeignClient.findTeacher(5));
}
}
@FeignClient(name = "teacher-service",path = "/teachers")
public interface TeacherFeignClient { @GetMapping("/{id}")
Teacher findTeacher(@PathVariable("id") int id); }

1.  首先我们肯定知道spring会基于TeacherFeignClient生成代理类Proxy,代理类的代码如下

    public final Teacher findTeacher(int var1) throws  {
try {
return (Teacher)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

代理类中的invoke方法指向的是feign.ReflectiveFeign.invoke方法,之后会走向调用SynchronousMethodHandler invoke方法,其中SynchronousMethodHandler中的target属性就是包装了feignclient的相关属性,比如service,url等,然后会调用executeAndDecode方法,该方法第一步就会根据target构造Request对象,这个方法最终会调用Cilent接口的实现类LoadBalancerFeignClient。

@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
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);
}
}

2.  LoadBalancerFeignClient execute方法先构造RibbonRequest,然后会去获取IClientConfig,再去调用SpringClientFactory的getInstance方法,走到NamedContextFactory getInstance方法。

    @Override
public <C> C getInstance(String name, Class<C> type) {
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
IClientConfig config = getInstance(name, IClientConfig.class);
return instantiateWithConfig(getContext(name), type, config);
}

3. 最终会通过AnnotationConfigApplicationContext getBean方法经过一些列流程走到 RibbonClientConfiguration 装载IClientConfig bean方法,最后就获得了当前service ribbon的相关配置

    @Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
return config;
}

4. 回到LoadBalancerFeignClient execute方法64行获取到IClientConfig后,此时有个核心步骤就是根据当前clientName获取到一个FeignLoadBalancer的实现,可以看到其中有一个cache属性,如果cache有的话就从cache返回,这也就feign第一次调用会慢的原因之一,因为首次需要去加载这个FeignLoadBalancer;首次加载的时候因为这里我们没有配置retryFactory,所以会返回一个 FeignLoadBalancer

    public FeignLoadBalancer create(String clientName) {
FeignLoadBalancer client = this.cache.get(clientName);
if(client != null) {
return client;
}
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}

5. 获取到FeignLoadBalancer会执行它父类AbstractLoadBalancerAwareClient中的executeWithLoadBalancer(该方法中牵扯到负载均衡的逻辑,会在下一篇中说到),该方法最终还是会执行FeignLoadBalancer的execute方法,方法中会获取request的client,该client默认是feign.Client.Default,实现就是通过构造HttpURLConnection发起http请求

    @Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
throws IOException {
Request.Options options;
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(
override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}

从服务间的一次调用分析整个springcloud的调用过程(二)的更多相关文章

  1. 从服务间的一次调用分析整个springcloud的调用过程(一)

    首先我们知道springcloud是一个微服务框架,按照官方文档的说法,springcloud提供了一些开箱即用的功能: 1 分布式/版本化配置 2 服务的注册与发现 3 路由 4 服务到服务之间调用 ...

  2. Android服务之PackageManagerService启动源码分析

    了解了Android系统的启动过程的读者应该知道,Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中.SystemServer进程在启 ...

  3. spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法

    spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...

  4. Asp.Net Core使用SignalR进行服务间调用

    网上查询过很多关于ASP.NET core使用SignalR的简单例子,但是大部分都是简易聊天功能,今天心血来潮就搞了个使用SignalR进行服务间调用的简单DEMO. 至于SignalR是什么我就不 ...

  5. SpringCloud服务间调用

    SpringCloud服务间的调用有两种方式:RestTemplate和FeignClient.不管是什么方式,他都是通过REST接口调用服务的http接口,参数和结果默认都是通过jackson序列化 ...

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

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

  7. spring boot项目使用swagger-codegen生成服务间调用的jar包

    swagger-codegen的github:https://github.com/swagger-api/swagger-codegen 需要的环境:jdk > 1.7   maven > ...

  8. SpringCloud实现服务间调用(RestTemplate方式)

    上一篇文章<SpringCloud搭建注册中心与服务注册>介绍了注册中心的搭建和服务的注册,本文将介绍下服务消费者调用服务提供者的过程. 本文目录 一.服务调用流程二.服务提供者三.服务消 ...

  9. SpringCloud 服务间互相调用 @FeignClient注解

    SpringCloud搭建各种微服务之后,服务间通常存在相互调用的需求,SpringCloud提供了@FeignClient 注解非常优雅的解决了这个问题 首先,保证几个服务都在一个Eureka中注册 ...

随机推荐

  1. go 使用 sort 对切片进行排序

    golang对slice的排序 golang里面需要使用sort包,并且实现几个接口Len, Swap, Less sort 包排序demo 假如现在有个slice 叫做 ids 里面保存的数据类型是 ...

  2. Linux驱动实践:中断处理函数如何【发送信号】给应用层?

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  3. kafka学习笔记(六)kafka的controller模块

    概述 今天我们主要看一下kafka的controller的代码,controller代码是kafka的非常重要的代码,需要我们深入学习.从某种意义上来说,它是kafka最核心的组件,一方面,他要为集群 ...

  4. R语言服务器程序 Rserve详解

    R语言服务器程序 Rserve详解 R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大. R语言作为统计学一门语言,一直在小众领域闪耀着光芒.直到 ...

  5. Android官方文档翻译 九 2.2Adding Action Buttons

    Adding Action Buttons 增加动作按钮 This lesson teaches you to 这节课教给你 Specify the Actions in XML 在XML中指定动作 ...

  6. RHCSA 第六天

    一.  创建下列用户.组和组成员资格: 1.创建名为 sysmgrs 的组 2.创建用户 natasha 同时指定sysmgrs作为natasha的附加组 3.创建用户 harry 同时指定 sysm ...

  7. 《剑指offer》面试题65. 不用加减乘除做加法

    问题描述 写一个函数,求两个整数之和,要求在函数体内不得使用 "+"."-"."*"."/" 四则运算符号. 示例: 输 ...

  8. 【刷题-LeetCode】122 Best Time to Buy and Sell Stock II

    Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price of a ...

  9. 【刷题-LeetCode】150 Evaluate Reverse Polish Notation

    Evaluate Reverse Polish Notation Evaluate the value of an arithmetic expression in Reverse Polish No ...

  10. 【记录一个问题】thanos receiver在tsdb切换期间,导致remote write接口失败增加

    如图:配置了thanos receiver落盘的时间周期为10分钟,结果导致在切换tsdb期间,remote write接口的失败率增高. 目前看来,解决办法就是上游增加重试.