Spring-cloud-openfeign动态确定服务名service-name

2022-07-06 10:56:50

Spring-cloud-openfeign动态指定服务名service-name

背景

一个超级大系统,为了应对超大吞吐量,需要把业务服务划分成不同的业务单元,每个业务单元是一个服务集群,然后根据一定的规则,把请求路由到不同的业务单元。
如一个客户服务customer-service,我们部署成n个业务customer-service-0到customer-service-n单元,客户号从0到10M的请求,要路由到http://customer-service-0/api上去,客户号从10M+1到20M的请求,要路由到http://customer-service-1/api上去…

解决方法

我在网上搜了一圈,做得最好的,也只有这个:Feign的构建过程及自定义扩展功能 – 动态服务名
但是,这篇文章的方法只能启动时静态的修改服务名,不能动态的改,而且它还用了Reflect,修改了私有变量,不安全。
本人研究了一下Spring-cloud-openfeign,终于找到一个简单办法,不敢私藏,特分享给需要的朋友们。
这个方法其实就是自定义一个RequestInterceptor,虽然也不完美,它必须对服务url做一些特殊处理才行,但它确实能简单的解决我们的问题。

示例代码

  1. 在Service Client中API的URL中定义一个特殊的变量标记,如:
@FeignClient("customer-service")publicinterfaceCustomerService{@GetMapping("//customer-service-$CLUSTER_ID/echo/{customer}")publicStringcustomerInfo(@PathVariable("customer")String customer);}

上述示例中的customerInfo方法URI中有两个特殊的地方:

  • 一是前面“//”,这个是由于feign template不允许URI有“http://"开头,所以我们用“//”标记为后面紧跟着服务名称,而不是普通的URI
  • 二是“$CLUSTER_ID”,这个是后面要替换成业务单元编号的
  1. 在RequestInterceptor中查找到特殊的变量标记,根据路由规则把变量转换成实际值:
@BeanpublicRequestInterceptorcloudContextInterceptor(){returnnewRequestInterceptor(){@Overridepublicvoidapply(RequestTemplate template){String url= template.url();if(url.contains("$CLUSTER_ID")){
					url= url.replace("$CLUSTER_ID",route(template));
					template.uri(url);}if(url.startsWith("//")){
					url="http:"+ url;
					template.target(url);
					template.uri("");}}privateCharSequenceroute(RequestTemplate template){// TODO 你的路由算法在这里return"000";}};}

这样就能实现服务单元路由了。

后记:

由于Spring-cloud-openfeign的Targeter是package范围的接口,不允许自行实现,这个文章是一个不得已而变通的方法。后来我又直接和Spring-cloud-openfeign开发团队联系,让他们改了一些东西,最近发现他们已经按我的建议修改了,而且已经发布到了Spring-cloud-openfeign 3.0.0.M2版本里面了。所以就有了按照新的方法来优雅地实现单元化动态指定服务名,请参考:
https://blog.csdn.net/weixin_45357522/article/details/106745468

  • 作者:无级程序员
  • 原文链接:https://blog.csdn.net/weixin_45357522/article/details/104020061
    更新时间:2022-07-06 10:56:50