Skip to content

Commit

Permalink
Merge pull request #1387 from chengyouling/1.0.x-fallback
Browse files Browse the repository at this point in the history
增加标签路由匹配fallback规则逻辑
  • Loading branch information
Sherlockhan authored Dec 19, 2023
2 parents 2ed4003 + 74b1b37 commit 514e698
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 34 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,93 @@ public void testApacheZone() {
Assert.assertEquals(2, mismatchInvoker.size());
Assert.assertEquals(invokers, mismatchInvoker);
}

/**
* rule中route有选中tag,但是没有符合版本的实例,invoker命中fallback版本实例的情况
*/
@Test
public void testAlibabaV1Fallback() {
setFallbackRoute();
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));
}

private void setFallbackRoute() {
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);
}

/**
* rule中设置route、fallback,且权重均有命中tag,但是invoker均未命中版本实例的情况
*/
@Test
public void testAlibabaV1NotMathRouteFallback() {
setFallbackRoute();
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());
}

/**
* rule中设置routes但权重计算未命中,invoker命中fallback版本实例的情况
*/
@Test
public void testAlibabaV1MathFallback() {
setFallbackRoute();
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));
}

/**
* rule中设置routes但权重计算未命中,同时fallback也未命中实例,invoker返回未设置规则版本号版本实例的情况
*/
@Test
public void testAlibabaV1BothNotMathFallbackRoute() {
setFallbackRoute();
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,9 +74,36 @@ 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);
return getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
public List<I> getMatchInstances(String serviceName, List<I> instances, Rule rule, boolean isReplaceDash) {
// match set routes
RouteResult<?> result = RuleUtils.getTargetTags(rule.getRoute(), isReplaceDash);
if (CollectionUtils.isEmpty(rule.getFallback())) {
return getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
}
if (result.isMatch()) {
// route命中标签,fallback有设置路由规则时,仅返回匹配的实例,如果存在直接返回
List<I> routeInstances = getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName,
instances, false);
if (!CollectionUtils.isEmpty(routeInstances)) {
return routeInstances;
}
}

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

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

// 结合上面result.isMatch()为true逻辑,如果为true未能匹配实例,直接返回所有实例,反之通过missmatch处理
return result.isMatch() ? instances
: getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
}

@Override
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

/**
* 路由插件工具类
Expand All @@ -46,14 +47,14 @@ private RouteUtils() {
* @param header header
* @return 匹配的路由
*/
public static List<Route> getRoutes(List<Rule> list, Map<String, List<String>> header) {
public static Optional<Rule> getRoutes(List<Rule> list, Map<String, List<String>> header) {
for (Rule rule : list) {
List<Route> routeList = getRoutes(header, rule);
if (!CollectionUtils.isEmpty(routeList)) {
return routeList;
return Optional.of(rule);
}
}
return Collections.emptyList();
return Optional.empty();
}

private static List<Route> getRoutes(Map<String, List<String>> header, Rule rule) {
Expand Down
Loading

0 comments on commit 514e698

Please sign in to comment.