This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new b7b4993  Add test case for RouterChain (#9732)
b7b4993 is described below

commit b7b499363507f23edc832b8ca98b39754c4d397b
Author: 灼华 <43363120+burnin...@users.noreply.github.com>
AuthorDate: Mon Mar 7 10:42:47 2022 +0800

    Add test case for RouterChain (#9732)
    
    * Add test case for RouterChain#route method
    
    * Add test case for RouterChain
---
 .../rpc/cluster/router/RouterSnapshotSwitcher.java |   2 +-
 .../condition/config/ListenableStateRouter.java    |   2 +-
 ...{ServiceRouter.java => ServiceStateRouter.java} |   4 +-
 .../config/ServiceStateRouterFactory.java          |   2 +-
 .../cluster/router/mesh/route/MeshRuleRouter.java  |   3 +-
 .../apache/dubbo/rpc/cluster/RouterChainTest.java  | 201 ++++++++++++++++++++-
 6 files changed, 206 insertions(+), 8 deletions(-)

diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotSwitcher.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotSwitcher.java
index 9ed5416..139a0bc 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotSwitcher.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/RouterSnapshotSwitcher.java
@@ -58,7 +58,7 @@ public class RouterSnapshotSwitcher {
     public void setSnapshot(String snapshot) {
         if (enable) {
             // lock free
-            recentSnapshot[offset.incrementAndGet() % MAX_LENGTH] = 
System.currentTimeMillis() + " - " + snapshot;
+            recentSnapshot[offset.getAndIncrement() % MAX_LENGTH] = 
System.currentTimeMillis() + " - " + snapshot;
         }
     }
 
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
index 2a2b309..c295a66 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
@@ -145,6 +145,6 @@ public abstract class ListenableStateRouter<T> extends 
AbstractStateRouter<T> im
 
     @Override
     public void stop() {
-        this.getRuleRepository().removeListener(ruleKey, this);
+        this.getRuleRepository().removeListener(ruleKey + RULE_SUFFIX, this);
     }
 }
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceRouter.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouter.java
similarity index 91%
rename from 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceRouter.java
rename to 
dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouter.java
index 82bea8c..064c600 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceRouter.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouter.java
@@ -22,10 +22,10 @@ import 
org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 /**
  * Service level router, "server-unique-name.condition-router"
  */
-public class ServiceRouter<T> extends ListenableStateRouter<T> {
+public class ServiceStateRouter<T> extends ListenableStateRouter<T> {
     public static final String NAME = "SERVICE_ROUTER";
 
-    public ServiceRouter(URL url) {
+    public ServiceStateRouter(URL url) {
         super(url, DynamicConfiguration.getRuleKey(url));
     }
 }
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouterFactory.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouterFactory.java
index 965e3bb..d5f3d6c 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouterFactory.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ServiceStateRouterFactory.java
@@ -32,7 +32,7 @@ public class ServiceStateRouterFactory extends 
CacheableStateRouterFactory {
 
     @Override
     protected <T> StateRouter<T> createRouter(Class<T> interfaceClass, URL 
url) {
-        return new ServiceRouter<T>(url);
+        return new ServiceStateRouter<T>(url);
     }
 
 }
diff --git 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
index 3ef8c4d..de4ab18 100644
--- 
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
+++ 
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
@@ -108,7 +108,8 @@ public abstract class MeshRuleRouter<T> extends 
AbstractStateRouter<T> implement
                 }
             }
         }
-        result = result.or(ruleCache.getUnmatchedInvokers());
+
+        // result = result.or(ruleCache.getUnmatchedInvokers());
 
         // empty protection
         if (result.isEmpty()) {
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java
index f57b717..4db393b 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/RouterChainTest.java
@@ -18,16 +18,38 @@ package org.apache.dubbo.rpc.cluster;
 
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.cluster.filter.DemoService;
 
+import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
+import org.apache.dubbo.rpc.cluster.router.condition.config.AppStateRouter;
+import 
org.apache.dubbo.rpc.cluster.router.condition.config.ListenableStateRouter;
+import org.apache.dubbo.rpc.cluster.router.condition.config.ServiceStateRouter;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleManager;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
 
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+import static 
org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX;
+import static org.mockito.Mockito.when;
 
 public class RouterChainTest {
 
@@ -36,7 +58,12 @@ public class RouterChainTest {
      */
     @Test
     public void testBuildRouterChain() {
+        RouterChain<DemoService> routerChain = createRouterChanin();
+        Assertions.assertEquals(0, routerChain.getRouters().size());
+        Assertions.assertEquals(5, routerChain.getStateRouters().size());
+    }
 
+    private RouterChain<DemoService> createRouterChanin() {
         Map<String, String> parameters = new HashMap<>();
         parameters.put(INTERFACE_KEY, DemoService.class.getName());
         parameters.put("registry", "zookeeper");
@@ -47,7 +74,177 @@ public class RouterChainTest {
             parameters);
 
         RouterChain<DemoService> routerChain = 
RouterChain.buildChain(DemoService.class, url);
-        Assertions.assertEquals(0, routerChain.getRouters().size());
-        Assertions.assertEquals(5, routerChain.getStateRouters().size());
+        return routerChain;
+    }
+
+    @Test
+    public void testRoute() {
+        RouterChain<DemoService> routerChain = createRouterChanin();
+
+        // mockInvoker will be filtered out by MockInvokersSelector
+        Invoker<DemoService> mockInvoker = createMockInvoker();
+
+        // invoker1 will be filtered out by MeshStateRouter
+        Map<String, String> map1 = new HashMap<>();
+        map1.put("env-sign", "yyyyyyy");
+        Invoker<DemoService> invoker1 = createNormalInvoker(map1);
+
+        // invoker2 will be filtered out by TagStateRouter
+        Map<String, String> map2 = new HashMap<>();
+        map2.put("env-sign", "xxx");
+        map2.put("tag1", "hello");
+        Invoker<DemoService> invoker2 = createNormalInvoker(map2);
+
+        // invoker3 will be filtered out by AppStateRouter
+        Map<String, String> map3 = new HashMap<>();
+        map3.put("env-sign", "xxx");
+        map3.put("tag1", "hello");
+        map3.put(TAG_KEY, "TAG_");
+        Invoker<DemoService> invoker3 = createNormalInvoker(map3);
+
+        // invoker4 will be filtered out by ServiceStateRouter
+        Map<String, String> map4 = new HashMap<>();
+        map4.put("env-sign", "xxx");
+        map4.put("tag1", "hello");
+        map4.put(TAG_KEY, "TAG_");
+        map4.put("timeout", "5000");
+        Invoker<DemoService> invoker4 = createNormalInvoker(map4);
+
+        // invoker5 is the only one returned at the end that is not filtered 
out
+        Map<String, String> map5 = new HashMap<>();
+        map5.put("env-sign", "xxx");
+        map5.put("tag1", "hello");
+        map5.put(TAG_KEY, "TAG_");
+        map5.put("timeout", "5000");
+        map5.put("serialization", "hessian2");
+        Invoker<DemoService> invoker5 = createNormalInvoker(map5);
+
+        BitList<Invoker<DemoService>> invokers = new 
BitList<>(Arrays.asList(mockInvoker, invoker1, invoker2, invoker3, invoker4, 
invoker5));
+        routerChain.setInvokers(invokers);
+
+        // mesh rule for MeshStateRouter
+        MeshRuleManager meshRuleManager = 
mockInvoker.getUrl().getOrDefaultModuleModel().getBeanFactory().getBean(MeshRuleManager.class);
+        ConcurrentHashMap<String, MeshAppRuleListener> appRuleListeners = 
meshRuleManager.getAppRuleListeners();
+        MeshAppRuleListener meshAppRuleListener = 
appRuleListeners.get(invoker1.getUrl().getRemoteApplication());
+        ConfigChangedEvent configChangedEvent = new 
ConfigChangedEvent("demo-route" + MESH_RULE_DATA_ID_SUFFIX, 
DynamicConfiguration.DEFAULT_GROUP,
+            MESH_RULE1 + "---\n" + MESH_RULE2, ConfigChangeType.ADDED);
+        meshAppRuleListener.process(configChangedEvent);
+
+
+        // condition rule for AppStateRouter&ServiceStateRouter
+        ListenableStateRouter serviceRouter = 
routerChain.getStateRouters().stream().filter(s -> s instanceof 
ServiceStateRouter).map(s -> (ListenableStateRouter) s).findAny().orElse(null);
+        ConfigChangedEvent serviceConditionEvent = new 
ConfigChangedEvent(DynamicConfiguration.getRuleKey(mockInvoker.getUrl()) + 
".condition-router", DynamicConfiguration.DEFAULT_GROUP,
+            SERVICE_CONDITION_RULE, ConfigChangeType.ADDED);
+        serviceRouter.process(serviceConditionEvent);
+
+        ListenableStateRouter appRouter = 
routerChain.getStateRouters().stream().filter(s -> s instanceof 
AppStateRouter).map(s -> (ListenableStateRouter) s).findAny().orElse(null);
+        ConfigChangedEvent appConditionEvent = new 
ConfigChangedEvent("app.condition-router", DynamicConfiguration.DEFAULT_GROUP,
+            APP_CONDITION_RULE, ConfigChangeType.ADDED);
+        appRouter.process(appConditionEvent);
+
+        // prepare consumerUrl and RpcInvocation
+        URL consumerUrl = 
URL.valueOf("consumer://localhost/DemoInterface?remote.application=app1");
+        RpcInvocation rpcInvocation = new RpcInvocation();
+        rpcInvocation.setServiceName("DemoService");
+        rpcInvocation.setObjectAttachment("trafficLabel", "xxx");
+        rpcInvocation.setObjectAttachment(TAG_KEY, "TAG_");
+
+        RpcContext.getServiceContext().setNeedPrintRouterSnapshot(true);
+        RouterSnapshotSwitcher routerSnapshotSwitcher = 
FrameworkModel.defaultModel().getBeanFactory().getBean(RouterSnapshotSwitcher.class);
+        
routerSnapshotSwitcher.addEnabledService("org.apache.dubbo.demo.DemoService");
+        // route
+        List<Invoker<DemoService>> result = routerChain.route(consumerUrl, 
invokers, rpcInvocation);
+        Assertions.assertEquals(result.size(), 1);
+        Assertions.assertTrue(result.contains(invoker5));
+
+        String snapshotLog =
+                 "[ Parent (Input: 6) (Current Node Output: 6) (Chain Node 
Output: 1) ] Input: 
localhost:9103,localhost:9103,localhost:9103,localhost:9103,localhost:9103 -> 
Chain Node Output: localhost:9103...\n" +
+                "  [ MockInvokersSelector (Input: 6) (Current Node Output: 5) 
(Chain Node Output: 1) Router message: invocation.need.mock not set. Return 
normal Invokers. ] Current Node Output: 
localhost:9103,localhost:9103,localhost:9103,localhost:9103,localhost:9103\n" +
+                "    [ StandardMeshRuleRouter (Input: 5) (Current Node Output: 
4) (Chain Node Output: 1) Router message: Match App: app Subset: isolation  ] 
Current Node Output: 
localhost:9103,localhost:9103,localhost:9103,localhost:9103\n" +
+                "      [ TagStateRouter (Input: 4) (Current Node Output: 3) 
(Chain Node Output: 1) Router message: Disable Tag Router. Reason: 
tagRouterRule is invalid or disabled ] Current Node Output: 
localhost:9103,localhost:9103,localhost:9103\n" +
+                "        [ ServiceStateRouter (Input: 3) (Current Node Output: 
3) (Chain Node Output: 1) Router message: null ] Current Node Output: 
localhost:9103,localhost:9103,localhost:9103\n" +
+                "          [ ConditionStateRouter (Input: 3) (Current Node 
Output: 2) (Chain Node Output: 2) Router message: Match return. ] Current Node 
Output: localhost:9103,localhost:9103\n" +
+                "          [ AppStateRouter (Input: 2) (Current Node Output: 
2) (Chain Node Output: 1) Router message: null ] Current Node Output: 
localhost:9103,localhost:9103\n" +
+                "            [ ConditionStateRouter (Input: 2) (Current Node 
Output: 1) (Chain Node Output: 1) Router message: Match return. ] Current Node 
Output: localhost:9103";
+        String[] snapshot = routerSnapshotSwitcher.cloneSnapshot();
+        Assertions.assertTrue(snapshot[0].contains(snapshotLog));
+
+        RpcContext.getServiceContext().setNeedPrintRouterSnapshot(false);
+        result = routerChain.route(consumerUrl, invokers, rpcInvocation);
+        Assertions.assertEquals(result.size(), 1);
+        Assertions.assertTrue(result.contains(invoker5));
+
+        routerChain.destroy();
+        Assertions.assertEquals(routerChain.getRouters().size(), 0);
+        Assertions.assertEquals(routerChain.getStateRouters().size(), 0);
+
     }
