From a7afaf97df93b640924dba59db5a0b699dd928e2 Mon Sep 17 00:00:00 2001 From: Rain Yu Date: Tue, 20 Jan 2026 00:55:22 +0800 Subject: [PATCH] Implement tags filtering logic in TagStateRouter and add unit test for tag routing --- .../cluster/router/tag/TagStateRouter.java | 35 +++++++++++++++---- .../router/tag/TagStateRouterTest.java | 33 +++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java index cacb941b16a7..c42f9799eea9 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java @@ -36,6 +36,9 @@ import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -54,6 +57,7 @@ public class TagStateRouter extends AbstractStateRouter implements Configu private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TagStateRouter.class); private static final String RULE_SUFFIX = ".tag-router"; public static final char TAG_SEPERATOR = '|'; + private static final String TAGS_SUPPORT = "tags.support"; private volatile TagRouterRule tagRouterRule; private String application; @@ -137,8 +141,7 @@ public BitList> doRoute( } else { // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by // dynamic tag group but force=false. check static tag - result = filterInvoker( - invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY))); + result = filterInvoker(invokers, predicateEqual(tag, url.getParameter(TAGS_SUPPORT, false))); } // If there's no tagged providers that can match the current tagged request. force.tag is set by default // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. @@ -196,10 +199,9 @@ public BitList> doRoute( * @param invokers * @param url * @param invocation - * @param * @return */ - private BitList> filterUsingStaticTag(BitList> invokers, URL url, Invocation invocation) { + private BitList> filterUsingStaticTag(BitList> invokers, URL url, Invocation invocation) { BitList> result; // Dynamic param String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) @@ -207,8 +209,7 @@ private BitList> filterUsingStaticTag(BitList> invoker : invocation.getAttachment(TAG_KEY); // Tag request if (!StringUtils.isEmpty(tag)) { - result = filterInvoker( - invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY))); + result = filterInvoker(invokers, predicateEqual(tag, url.getParameter(TAGS_SUPPORT, false))); if (CollectionUtils.isEmpty(result) && !isForceUseTag(invocation)) { result = filterInvoker( invokers, @@ -221,6 +222,26 @@ private BitList> filterUsingStaticTag(BitList> invoker return result; } + private Predicate> predicateEqual(String tag, boolean tagsEnable) { + return invoker -> { + List consumerTags; + List providerTags; + if (tagsEnable) { + consumerTags = StringUtils.splitToList(tag, ','); + } else { + consumerTags = tag != null ? Arrays.asList(tag) : Collections.emptyList(); + } + boolean providerTagEnable = invoker.getUrl().getParameter(TAGS_SUPPORT, false); + if (providerTagEnable) { + providerTags = StringUtils.splitToList(invoker.getUrl().getParameter(TAG_KEY), ','); + } else { + String providerTag = invoker.getUrl().getParameter(TAG_KEY); + providerTags = providerTag != null ? Arrays.asList(providerTag) : Collections.emptyList(); + } + return !Collections.disjoint(consumerTags, providerTags); + }; + } + @Override public boolean isRuntime() { return tagRouterRule != null && tagRouterRule.isRuntime(); @@ -237,7 +258,7 @@ private boolean isForceUseTag(Invocation invocation) { invocation.getAttachment(FORCE_USE_TAG, this.getUrl().getParameter(FORCE_USE_TAG, "false"))); } - private BitList> filterInvoker(BitList> invokers, Predicate> predicate) { + private BitList> filterInvoker(BitList> invokers, Predicate> predicate) { if (invokers.stream().allMatch(predicate)) { return invokers; } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java index 79b34c682356..1d6270bbf6f2 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouterTest.java @@ -298,4 +298,37 @@ public void tagLevelForceTest() { Set selectedAddresses = TagStateRouter.selectAddressByTagLevel(tagAddresses, "beta", true); Assertions.assertEquals(addresses, selectedAddresses); } + + @Test + void testTagsRoutePickInvokers() { + StateRouter router = new TagStateRouterFactory().getRouter(TagRouterRule.class, url); + + List> originInvokers = new ArrayList<>(); + + URL url1 = URL.valueOf("test://127.0.0.1:7777/DemoInterface?dubbo.tag=tag2,tag1") + .setScopeModel(moduleModel); + url1 = url1.addParameter("tags.support", "true"); + URL url2 = URL.valueOf("test://127.0.0.1:7778/DemoInterface?dubbo.tag=tag1") + .setScopeModel(moduleModel); + URL url3 = URL.valueOf("test://127.0.0.1:7779/DemoInterface?dubbo.tag=tag2") + .setScopeModel(moduleModel); + URL url4 = URL.valueOf("test://127.0.0.1:7779/DemoInterface").setScopeModel(moduleModel); + Invoker invoker1 = new MockInvoker<>(url1, true); + Invoker invoker2 = new MockInvoker<>(url2, true); + Invoker invoker3 = new MockInvoker<>(url3, true); + Invoker invoker4 = new MockInvoker<>(url4, true); + + originInvokers.add(invoker1); + originInvokers.add(invoker2); + originInvokers.add(invoker3); + originInvokers.add(invoker4); + BitList> invokers = new BitList<>(originInvokers); + RpcInvocation invocation = new RpcInvocation(); + invocation.setAttachment(TAG_KEY, "tag2"); + List> filteredInvokers = + router.route(invokers.clone(), invokers.get(0).getUrl(), invocation, false, new Holder<>()); + Assertions.assertEquals(2, filteredInvokers.size()); + Assertions.assertTrue(filteredInvokers.contains(invoker1)); + Assertions.assertTrue(filteredInvokers.contains(invoker3)); + } }