背景
上次写了一篇关于Spring-cloud-openfeign 2.x版本的单元化动态指定服务名的方法,那个方法是由于Spring-cloud-openfeign的Targeter是package范围的接口,不允许自行实现,不得已而变通的方法。后来我又直接和Spring-cloud-openfeign开发团队联系,让他们改了一些东西,最近发现他们已经按我的建议修改了,而且已经发布到了Spring-cloud-openfeign 3.0.0.M2版本里面了。所以今天就按照新的方法来优雅地实现单元化动态指定服务名。
废话不多说,直接上示例
示例代码
1. 在Service Client中API的URL中定义一个特殊的变量标记,如:
@FeignClient("customer-service-CLUSTER-ID")
public interface CustomerService {
@GetMapping("/echo/{customer}")
public String customerInfo(@PathVariable("customer") String customer);
}
上述示例中的,FeignClient的服务名称,以“CLUSTER-ID”结尾,用来标识本服务是单元化的,运行时,要把“CLUSTER-ID”替换为实际定位到的单元ID。(本来想用“%CLUSTER-ID%”特殊符号作为变量place-holder,但Spring-cloud-openfeign会在启动校验服务名,判定为非法域名,所以只能用合法的域名允许的字符。)
2. 以HardCodedTarget为基础,实现Targeter
public class RouteTargeter implements Targeter {
/**
* 服务名以本字符串结尾的,会被置换为实现定位到的单元号
*/
public static final String CLUSTER_ID_SUFFIX = "CLUSTER_ID";
@Override
public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context,
HardCodedTarget<T> target) {
return feign.target(new RouteTarget<>(target));
}
public static class RouteTarget<T> implements Target<T> {
Logger log = LoggerFactory.getLogger(getClass());
private Target<T> realTarget;
public RouteTarget(Target<T> realTarget) {
super();
this.realTarget = realTarget;
}
@Override
public Class<T> type() {
return realTarget.type();
}
@Override
public String name() {
return realTarget.name();
}
@Override
public String url() {
String url = realTarget.url();
if (url.endsWith(CLUSTER_ID_SUFFIX)) {
url = url.replace(CLUSTER_ID_SUFFIX, locateCusterId());
log.debug("url changed from {} to {}", realTarget.url(), url);
}
return url;
}
/**
* @return 定位到的实际单元号
*/
private String locateCusterId() {
// TODO 你的路由算法在这里
return "001";
}
@Override
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.target(url());
}
return input.request();
}
}
}
3. 使用自定义的Targeter实现代替缺省的实现
在XXX应用中,自己写一个XXXOpenfeignConfiguration,加入如下Bean
@Bean
public RouteTargeter getRouteTargeter() {
return new RouteTargeter();
}
这样,在运行时,我们的“RouteTargeter”就能根据服务名后缀判断到需要动态定位单元时,实现动态定位单元。
现在已经有了Spring-cloud 3.0.0M3版本了,有网友反应下载不到,大家可以在pom.xml中添加一个库就行了:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>