Open feign动态切换服务目标地址组件
组件背景
核心设计思路(步骤化概览)
路由键注解 - @RoutingKey
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RoutingKey {
String value() default "";
}RoutingKeyInterceptor
@Slf4j
public class RoutingKeyInterceptor implements MethodInterceptor {
private AnnotationClassResolver resolver;
private Environment environment;
private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
public RoutingKeyInterceptor(AnnotationClassResolver resolver, Environment environment) {
this.resolver = resolver;
this.environment = environment;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String url = determineUrl(invocation);
try {
RequestUrlContext.set(url);
return invocation.proceed();
} finally {
RequestUrlContext.clear();
}
}
private String determineUrl(MethodInvocation invocation) {
String expr = resolver.getValue(invocation.getMethod(), invocation.getThis().getClass());
if (StrUtil.isBlank(expr)) {
if (log.isDebugEnabled()) {
log.debug("RoutingKeyInterceptor.determineUrl, no expr found, method={}", invocation.getMethod());
}
return StrUtil.EMPTY;
}
String routingKey = calculateExpr(expr, invocation.getMethod(), invocation.getArguments());
if (StrUtil.isBlank(routingKey)) {
if (log.isDebugEnabled()) {
log.debug("RoutingKeyInterceptor.determineUrl, expr value blank, expr={}, method={}, args={}", expr, invocation.getMethod(), Arrays.asList(invocation.getArguments()));
}
return StrUtil.EMPTY;
}
String url = environment.getProperty(routingKey, StrUtil.EMPTY);
if (log.isDebugEnabled()) {
log.debug("RoutingKeyInterceptor.determineUrl, end, url={}, routingKey={}", url, routingKey);
}
return url;
}
private String calculateExpr(String expr, Method method, Object[] params) {
String exprValue = "unknown";
if (StrUtil.isNotBlank(expr) && expr.contains("#")) {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
String[] parameterNames = discoverer.getParameterNames(method);
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
ctx.setVariable(parameterNames[i], params[i]);
}
}
Expression expression = parser.parseExpression(expr);
Object value = expression.getValue(ctx);
if (value != null) {
exprValue = value.toString();
}
}
return exprValue;
}
}DynamicClusterRequestInterceptor(Feign 请求拦截器)
RequestUrlContext(请求上下文)
ContextRequestRouter(上下文路由器)
启用配置 - @EnabledRoutingKeyAdvisor / DynamicClusterConfiguration
核心代码解析(按模块复列)
使用示例(步骤化)
启用功能(在 Spring Boot 主类上添加注解)
在 Feign 客户端方法上使用 @RoutingKey
在配置文件中定义路由键对应的 URL
总结
最后更新于