剖析SpringCloud Feign中所隐藏的坑

 更新时间:2022-08-09 05:46:46   作者:佚名   我要评论(0)

目录背景DebugFeign 的实现总结背景
前段时间同事碰到一个问题,需要在 SpringCloud 的 Feign 调用中使用自定义的 URL;通常情况下是没有这个

背景

前段时间同事碰到一个问题,需要在 SpringCloud 的 Feign 调用中使用自定义的 URL;通常情况下是没有这个需求的;毕竟都用了 SpringCloud 的了,那服务之间的调用都是走注册中心的,不会需要自定义 URL 的情况。

但也有特殊的,比如我们这里碰到 ToB 场景,需要对每个商户自定义的 URL 进行调用。

虽说也可以使用原生的 Feign 甚至是自定义一个 OKHTTP Client 实现,但这些方案都得换一种写法;

打算利用现有的 SpringCloud OpenFeign 来实现,毕竟原生的 Feign 其实是支持该功能的,而 SpringCloud OpenFeign 也只是在这基础上封装了一层。

只需要在接口声明处加上一个 URI 参数即可,这样就可以在每次调用时传递不同的 URI 来实现动态 URL 的目的。

想法很简单,但实践起来却不是那么回事了。 伪代码如下:

	@FeignClient(name = "dynamic")
	interface DynamicClient {
		@GetMapping("/")
		String get(URI uri);
	}
	dynamicClient.get(URI.create("https://github.com"));	

执行后会抛出负载均衡的异常:

java.lang.RuntimeException: com.netflix.client.ClientException:
Load balancer does not have available server for client: github.com

这个异常也能理解,就是找不到 github 这个服务;找不到也是合理的,毕竟也不是一个内部注册的服务。

但按照 Feign 的官方介绍,只要接口中声明了 URI 这个参数就能自定义,同时我自己也用原生的 Feign 测试过确实没什么问题。

Debug

那问题只能出在 SpringCloud OpenFeign 的封装上了;经过同事的搜索在网上找到一篇博客解决了这个问题。

按照文中的说法,确实只需要加上 URL 参数同时有值就可以了,但原因不明。

本着打破砂锅问到底的精神,我个人也想知道 OpenFeign 是如何处理的,只要 url 有值就可以,这完全是个黑盒,而且在官方的注释中并没有对这种情况有特殊说明。

所以我准备从源码中找到答案。

既然是 url 有值就能正常运行,那一定是在运行过程中获取了这个值;

但我在源码中查看 url 所使用的地方,并没有在单测之外找到哪里有所应用,说明源码中并没有直接调用 url() 这个函数来获取值。

org.springframework.cloud.openfeign.FeignClient 这个注解总会使用吧,于是我又查询这个注解的使用情况。

最终在这里查到了使用的痕迹。

这里查阅源码时也有一些小技巧,比如如果我们直接查询时,IDEA 默认的查询范围是整个项目和所有依赖库,会有许多干扰信息。

比如我这里就需要只看项目源码,单测这些都不用看;所以在查询的时候可以过滤一下,这样干扰信息就会少很多。

左边的工具栏还有许多过滤条件,大家可以自行研究一下。

接着从源码中进行阅读,会发现是将 @FeignClient 中的所有数据都写到一个 Map 里进行使用的。

最终会发现这个 url 被写入到了 FeignClientFactoryBean 中的 url 成员变量中了。

查看哪里在使用这个 url 就知道背后的原理了。

在这里打个断点会发现:当 url 为空时会返回一个 LoadBalanceclient,也就是会从注册中心获取 url 的客户端,而 url 有值时则会获取一个默认的客户端,这样就不会走负载均衡了。

所以我们如果想在 OpenFeign 中使用动态 url 时就得让 @Feign 的 url 有值才行,无论是什么都可以。

Feign 的实现

既然已经看到这一步了,我也比较好奇 Feign 是如何做到只要有 URI 参数就使用指定的 URL 呢?

这里也分享一个读源码的小技巧,如果我们跟着程序执行的思路去一步步 debug 的话会非常消耗时间,毕竟这类成熟库的代码量也不小。

这里我们从官方文档中可以得知只要在接口参数中使用了 java.net.URI 便会走自定义的 url,所以我们反过来只要在源码中找到哪里在使用 java.net.URI 便能知道关键源码。

毕竟使用 java.net.URI 的场景也不会太多。

所以只需要在这个依赖的地方 cmd+shift+f 全局搜索 java.net.URI 就能查到结果,果然不多,只有两处使用。

再结合使用场景猜测大概率是判断参数中是否是有 URL.class 这样的条件,或者是 url 对象;总之我们先用 URL 这样关键字在这两个文件中搜索一下,记得勾选匹配大小写;最后会发现的确是判断了参数中是否有 URL 这个类,同时将这个索引位置记录了下来。

