先看示例代码

@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. Pytest_用例分组(6)

    用例分组 pytest进行分组测试的方法是使用装饰器 @pytest.mark.标记名称,被标记为相同名称的用例可以看做为同一个组. 分组用例的运行方式是在执行命令中追加 -m "标记名称& ...

  2. Kubernetes 中的 Pod 安全策略

    来源:伪架构师作者:崔秀龙很多人分不清 SecurityContext 和 PodSecurityPolicy 这两个关键字的差别,其实很简单:•SecurityContext 是 Pod 中的一个字 ...

  3. 开源数据可视化BI工具SuperSet(安装)

    本次安装教程共分两大步骤,因为Superset 基于python3编写的web应用(flask) 所以要求python3环境,故首先要将linux系统自带的环境进行升级,已经是python3的可跳过- ...

  4. Scratch 3 矢量编辑器——“临摹”一只哆啦A梦

    利用Scratch来制作一些小作品,常常需要到网上去找图片,而网上下载的图片一般都是位图,往往存在两个问题: 图片不够清晰,当图片放大后会出现"马赛克"现象: 图片中存在不必要的背 ...

  5. ctfshow web2 web3

    ctfshow web2 1.手动注入题.先用万能密码admin' or 1=1%23,有回显 2.union select注入,2处有回显 3.依次查找数据库.表.字段 得到flag ctfshow ...

  6. 【刷题-LeetCode】188 Best Time to Buy and Sell Stock IV

    Best Time to Buy and Sell Stock IV Say you have an array for which the i-th element is the price of ...

  7. prometheus基本概念(思维导图)

    参考文章: prometheus词汇表 prometheus的summary和histogram指标的简单理解

  8. gorm连接mysql和模型定义那些事

    1. gorm操作mysql: 1.1 安装gorm gorm官网: https://gorm.io/zh_CN/docs/connecting_to_the_database.html gorm的g ...

  9. java多态概述特点转型I

    1 package face_09; 2 3 import com.sun.jdi.Method; 4 5 /* 6 * 对象的多态性. 7 * class 动物 8 * {} 9 * 10 * cl ...

  10. VUE3 之 多个 v-model 绑定及 v-model 修饰符的使用 - 这个系列的教程通俗易懂,适合新手

    1. 概述 洛克定律告诉我们: 当我们的目标很远大,远到我们都看不到终点时,放弃几率就会很大,就像跑马拉松比赛,由于时间长.距离长,很多选手都会选择在中途放弃. 其实有个好办法,就是拆分,把大目标拆分 ...