+
+    private Invoker<DemoService> createMockInvoker() {
+        URL url = 
URL.valueOf("mock://localhost:9103/DemoInterface?remote.application=app");
+        Invoker<DemoService> invoker = Mockito.mock(Invoker.class);
+        when(invoker.getUrl()).thenReturn(url);
+        return invoker;
+    }
+
+    private Invoker<DemoService> createNormalInvoker(Map<String, String> 
parameters) {
+        URL url = 
URL.valueOf("dubbo://localhost:9103/DemoInterface?remote.application=app");
+        if (CollectionUtils.isNotEmptyMap(parameters)) {
+            url = url.addParameters(parameters);
+        }
+        Invoker<DemoService> invoker = Mockito.mock(Invoker.class);
+        when(invoker.getUrl()).thenReturn(url);
+        return invoker;
+    }
+
+
+    private final static String MESH_RULE1 = "apiVersion: 
service.dubbo.apache.org/v1alpha1\n" +
+        "kind: DestinationRule\n" +
+        "metadata: { name: demo-route }\n" +
+        "spec:\n" +
+        "  host: demo\n" +
+        "  subsets:\n" +
+        "    - labels: { env-sign: xxx, tag1: hello }\n" +
+        "      name: isolation\n" +
+        "    - labels: { env-sign: yyy }\n" +
+        "      name: testing-trunk\n" +
+        "    - labels: { env-sign: zzz }\n" +
+        "      name: testing\n" +
+        "  trafficPolicy:\n" +
+        "    loadBalancer: { simple: ROUND_ROBIN }\n" +
+        "\n";
+
+    private final static String MESH_RULE2 = "apiVersion: 
service.dubbo.apache.org/v1alpha1\n" +
+        "kind: VirtualService\n" +
+        "metadata: { name: demo-route }\n" +
+        "spec:\n" +
+        "  dubbo:\n" +
+        "    - routedetail:\n" +
+        "        - match:\n" +
+        "            - attachments: \n" +
+        "                dubboContext: {trafficLabel: {regex: xxx}}\n" +
+        "          name: xxx-project\n" +
+        "          route:\n" +
+        "            - destination: {host: demo, subset: isolation}\n" +
+        "      services:\n" +
+        "        - {regex: DemoService}\n" +
+        "  hosts: [demo]\n";
+
+    private static final String APP_CONDITION_RULE = "scope: application\n" +
+        "force: true\n" +
+        "runtime: false\n" +
+        "enabled: true\n" +
+        "priority: 1\n" +
+        "key: demo-consumer\n" +
+        "conditions:\n" +
+        "- => serialization=hessian2";
+
+    private static final String SERVICE_CONDITION_RULE = "scope: service\n" +
+        "force: true\n" +
+        "runtime: false\n" +
+        "enabled: true\n" +
+        "priority: 1\n" +
+        "key: org.apache.dubbo.demo.DemoService\n" +
+        "conditions:\n" +
+        "- => timeout=5000";
 }

Reply via email to