想必后续会通过这个索引位置读取最终的 url 信息。

最终通过这个索引的使用地方查询到了核心源码,如果有值时就取这个 URI 中所指定的地址作为 target

到此为止这个问题的背后原理都已经分析完毕了。

总结

其实本文重点是分析了一些 debug 和阅读源码的一些小技巧,特别是在读关于 Spring 相关的代码时一定不能 debug 跟踪到细节中,因为调用链通常是很长的,稍不留神就把自己都绕晕了,只需要知道核心、关键源码是如何处理的即可。

最后对于 OpenFeign 处理动态 url 的方案确实也有些疑惑,是一个典型的约定大于配置的场景,但问题就在于我们并不知道这个约定是 @Feign 的 url 得有值。

所以我也提了一个 PROpenFeign,感兴趣的朋友也可以查看一下:

github.com/spring-clou…

以上就是剖析SpringCloud Feign中所隐藏的坑的详细内容,更多关于SpringCloud Feign坑的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
  • SpringCloud中分析讲解Feign组件添加请求头有哪些坑梳理
  • springcloud feign传输List的坑及解决
  • SpringCloud:feign对象传参和普通传参及遇到的坑解决
  • springcloud使用feign调用服务时参数内容过大问题
  • springcloud?feign服务之间调用,date类型转换错误的问题
  • SpringCloud通过Feign传递List类型参数方式
  • SpringCloud中的Feign远程调用接口传参失败问题

相关文章

  • 剖析SpringCloud Feign中所隐藏的坑

    剖析SpringCloud Feign中所隐藏的坑

    目录背景DebugFeign 的实现总结背景 前段时间同事碰到一个问题,需要在 SpringCloud 的 Feign 调用中使用自定义的 URL;通常情况下是没有这个
    2022-08-09
  • python接口自动化使用requests库发送http请求

    python接口自动化使用requests库发送http请求

    目录前言一、requests库二、HTTP 请求方法三、发送GET请求四、发送POST请求五、获取响应数据六、高级操作6.1文件下载6.2文件上传6.3SSL证书验
    2022-08-09
  • React实现监听粘贴事件并获取粘贴板中的截图

    React实现监听粘贴事件并获取粘贴板中的截图

    目录监听粘贴事件并获取粘贴板中的截图TSX中给组件添加监听粘贴事件从粘贴板获取截图文件React监听事件事件监听绑定的事件函数相关扩展监听粘
    2022-08-09
  • Python使用Rich?type和TinyDB构建联系人通讯录

    Python使用Rich?type和TinyDB构建联系人通讯录

    目录引言工具准备通讯录特征如何创建联系人模型如何使用TinyDB创建数据库如何使用typer创建命令行如何使用Rich设计终端如何使用打字命令连接
    2022-08-09
  • 一文解答什么是MySQL的回表

    一文解答什么是MySQL的回表

    目录引言聚簇索引和非聚簇索引是什么?主键索引和非主键索引有什么区别?B-Tree 和 B+Tree 的简单理解如何避免回表?引言 简单来说,回表就是
    2022-08-09
  • resubmit渐进式防重复提交框架示例

    resubmit渐进式防重复提交框架示例

    目录resubmit创作目的特性maven 引入编码自定义spring 整合使用maven 引入@EnableResubmit 注解说明测试代码整合 spring-bootmaven 引入代码
    2022-08-09
  • 一文详解Golang中net/http包的实现原理

    一文详解Golang中net/http包的实现原理

    目录前言http包执行流程http包源码分析端口监听请求解析路由分配响应处理前言 Go语言自带的net/http包提供了HTTP客户端和服务端的实现,实现
    2022-08-09
  • SpringBoot整合SpringSession实现分布式登录详情

    SpringBoot整合SpringSession实现分布式登录详情

    目录Session 共享为什么服务器 A 登录后,请求发到服务器 B,不认识该用户?解决方案SpringBoot整合SpringSession实现分布式登录Session 共享
    2022-08-09
  • Redis之SDS数据结构的使用

    Redis之SDS数据结构的使用

    目录序言字符串char*字符串数组简单动态字符串SDS序言 Redis的几种基本数据结构有字符串(String)、哈希(Hash)、列表(List)、集合(Set
    2022-08-09
  • pnpm对npm及yarn降维打击详解

    pnpm对npm及yarn降维打击详解

    目录正文npm2yarnpnpm总结正文 大家最近是不是经常听到 pnpm,我也一样。今天研究了一下它的机制,确实厉害,对 yarn 和 npm 可以说是降维打
    2022-08-09

最新评论