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

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")
public interface CustomerService {
	@GetMapping("//customer-service-$CLUSTER_ID/echo/{customer}")
	public String customerInfo(@PathVariable("customer") String customer);
}

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

  • 一是前面“//”,这个是由于feign template不允许URI有“http://"开头,所以我们用“//”标记为后面紧跟着服务名称,而不是普通的URI
  • 二是“$CLUSTER_ID”,这个是后面要替换成业务单元编号的
  1. 在RequestInterceptor中查找到特殊的变量标记,根据路由规则把变量转换成实际值:
    @Bean
    public RequestInterceptor cloudContextInterceptor() {
    	return new RequestInterceptor() {
    		@Override
			public void apply(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("");
				}
				
			}


			private CharSequence route(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

你可能感兴趣的:(java,spring-cloud,spring,spring,cloud,openfeign)