Skip to content

Commit

Permalink
*增加fallback路由
Browse files Browse the repository at this point in the history
Signed-off-by: provenceee <[email protected]>
  • Loading branch information
chengyouling authored and provenceee committed Jan 10, 2024
1 parent 9d1f090 commit 235ecdb
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ private List<Object> getTargetInvokersByRules(List<Object> invokers, Object invo
if (CollectionUtils.isEmpty(rules)) {
return invokers;
}
List<Route> routes = getRoutes(rules, DubboReflectUtils.getArguments(invocation),
Optional<Rule> ruleOptional = getRule(rules, DubboReflectUtils.getArguments(invocation),
parseAttachments(invocation));
if (!CollectionUtils.isEmpty(routes)) {
return RuleStrategyHandler.INSTANCE.getMatchInvokers(targetService, invokers, routes);
if (ruleOptional.isPresent()) {
return RuleStrategyHandler.INSTANCE.getFlowMatchInvokers(targetService, invokers, ruleOptional.get());
}
return RuleStrategyHandler.INSTANCE
.getMismatchInvokers(targetService, invokers, RuleUtils.getTags(rules), true);
Expand Down Expand Up @@ -202,11 +202,11 @@ private String getVersion(Map<String, String> queryMap) {
* @param attachments dubbo的attachments参数
* @return 匹配的路由
*/
private static List<Route> getRoutes(List<Rule> list, Object[] arguments, Map<String, Object> attachments) {
private 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 @@ -217,10 +217,10 @@ private static List<Route> getRoutes(List<Rule> list, Object[] arguments, Map<St
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 @@ -16,7 +16,6 @@

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 @@ -47,11 +46,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);
public List<Object> getFlowMatchInvokers(String serviceName, List<Object> invokers, Rule rule) {
return ruleStrategy.getFlowMatchInstances(serviceName, invokers, rule);
}

/**
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.META_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.getFlowMatchInvokers("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.getFlowMatchInvokers("foo", invokers, rule);
Assert.assertEquals(1, matchInvokers.size());
Assert.assertEquals(invoker2, matchInvokers.get(0));

Expand All @@ -125,7 +130,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.getFlowMatchInvokers("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 @@ -162,7 +167,7 @@ public void testApacheMismatch() {
routes.get(0).setWeight(0);

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

Expand All @@ -173,4 +178,93 @@ public void testApacheMismatch() {
Assert.assertEquals(1, mismatchInvoker.size());
Assert.assertEquals(invoker2, mismatchInvoker.get(0));
}

/**
* 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.getFlowMatchInvokers("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.META_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.getFlowMatchInvokers("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.getFlowMatchInvokers("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.getFlowMatchInvokers("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 @@ -21,7 +21,6 @@
import com.huaweicloud.sermant.router.common.utils.CollectionUtils;
import com.huaweicloud.sermant.router.config.entity.Match;
import com.huaweicloud.sermant.router.config.entity.Policy;
import com.huaweicloud.sermant.router.config.entity.Route;
import com.huaweicloud.sermant.router.config.entity.Rule;
import com.huaweicloud.sermant.router.config.utils.PolicyEventUtils;
import com.huaweicloud.sermant.router.config.utils.RuleUtils;
Expand Down Expand Up @@ -74,9 +73,35 @@ public AbstractRuleStrategy(String source, InstanceStrategy<I, Map<String, Strin
}

@Override
public List<I> getMatchInstances(String serviceName, List<I> instances, List<Route> routes) {
RouteResult<?> result = RuleUtils.getTargetTags(routes);
return getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
public List<I> getFlowMatchInstances(String serviceName, List<I> instances, Rule rule) {
RouteResult<?> result = RuleUtils.getTargetTags(rule.getRoute());
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());
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未能匹配实例,直接返回所有实例,反之通过mismatch处理
return result.isMatch() ? instances
: getInstances(getStrategy(result.isMatch()), result.getTags(), serviceName, instances, true);
}

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

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;
Expand All @@ -35,10 +34,10 @@ public interface RuleStrategy<I> {
*
* @param serviceName 服务名
* @param instances 实例列表
* @param routes 路由规则
* @param rule 路由规则
* @return 路由过滤后的实例
*/
List<I> getMatchInstances(String serviceName, List<I> instances, List<Route> routes);
List<I> getFlowMatchInstances(String serviceName, List<I> instances, Rule rule);

/**
* 选取路由的实例
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ public static void removeInvalidRules(List<EntireRule> list, boolean isReplaceDa
// 去掉无效的路由和修复同标签规则的路由
removeInvalidRoute(routes, kind, isReplaceDash, isAppendPrefix);

List<Route> fallback = rule.getFallback();
if (!CollectionUtils.isEmpty(fallback)) {
// 去掉无效的路由和修复同标签规则的路由
removeInvalidRoute(fallback, kind, isReplaceDash, isAppendPrefix);
}

// 去掉全是无效路由的规则
if (CollectionUtils.isEmpty(routes)) {
LOGGER.warning("Routes are invalid, rule will be removed.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* 流量匹配方式的路由处理器
Expand Down Expand Up @@ -140,9 +141,9 @@ private List<Object> getTargetInstancesByRules(String targetName, List<Object> i
if (CollectionUtils.isEmpty(rules)) {
return instances;
}
List<Route> routes = getRoutes(rules, header);
if (!CollectionUtils.isEmpty(routes)) {
return RuleStrategyHandler.INSTANCE.getMatchInstances(targetName, instances, routes);
Optional<Rule> ruleOptional = getRule(rules, header);
if (ruleOptional.isPresent()) {
return RuleStrategyHandler.INSTANCE.getFlowMatchInstances(targetName, instances, ruleOptional.get());
}
return RuleStrategyHandler.INSTANCE
.getMismatchInstances(targetName, instances, RuleUtils.getTags(rules), true);
Expand All @@ -155,14 +156,14 @@ private List<Object> getTargetInstancesByRules(String targetName, List<Object> i
* @param header header
* @return 匹配的路由
*/
private List<Route> getRoutes(List<Rule> list, Map<String, List<String>> header) {
private Optional<Rule> getRule(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 List<Route> getRoutes(Map<String, List<String>> header, Rule rule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

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;
Expand Down Expand Up @@ -65,11 +64,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);
public List<Object> getFlowMatchInstances(String serviceName, List<Object> instances, Rule rule) {
return getRuleStrategy(instances).getFlowMatchInstances(serviceName, instances, rule);
}

/**
Expand Down
Loading

0 comments on commit 235ecdb

Please sign in to comment.