Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tag support multi level #12673

Merged
merged 3 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,23 @@ public static String join(Collection<String> coll, String split) {
return sb.toString();
}

public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) {
if (ArrayUtils.isEmpty(array)) {
return EMPTY_STRING;
}
if (endIndex - startIndex <= 0) {
return EMPTY_STRING;
}
StringBuilder sb = new StringBuilder();
for (int i = startIndex; i < endIndex; i++) {
if (i > 0) {
sb.append(delimiter);
}
sb.append(array[i]);
}
return sb.toString();
}

/**
* parse key-value pair.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ void testJoin() throws Exception {
assertEquals(StringUtils.join(s), "123");
assertEquals(StringUtils.join(s, ','), "1,2,3");
assertEquals(StringUtils.join(s, ","), "1,2,3");
assertEquals(StringUtils.join(s, ',', 0, 1), "1");
assertEquals(StringUtils.join(s, ',', 0, 2), "1,2");
assertEquals(StringUtils.join(s, ',', 0, 3), "1,2,3");
assertEquals("", StringUtils.join(s, ',', 2, 0), "1,2");
}

@Test
Expand Down Expand Up @@ -502,4 +506,4 @@ void testStartsWithIgnoreCase() {
assertTrue(startsWithIgnoreCase("Dubbo.application.name", "dubbo.application."));

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;

import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

Expand All @@ -52,6 +53,7 @@ public class TagStateRouter<T> extends AbstractStateRouter<T> implements Configu
public static final String NAME = "TAG_ROUTER";
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TagStateRouter.class);
private static final String RULE_SUFFIX = ".tag-router";
public static final char TAG_SEPERATOR = '|';

private volatile TagRouterRule tagRouterRule;
private String application;
Expand Down Expand Up @@ -106,7 +108,8 @@ public BitList<Invoker<T>> doRoute(BitList<Invoker<T>> invokers, URL url, Invoca

// if we are requesting for a Provider with a specific tag
if (StringUtils.isNotEmpty(tag)) {
Set<String> addresses = tagRouterRuleCopy.getTagnameToAddresses().get(tag);
Map<String, Set<String>> tagnameToAddresses = tagRouterRuleCopy.getTagnameToAddresses();
Set<String> addresses = selectAddressByTagLevel(tagnameToAddresses, tag, isForceUseTag(invocation));
// filter by dynamic tag group first
if (addresses != null) { // null means tag not set
result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));
Expand Down Expand Up @@ -306,4 +309,34 @@ public void stop() {
public void setTagRouterRule(TagRouterRule tagRouterRule) {
this.tagRouterRule = tagRouterRule;
}

/**
* select addresses by tag with level
* <p>
* example:
* selector=beta|team1|partner1
* step1.select tagAddresses with selector=beta|team1|partner1, if result is empty, then run step2
* step2.select tagAddresses with selector=beta|team1, if result is empty, then run step3
* step3.select tagAddresses with selector=beta, if result is empty, result is null
* </p>
*
* @param tagAddresses
* @param tagSelector eg: beta|team1|partner1
* @return
*/
public static Set<String> selectAddressByTagLevel(Map<String, Set<String>> tagAddresses, String tagSelector, boolean isForce) {
if (isForce || StringUtils.isNotContains(tagSelector, TAG_SEPERATOR)) {
return tagAddresses.get(tagSelector);
}
String[] selectors = StringUtils.split(tagSelector, TAG_SEPERATOR);
for (int i = selectors.length; i > 0; i--) {
String selectorTmp = StringUtils.join(selectors, TAG_SEPERATOR, 0, i);
Set<String> addresses = tagAddresses.get(selectorTmp);
if (CollectionUtils.isNotEmpty(addresses)) {
return addresses;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,24 @@
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.cluster.router.MockInvoker;
//import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ModuleModel;

import com.google.common.collect.Sets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -256,4 +259,47 @@ private TagRouterRule getTagRule() {
TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig);
return tagRouterRule;
}

@Test
public void tagMultiLevelTest() {
String tagSelector = "beta|team1|partner1";
Set<String> address1 = Sets.newHashSet("192.168.5.1:20880");
Map<String, Set<String>> tagAddresses = new HashMap<>();
tagAddresses.put("beta", address1);
Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));

Set<String> address2 = Sets.newHashSet("192.168.5.2:20880");
tagAddresses.put("beta|team1", address2);
Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));

Set<String> address3 = Sets.newHashSet("192.168.5.3:20880");
tagAddresses.put("beta|team1|partner1", address3);
Assertions.assertEquals(address3, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));

tagSelector = "beta";
Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));
tagSelector = "beta|team1";
Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));
tagSelector = "beta|team1|partner1";
Assertions.assertEquals(address3, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));

tagSelector = "beta2";
Assertions.assertNull(TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));
tagSelector = "beta|team2";
Assertions.assertEquals(address1, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));
tagSelector = "beta|team1|partner2";
Assertions.assertEquals(address2, TagStateRouter.selectAddressByTagLevel(tagAddresses, tagSelector, false));


}

@Test
public void tagLevelForceTest() {
Set<String> addresses = Sets.newHashSet("192.168.1.223:20880");
Map<String, Set<String>> tagAddresses = new HashMap<>();
tagAddresses.put("beta", addresses);
Set<String> selectedAddresses = TagStateRouter.selectAddressByTagLevel(tagAddresses, "beta", true);
Assertions.assertEquals(addresses, selectedAddresses);
}

}