先看示例代码

@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. 初识python: for循环之“两数之和”

    需求:给定一个数字列表和一个目标值,找出列表中和为目标值的两个数: #!/user/bin env python # author:Simple-Sir # time:20180913 # 给定一个整 ...

  2. concat模糊查询

    <if test="name!=null"> name like concat('%',concat(#{name},'%')) </if> choose ...

  3. 面渣逆袭:Java并发六十问,快来看看你会多少道!

    大家好,我是老三,面渣逆袭 继续,这节我们来盘一盘另一个面试必问知识点--Java并发. 这篇文章有点长,四万字,图文详解六十道Java并发面试题.人已经肝麻了,大家可以点赞.收藏慢慢看!扶我起来,我 ...

  4. C#检测外部exe程序弹窗错误,并重启

    private void button2_Click(object sender, EventArgs e) { string mainTitle = System.Configuration.Con ...

  5. RichTextBox单独设置文字颜色

    richTextBox1.Select(index, "str1".Length); richTextBox1.SelectionColor = Color.Red;

  6. 第02讲:Flink 入门程序 WordCount 和 SQL 实现

    我们右键运行时相当于在本地启动了一个单机版本.生产中都是集群环境,并且是高可用的,生产上提交任务需要用到flink run 命令,指定必要的参数. 本课时我们主要介绍 Flink 的入门程序以及 SQ ...

  7. collate utf8_bin是什么意思

    创建数据库时 collate utf_bin是 以二进制值比较,也就是区分大小写,collate是核对的意思 uft-8_general_ci  一般比较,不区分大小写

  8. 微服务探索之路03篇-docker私有仓库Harbor搭建+Kubernetes(k8s)部署私有仓库的镜像

    ❝ 目录: 微服务探索之路01篇.net6.0项目本地win10系统docker到服务器liunx系统docker的贯通 微服务探索之路02篇liunx ubuntu服务器部署k8s(kubernet ...

  9. python列表增加,修改,插入

  10. python 模块Module

    一.模块 1.定义: 模块是一个python文件,以.py结尾,包含了python对象定义和python语句. 2.作用: 模块内可以定义函数.类和变量: 模块可以提高代码的可维护性和重复使用: 让代 ...