Skip to content

Commit

Permalink
增加标签路由匹配fallback规则逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
chengyouling committed Dec 18, 2023
1 parent 10bed08 commit f1e8a84
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ private List<Object> getTargetInvokersByRules(List<Object> invokers, Object invo
+ DubboReflectUtils.getMethodName(invocation) + ":" + getVersion(queryMap);
List<Rule> rules = RuleUtils
.getRules(configuration, targetService, interfaceName, DubboCache.INSTANCE.getAppName());
List<Route> routes = RouteUtils.getRoutes(rules, DubboReflectUtils.getArguments(invocation),
Optional<Rule> matchRuleOptional = RouteUtils.getRule(rules, DubboReflectUtils.getArguments(invocation),
parseAttachments(invocation));
if (!CollectionUtils.isEmpty(routes)) {
return RuleStrategyHandler.INSTANCE.getMatchInvokers(targetService, invokers, routes);
if (matchRuleOptional.isPresent()) {
return RuleStrategyHandler.INSTANCE.getMatchInvokers(targetService, invokers, matchRuleOptional.get());
}
return RuleStrategyHandler.INSTANCE
.getMismatchInvokers(targetService, invokers, RuleUtils.getTags(rules, true), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.huaweicloud.sermant.router.dubbo.strategy;

import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.config.strategy.RuleStrategy;
import com.huaweicloud.sermant.router.dubbo.strategy.rule.InvokerRuleStrategy;

Expand Down Expand Up @@ -46,11 +47,11 @@ public enum RuleStrategyHandler {
*
* @param serviceName 服务名
* @param invokers dubbo invokers
* @param routes 路由规则
* @param rule 路由规则
* @return 标签应用的invokers
*/
public List<Object> getMatchInvokers(String serviceName, List<Object> invokers, List<Route> routes) {
return ruleStrategy.getMatchInstances(serviceName, invokers, routes, true);
public List<Object> getMatchInvokers(String serviceName, List<Object> invokers, Rule rule) {
return ruleStrategy.getMatchInstances(serviceName, invokers, rule, true);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ private RouteUtils() {
* @param attachments dubbo的attachments参数
* @return 匹配的路由
*/
public static List<Route> getRoutes(List<Rule> list, Object[] arguments, Map<String, Object> attachments) {
public static Optional<Rule> getRule(List<Rule> list, Object[] arguments, Map<String, Object> attachments) {
for (Rule rule : list) {
Match match = rule.getMatch();
if (match == null) {
return rule.getRoute();
return Optional.of(rule);
}
List<Route> routeList;
if (!CollectionUtils.isEmpty(match.getAttachments()) && !CollectionUtils.isEmpty(attachments)) {
Expand All @@ -65,10 +65,10 @@ public static List<Route> getRoutes(List<Rule> list, Object[] arguments, Map<Str
routeList = Collections.emptyList();
}
if (!CollectionUtils.isEmpty(routeList)) {
return routeList;
return Optional.of(rule);
}
}
return Collections.emptyList();
return Optional.empty();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.huaweicloud.sermant.router.common.constants.RouterConstant;
import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.dubbo.AlibabaInvoker;
import com.huaweicloud.sermant.router.dubbo.ApacheInvoker;

Expand All @@ -39,10 +40,13 @@
public class RuleStrategyHandlerTest {
private final List<Route> routes;

private final Rule rule;

/**
* 构造方法
*/
public RuleStrategyHandlerTest() {
rule = new Rule();
routes = new ArrayList<>();
Map<String, String> tags1 = new HashMap<>();
tags1.put(RouterConstant.DUBBO_VERSION_KEY, "0.0.1");
Expand All @@ -56,6 +60,7 @@ public RuleStrategyHandlerTest() {
route2.setTags(tags2);
route2.setWeight(100);
routes.add(route2);
rule.setRoute(routes);
}

/**
Expand All @@ -68,7 +73,7 @@ public void testAlibabaV1() {
invokers.add(invoker1);
AlibabaInvoker<Object> invoker2 = new AlibabaInvoker<>("0.0.2");
invokers.add(invoker2);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, routes);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(100, routes.get(0).getWeight().intValue());
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker1, matchInvokers.get(0));
Expand Down Expand Up @@ -103,7 +108,7 @@ public void testAlibabaMismatch() {
routes.get(0).setWeight(0);

// 测试匹配上路由,没有随机到实例的情况
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, routes);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker2, matchInvokers.get(0));

Expand Down Expand Up @@ -147,7 +152,7 @@ public void testApacheV1() {
invokers.add(invoker1);
ApacheInvoker<Object> invoker2 = new ApacheInvoker<>("0.0.2");
invokers.add(invoker2);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, routes);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(100, routes.get(0).getWeight().intValue());
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker1, matchInvokers.get(0));
Expand Down Expand Up @@ -183,7 +188,7 @@ public void testApacheMismatch() {
routes.get(0).setWeight(0);

// 测试匹配上路由,没有随机到实例的情况
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, routes);
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker2, matchInvokers.get(0));

Expand Down Expand Up @@ -216,4 +221,112 @@ public void testApacheZone() {
Assert.assertEquals(2, mismatchInvoker.size());
Assert.assertEquals(invokers, mismatchInvoker);
}

/**
* 测试alibaba rule中route有选中tag,但是没有符合版本的实例,invoker命中fallback版本实例的情况
*/
@Test
public void testAlibabaV1Fallback() {
List<Route> fallback = new ArrayList<>();
Map<String, String> tags = new HashMap<>();
tags.put(RouterConstant.DUBBO_VERSION_KEY, "0.0.3");
Route route = new Route();
route.setTags(tags);
route.setWeight(100);
fallback.add(route);
rule.setFallback(fallback);

List<Object> invokers = new ArrayList<>();
AlibabaInvoker<Object> invoker1 = new AlibabaInvoker<>("0.0.3");
invokers.add(invoker1);
AlibabaInvoker<Object> invoker2 = new AlibabaInvoker<>("0.0.4");
invokers.add(invoker2);

// Route随机命中route1,但是没有0.0.1版本实例;命中fallback,返回fallback实例信息
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(100, routes.get(0).getWeight().intValue());
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker1, matchInvokers.get(0));
}

/**
* 测试alibaba rule中设置route、fallback,且权重均有命中tag,但是invoker均未命中版本实例的情况
*/
@Test
public void testAlibabaV1NotMathRouteFallback() {
List<Route> fallback = new ArrayList<>();
Map<String, String> tags = new HashMap<>();
tags.put(RouterConstant.DUBBO_VERSION_KEY, "0.0.3");
Route route = new Route();
route.setTags(tags);
route.setWeight(100);
fallback.add(route);
rule.setFallback(fallback);

List<Object> invokers = new ArrayList<>();
ApacheInvoker<Object> invoker1 = new ApacheInvoker<>("0.0.2");
invokers.add(invoker1);
ApacheInvoker<Object> invoker2 = new ApacheInvoker<>("0.0.4");
invokers.add(invoker2);

// Route随机命中route1,但是没有0.0.1版本实例,fallback也未命中tag实例,所以返回全部实例信息
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(2, matchInvokers.size());
}

/**
* 测试alibaba rule中设置routes但权重计算未命中,invoker命中fallback版本实例的情况
*/
@Test
public void testAlibabaV1MathFallback() {
List<Route> fallback = new ArrayList<>();
Map<String, String> tags = new HashMap<>();
tags.put(RouterConstant.DUBBO_VERSION_KEY, "0.0.3");
Route route = new Route();
route.setTags(tags);
route.setWeight(100);
fallback.add(route);
rule.setFallback(fallback);
routes.get(0).setWeight(0);
routes.get(1).setWeight(0);

List<Object> invokers = new ArrayList<>();
ApacheInvoker<Object> invoker1 = new ApacheInvoker<>("0.0.2");
invokers.add(invoker1);
ApacheInvoker<Object> invoker2 = new ApacheInvoker<>("0.0.3");
invokers.add(invoker2);

// Route计算权重均未命中tag,fallback权重计算命中tag,返回fallback规则命中实例信息
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker2, matchInvokers.get(0));
}

/**
* 测试alibaba rule中设置routes但权重计算未命中,同时fallback也未命中实例,invoker返回未设置规则版本号版本实例的情况
*/
@Test
public void testAlibabaV1BothNotMathFallbackRoute() {
List<Route> fallback = new ArrayList<>();
Map<String, String> tags = new HashMap<>();
tags.put(RouterConstant.DUBBO_VERSION_KEY, "0.0.3");
Route route = new Route();
route.setTags(tags);
route.setWeight(100);
fallback.add(route);
rule.setFallback(fallback);
routes.get(0).setWeight(0);
routes.get(1).setWeight(0);

List<Object> invokers = new ArrayList<>();
ApacheInvoker<Object> invoker1 = new ApacheInvoker<>("0.0.1");
invokers.add(invoker1);
ApacheInvoker<Object> invoker2 = new ApacheInvoker<>("0.0.4");
invokers.add(invoker2);

// Route计算权重均未命中tag,fallback权重计算也未命中tag,返回route中未设置tag实例信息
List<Object> matchInvokers = RuleStrategyHandler.INSTANCE.getMatchInvokers("foo", invokers, rule);
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker2, matchInvokers.get(0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public class Rule {
*/
private List<Route> route;

/**
* 降级路由
*/
private List<Route> fallback;

public void setPrecedence(int precedence) {
this.precedence = precedence;
}
Expand All @@ -63,4 +68,12 @@ public void setRoute(List<Route> route) {
public List<Route> getRoute() {
return this.route;
}

public List<Route> getFallback() {
return fallback;
}

public void setFallback(List<Route> fallback) {
this.fallback = fallback;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import com.huaweicloud.sermant.core.common.LoggerFactory;
import com.huaweicloud.sermant.core.utils.StringUtils;
import com.huaweicloud.sermant.router.common.utils.CollectionUtils;
import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.config.utils.RuleUtils;
import com.huaweicloud.sermant.router.config.utils.RuleUtils.RouteResult;

Expand Down Expand Up @@ -74,8 +74,30 @@ public AbstractRuleStrategy(String source, InstanceStrategy<I, Map<String, Strin
}

@Override
public List<I> getMatchInstances(String serviceName, List<I> instances, List<Route> routes, boolean isReplaceDash) {
RouteResult<?> result = RuleUtils.getTargetTags(routes, isReplaceDash);
public List<I> getMatchInstances(String serviceName, List<I> instances, Rule rule, boolean isReplaceDash) {
// match set routes
RouteResult<?> result = RuleUtils.getTargetTags(rule.getRoute(), isReplaceDash);
if (result.isMatch() && !CollectionUtils.isEmpty(rule.getFallback())) {
// fallback有设置路由规则时,仅返回匹配的实例,如果存在直接返回
List<I> routeInstances = getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName,
instances, false);
if (!CollectionUtils.isEmpty(routeInstances)) {
return routeInstances;
}
}

// route设置的目标标签未匹配实例时,fallback有设置路由,通过fallback路由目标标签实例
if (!CollectionUtils.isEmpty(rule.getFallback())) {
RouteResult<?> fallback = RuleUtils.getTargetTags(rule.getFallback(), isReplaceDash);
List<I> fallbackInstances = getInstances(getStrategy(fallback.isMatch()), fallback.getTags(), serviceName,
instances, false);

// fallback中设置了路由规则命中routeTag,且有对应的实例匹配则按fallback路由规则选中实例,返回对应实例
if (!CollectionUtils.isEmpty(fallbackInstances)) {
return fallbackInstances;
}
}

return getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.huaweicloud.sermant.router.config.strategy;

import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;

import java.util.List;
import java.util.Map;
Expand All @@ -34,11 +35,11 @@ public interface RuleStrategy<I> {
*
* @param serviceName 服务名
* @param instances 实例列表
* @param routes 路由规则
* @param rule 路由规则
* @param isReplaceDash 是否需要替换破折号为点号(dubbo需要)
* @return 路由过滤后的实例
*/
List<I> getMatchInstances(String serviceName, List<I> instances, List<Route> routes, boolean isReplaceDash);
List<I> getMatchInstances(String serviceName, List<I> instances, Rule rule, boolean isReplaceDash);

/**
* 根据请求信息选取路由的实例
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.huaweicloud.sermant.router.common.utils.CollectionUtils;
import com.huaweicloud.sermant.router.config.cache.ConfigCache;
import com.huaweicloud.sermant.router.config.entity.EnabledStrategy;
import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.RouterConfiguration;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.config.utils.RuleUtils;
Expand All @@ -35,6 +34,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* BaseLoadBalancerInterceptor服务
Expand Down Expand Up @@ -96,9 +96,9 @@ private List<Object> getTargetInstancesByRules(String targetName, List<Object> i
return instances;
}
List<Rule> rules = RuleUtils.getRules(configuration, targetName, path, AppCache.INSTANCE.getAppName());
List<Route> routes = RouteUtils.getRoutes(rules, header);
if (!CollectionUtils.isEmpty(routes)) {
return RuleStrategyHandler.INSTANCE.getMatchInstances(targetName, instances, routes);
Optional<Rule> ruleOptional = RouteUtils.getRoutes(rules, header);
if (ruleOptional.isPresent()) {
return RuleStrategyHandler.INSTANCE.getMatchInstances(targetName, instances, ruleOptional.get());
}
return RuleStrategyHandler.INSTANCE
.getMismatchInstances(targetName, instances, RuleUtils.getTags(rules, false), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.huaweicloud.sermant.router.spring.strategy;

import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.config.strategy.AbstractRuleStrategy;
import com.huaweicloud.sermant.router.spring.strategy.mapper.AbstractMetadataMapper;
import com.huaweicloud.sermant.router.spring.strategy.mapper.DefaultMetadataMapper;
Expand Down Expand Up @@ -64,11 +65,11 @@ private void init(AbstractMetadataMapper<Object> mapper) {
*
* @param serviceName 服务名
* @param instances 实例列表
* @param routes 路由规则
* @param rule 路由规则
* @return 路由匹配的实例
*/
public List<Object> getMatchInstances(String serviceName, List<Object> instances, List<Route> routes) {
return getRuleStrategy(instances).getMatchInstances(serviceName, instances, routes, false);
public List<Object> getMatchInstances(String serviceName, List<Object> instances, Rule rule) {
return getRuleStrategy(instances).getMatchInstances(serviceName, instances, rule, false);
}

/**
Expand Down
Loading

0 comments on commit f1e8a84

Please sign in to comment.