배경
얼마 전 동료에게 문제가 발생 SpringCloud
하여 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으로 테스트했는데 문제가 없습니다.
디버그
그 문제는 ; SpringCloud OpenFeign
의 . 동료 검색 후 문제를 해결한 블로그를 인터넷에서 찾았습니다.
기사에 따르면 실제로는 URL 매개변수를 추가하고 값만 있으면 되지만 그 이유는 알려지지 않았다.
本着打破砂锅问到底的精神,我个人也想知道 OpenFeign
是如何处理的,只要 url 有值就可以,这完全是个黑盒,而且在官方的注释中并没有对这种情况有特殊说明。
所以我准备从源码中找到答案。
既然是 url 有值就能正常运行,那一定是在运行过程中获取了这个值;
但我在源码中查看 url 所使用的地方,并没有在单测之外找到哪里有所应用,说明源码中并没有直接调用 url()
这个函数来获取值。
但 org.springframework.cloud.openfeign.FeignClient
这个注解总会使用吧,于是我又查询这个注解的使用情况。
最终在这里查到了使用的痕迹。
这里查阅源码时也有一些小技巧,比如如果我们直接查询时,IDEA 默认的查询范围是整个项目和所有依赖库,会有许多干扰信息。
比如我这里就需要只看项目源码,单测这些都不用看;所以在查询的时候可以过滤一下,这样干扰信息就会少很多。
左边的工具栏还有许多过滤条件,大家可以自行研究一下。
接着从源码中进行阅读,会发现是将 @FeignClient
中的所有数据都写到一个 Map
里进行使用的。 最终会发现这个 url 被写入到了 FeignClientFactoryBean
中的 url 成员变量中了。
查看哪里在使用这个 url 就知道背后的原理了。
在这里打个断点会发现:当 url 为空时会返回一个 LoadBalance
的 client
,也就是会从注册中心获取 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 得有值。
所以我也提了一个 PR
给 OpenFeign
,感兴趣的朋友也可以查看一下: