背景前段时间同事遇到一个问题,需要在SpringCloudFeign调用中使用自定义的URL;通常没有这样的要求;毕竟用的是SpringCloud,服务之间的调用都是通过注册中心,不需要自定义url。但是也有比较特殊的,比如我们这里遇到的ToB场景,需要调用各个商家自定义的URL。虽然也可以使用原生的Feign甚至自定义一个OKHTTPClient实现,但这些解决方案必须以不同的方式编写;我打算使用现有的SpringCloudOpenFeign来实现。毕竟原生的Feign其实是支持这个功能的,而SpringCloudOpenFeign只是在这个基础上做了一层封装。只需要在接口声明中加入一个URI参数,这样每次调用都可以传递不同的URI,达到动态URL的目的。这个想法很简单,但在实践中并没有那么多。伪代码如下:@FeignClient(name="dynamic")interfaceDynamicClient{@GetMapping("/")Stringget(URIuri);}dynamicClient.get(URI.create("https://github.com"));执行后会抛出负载均衡异常:java.lang.RuntimeException:com.netflix.client.ClientException:Loadbalancerdoesnothaveavailableserverforclient:github.com这个异常也可以理解,但是github找不到服务;没找到也是有道理的,毕竟不是内部注册的服务。但是根据Feign官方的介绍,只要在接口中声明URI参数,就可以进行自定义。同时,我用原生的Feign测试过,没有问题。Debug的问题只能出在SpringCloudOpenFeign的封装上;经过同事的查找,在网上找到了一个解决这个问题的博客。https://www.cnblogs.com/syui-terra/p/14386188.html。按照文章的说法,确实只需要添加url参数,同时有值,但原因不明。本着打破砂锅问到底的精神,我个人很想知道OpenFeign是怎么处理的,只要url有值,完全是黑框,对于这种情况官方也没有特别说明笔记。所以我打算从源码中寻找答案。既然url有值就可以正常运行,那么肯定是在运行过程中获取到了这个值。但是我查看了源码中使用url的地方,并没有发现单元测试之外的任何应用,也就是说源码中并没有直接调用url()函数来获取值。但是会一直使用org.springframework.cloud.openfeign.FeignClient注解,所以查询了一下这个注解的用法。终于在这里找到了使用痕迹。这里查看源码的时候也有一些提示。比如我们直接查询,IDEA默认的查询范围是整个项目和所有依赖的库,会有很多干扰信息。比如我这里只需要看项目源码,不需要看单测;所以我在查询的时候可以过滤一下,这样干扰信息就会少很多。左边工具栏里有很多筛选条件,大家可以自己研究下。然后阅读源码,会发现@FeignClient中的所有数据都写入了一个Map中使用。最后你会发现这个url被写入了FeignClientFactoryBean中的url成员变量。查一下这个url用在什么地方就知道背后的原理了。这里打个断点,你会发现:当url为空时,会返回一个LoadBalanceclient,即从注册中心获取url的client,当url有a时,会获取一个defaultclient值,这样它就不会去负载均衡。所以如果我们要在OpenFeign中使用动态url,就得让@Feign的url有值,不管它是什么。既然看到了Feign的实现中的这一步,我也很好奇只要有URI参数,Feign是如何使用指定的URL的呢?这里还有一个阅读源码的小技巧。如果按照程序一步步执行的思路先调试会很费时间,毕竟这种成熟库的代码量不小。这里我们从官方文档可以知道,只要在接口参数中使用了java.net.URI,就会使用一个自定义的url,所以我们只要找出java.net.URI在哪里就可以知道key了在源代码源代码中使用。毕竟使用java.net.URI的场景不多。所以你只需要在这个依赖的地方用cmd+shift+f全局搜索java.net.URI就可以找到结果了。果然不多,只用了两个地方。结合使用场景猜测大概率是判断参数中是否有url.class等条件,或者是url对象;总之,我们先用url之类的关键字在这两个文件中搜索,记得要勾选匹配的大小写;最后会发现确实判断了参数中是否有url类,同时记录了索引位置。想必后面会通过这个索引位置读取到最终的url信息。最后通过这个索引查询核心源码。如果有值,则将此URI中指定的地址作为目标。至此,已经分析了这个问题背后的基本原理。总结其实这篇文章的重点是分析一些调试和阅读源码的一些技巧。尤其是在阅读Spring相关代码的时候,千万不要debug去追细节,因为调用链通常很长。晕了,只需要知道核心和关键源码是怎么处理的就可以了。最后,对OpenFeign处理动态url的方案有一些疑惑。是一个典型的约定大于配置的场景,但是问题是我们并不知道约定是@Feign的url一定要有值。所以我也给OpenFeign提交了一个PR,感兴趣的朋友也可以看看:https://github.com/spring-cloud/spring-cloud-openfeign/pull/713。
