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"; }