You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/03/16 11:51:45 UTC

[dubbo] branch 3.0-preview updated: Brand new routing rule (#7372)

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

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


The following commit(s) were added to refs/heads/3.0-preview by this push:
     new 7c505f7  Brand new routing rule (#7372)
7c505f7 is described below

commit 7c505f7467a7bbd6ef0daccac6f436673e6ad665
Author: qinliujie <li...@alibaba-inc.com>
AuthorDate: Tue Mar 16 19:51:23 2021 +0800

    Brand new routing rule (#7372)
---
 dubbo-cluster/pom.xml                              |    4 +-
 .../org/apache/dubbo/registry/AddressListener.java |    0
 .../router/mesh/route/MeshAppRuleListener.java     |   98 ++
 .../route/MeshRuleAddressListenerInterceptor.java  |   51 +
 .../cluster/router/mesh/route/MeshRuleManager.java |   79 ++
 .../cluster/router/mesh/route/MeshRuleRouter.java  |  359 +++++
 .../router/mesh/route/MeshRuleRouterFactory.java   |   29 +-
 .../rpc/cluster/router/mesh/rule/BaseRule.java     |   60 +
 .../router/mesh/rule/VsDestinationGroup.java       |   55 +
 .../rule/destination/ConnectionPoolSettings.java   |   21 +-
 .../mesh/rule/destination/DestinationRule.java     |   36 +-
 .../mesh/rule/destination/DestinationRuleSpec.java |   60 +
 .../router/mesh/rule/destination/Subset.java       |   45 +-
 .../router/mesh/rule/destination/TCPSettings.java  |   24 +-
 .../router/mesh/rule/destination/TcpKeepalive.java |   23 +-
 .../mesh/rule/destination/TrafficPolicy.java       |   35 +-
 .../destination/loadbalance/ConsistentHashLB.java  |   21 +-
 .../loadbalance/LoadBalancerSettings.java          |   43 +-
 .../rule/destination/loadbalance/SimpleLB.java     |   25 +-
 .../rule/virtualservice/DubboMatchRequest.java     |  130 ++
 .../mesh/rule/virtualservice/DubboRoute.java       |   62 +
 .../mesh/rule/virtualservice/DubboRouteDetail.java |   62 +
 .../rule/virtualservice/VirtualServiceRule.java    |   36 +-
 .../rule/virtualservice/VirtualServiceSpec.java    |   43 +-
 .../destination/DubboDestination.java              |   59 +
 .../destination/DubboRouteDestination.java         |   35 +-
 .../mesh/rule/virtualservice/match/BoolMatch.java  |   33 +-
 .../rule/virtualservice/match/DoubleMatch.java     |   63 +
 .../virtualservice/match/DoubleRangeMatch.java     |   53 +
 .../virtualservice/match/DubboAttachmentMatch.java |   76 ++
 .../rule/virtualservice/match/DubboMethodArg.java  |   90 ++
 .../virtualservice/match/DubboMethodMatch.java     |  128 ++
 .../rule/virtualservice/match/ListBoolMatch.java   |   21 +-
 .../rule/virtualservice/match/ListDoubleMatch.java |   36 +-
 .../rule/virtualservice/match/ListStringMatch.java |   37 +-
 .../rule/virtualservice/match/StringMatch.java     |  105 ++
 .../util/VsDestinationGroupRuleDispatcher.java     |   53 +
 .../mesh/util/VsDestinationGroupRuleListener.java  |   22 +-
 .../org.apache.dubbo.registry.AddressListener      |    1 +
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |    1 +
 .../router/mesh/route/MeshAppRuleListenerTest.java |  180 +++
 .../router/mesh/route/MeshRuleManagerTest.java     |  160 +++
 .../mesh/route/MeshRuleRouterFactoryTest.java      |   27 +-
 .../router/mesh/route/MeshRuleRouterTest.java      | 1407 ++++++++++++++++++++
 .../router/mesh/rule/DestinationRuleTest.java      |  102 ++
 .../router/mesh/rule/VirtualServiceRuleTest.java   |   31 +-
 .../rule/virtualservice/DubboMatchRequestTest.java |  140 ++
 .../rule/virtualservice/match/BoolMatchTest.java   |   35 +-
 .../rule/virtualservice/match/DoubleMatchTest.java |  101 ++
 .../match/DubboAttachmentMatchTest.java            |  178 +++
 .../virtualservice/match/DubboMethodMatchTest.java |  156 +++
 .../virtualservice/match/ListDoubleMatchTest.java  |   52 +
 .../virtualservice/match/ListStringMatchTest.java  |   54 +
 .../rule/virtualservice/match/StringMatchTest.java |   81 ++
 .../util/VsDestinationGroupRuleDispatcherTest.java |   74 +
 .../src/test/resources/DestinationRuleTest.yaml    |   33 +
 .../src/test/resources/DestinationRuleTest2.yaml   |   58 +
 .../src/test/resources/VirtualServiceTest.yaml     |   41 +
 58 files changed, 4780 insertions(+), 344 deletions(-)

diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
index bd62974..bb58849 100644
--- a/dubbo-cluster/pom.xml
+++ b/dubbo-cluster/pom.xml
@@ -14,7 +14,8 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.dubbo</groupId>
@@ -39,7 +40,6 @@
             <groupId>org.yaml</groupId>
             <artifactId>snakeyaml</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-framework</artifactId>
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/registry/AddressListener.java
similarity index 100%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/registry/AddressListener.java
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
new file mode 100644
index 0000000..3946ba4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.VsDestinationGroupRuleDispatcher;
+import org.yaml.snakeyaml.Yaml;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+
+public class MeshAppRuleListener implements ConfigurationListener {
+
+    public static final Logger logger = LoggerFactory.getLogger(MeshAppRuleListener.class);
+
+    private final VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+    private String appName;
+
+    private VsDestinationGroup vsDestinationGroupHolder;
+
+    public MeshAppRuleListener(String appName) {
+        this.appName = appName;
+    }
+
+    public void receiveConfigInfo(String configInfo) {
+        logger.info(MessageFormat.format("[MeshAppRule] Received rule for app [{0}]: {1}.",
+                appName, configInfo));
+        try {
+
+            VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+            vsDestinationGroup.setAppName(appName);
+
+            Yaml yaml = new Yaml();
+            Yaml yaml2 = new Yaml();
+            Iterable objectIterable = yaml.loadAll(configInfo);
+            for (Object result : objectIterable) {
+
+                Map resultMap = (Map) result;
+                if (resultMap.get("kind").equals("DestinationRule")) {
+                    DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+                    vsDestinationGroup.getDestinationRuleList().add(destinationRule);
+
+                } else if (resultMap.get("kind").equals("VirtualService")) {
+                    VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+                    vsDestinationGroup.getVirtualServiceRuleList().add(virtualServiceRule);
+                }
+            }
+
+            vsDestinationGroupHolder = vsDestinationGroup;
+        } catch (Exception e) {
+            logger.error("[MeshAppRule] parse failed: " + configInfo, e);
+        }
+        if (vsDestinationGroupHolder != null) {
+            vsDestinationGroupRuleDispatcher.post(vsDestinationGroupHolder);
+        }
+
+    }
+
+    public void register(MeshRuleRouter subscriber) {
+        if (vsDestinationGroupHolder != null) {
+            subscriber.onRuleChange(vsDestinationGroupHolder);
+        }
+        vsDestinationGroupRuleDispatcher.register(subscriber);
+    }
+
+    //
+    public void unregister(MeshRuleRouter sub) {
+        vsDestinationGroupRuleDispatcher.unregister(sub);
+    }
+
+    @Override
+    public void process(ConfigChangedEvent event) {
+        receiveConfigInfo(event.getContent());
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java
new file mode 100644
index 0000000..d4cf551
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleAddressListenerInterceptor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.registry.AddressListener;
+import org.apache.dubbo.rpc.cluster.Directory;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Activate(order = 670)
+public class MeshRuleAddressListenerInterceptor implements AddressListener {
+
+    private static final Object mark = new Object();
+    private static ConcurrentHashMap<String, Object> appMap = new ConcurrentHashMap<String, Object>();
+
+    @Override
+    public List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory) {
+
+        if (addresses != null && !addresses.isEmpty()) {
+            for (URL serviceURL : addresses) {
+
+                String app = serviceURL.getRemoteApplication();
+                if (app != null && !app.isEmpty()) {
+                    if (appMap.putIfAbsent(app, mark) == null) {
+                        MeshRuleManager.subscribeAppRule(app);
+                    }
+                }
+            }
+        }
+
+        return addresses;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
new file mode 100644
index 0000000..aa1630f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public final class MeshRuleManager {
+
+    public static final Logger logger = LoggerFactory.getLogger(MeshRuleManager.class);
+
+    private static final String MESH_RULE_DATA_ID_SUFFIX = ".MESHAPPRULE";
+    private static final String GROUP = "DEFAULT_GROUP";
+
+    private static ConcurrentHashMap<String, MeshAppRuleListener> appRuleListeners = new ConcurrentHashMap<>();
+
+    public synchronized static void subscribeAppRule(String app) {
+
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener(app);
+        String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX;
+        DynamicConfiguration configuration = ApplicationModel.getEnvironment().getDynamicConfiguration()
+                .orElse(null);
+
+        if (configuration == null) {
+            logger.warn("Doesn't support DynamicConfiguration!");
+            return;
+        }
+
+        try {
+            String rawConfig = configuration.getConfig(appRuleDataId, GROUP, 5000L);
+            if (rawConfig != null) {
+                meshAppRuleListener.receiveConfigInfo(rawConfig);
+            }
+        } catch (Throwable throwable) {
+            logger.error("get MeshRuleManager app rule failed.", throwable);
+        }
+
+        configuration.addListener(appRuleDataId, GROUP, meshAppRuleListener);
+        appRuleListeners.put(app, meshAppRuleListener);
+    }
+
+    public static void register(String app, MeshRuleRouter subscriber) {
+        MeshAppRuleListener meshAppRuleListener = appRuleListeners.get(app);
+        if (meshAppRuleListener == null) {
+            logger.warn("appRuleListener can't find when Router register");
+            return;
+        }
+        meshAppRuleListener.register(subscriber);
+    }
+
+    public static void unregister(MeshRuleRouter subscriber) {
+        Collection<MeshAppRuleListener> listeners = appRuleListeners.values();
+        for (MeshAppRuleListener listener : listeners) {
+            listener.unregister(subscriber);
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..34b9e7f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.VsDestinationGroupRuleListener;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+
+public class MeshRuleRouter implements Router, VsDestinationGroupRuleListener {
+
+    protected int priority = -500;
+    protected boolean force = false;
+    protected URL url;
+
+    private VsDestinationGroup vsDestinationGroup;
+
+    private Map<String, String> sourcesLables = new HashMap<>();
+
+    protected List<Invoker<?>> invokerList = new ArrayList<>();
+
+    Map<String, List<Invoker<?>>> subsetMap;
+
+    private String remoteAppName;
+
+    public MeshRuleRouter(URL url) {
+        this.url = url;
+        sourcesLables.putAll(url.getParameters());
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
+
+        List<DubboRouteDestination> routeDestination = getDubboRouteDestination(invocation);
+
+        if (routeDestination == null) {
+            return invokers;
+        } else {
+            Random random = new Random();
+            int index = random.nextInt(routeDestination.size());
+            DubboRouteDestination dubboRouteDestination = routeDestination.get(index);
+
+            DubboDestination dubboDestination = dubboRouteDestination.getDestination();
+
+            String host = dubboDestination.getHost();
+            String subset = dubboDestination.getSubset();
+
+            List<Invoker<?>> result;
+
+            Map<String, List<Invoker<?>>> subsetMapCopy = this.subsetMap;
+
+            //TODO make intersection with invokers
+            if (subsetMapCopy != null) {
+
+                do {
+                    result = subsetMapCopy.get(subset);
+
+                    if (result != null && result.size() > 0) {
+                        return (List) result;
+                    }
+
+                    dubboRouteDestination = dubboDestination.getFallback();
+                    if (dubboRouteDestination == null) {
+                        break;
+                    }
+                    dubboDestination = dubboRouteDestination.getDestination();
+
+                    host = dubboDestination.getHost();
+                    subset = dubboDestination.getSubset();
+                } while (true);
+
+                return null;
+            }
+        }
+
+        return invokers;
+    }
+
+    @Override
+    public <T> void notify(List<Invoker<T>> invokers) {
+        List invokerList = invokers == null ? Collections.emptyList() : invokers;
+        this.invokerList = invokerList;
+        registerAppRule(invokerList);
+        computeSubset();
+    }
+
+
+    private void registerAppRule(List<Invoker<?>> invokers) {
+        if (StringUtils.isEmpty(remoteAppName)) {
+            synchronized (this) {
+                if (StringUtils.isEmpty(remoteAppName) && invokers != null && invokers.size() > 0) {
+                    for (Invoker invoker : invokers) {
+                        String applicationName = invoker.getUrl().getRemoteApplication();
+                        if (StringUtils.isNotEmpty(applicationName) && !"unknown".equals(applicationName)) {
+                            remoteAppName = applicationName;
+                            MeshRuleManager.register(remoteAppName, this);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    public void onRuleChange(VsDestinationGroup vsDestinationGroup) {
+        this.vsDestinationGroup = vsDestinationGroup;
+        computeSubset();
+    }
+
+    @Override
+    public boolean isRuntime() {
+        return true;
+    }
+
+    @Override
+    public boolean isForce() {
+        return force;
+    }
+
+    @Override
+    public int getPriority() {
+        return priority;
+    }
+
+    private List<DubboRouteDestination> getDubboRouteDestination(Invocation invocation) {
+
+        if (vsDestinationGroup != null) {
+
+            List<VirtualServiceRule> virtualServiceRuleList = vsDestinationGroup.getVirtualServiceRuleList();
+            if (virtualServiceRuleList.size() > 0) {
+                for (VirtualServiceRule virtualServiceRule : virtualServiceRuleList) {
+                    DubboRoute dubboRoute = getDubboRoute(virtualServiceRule, invocation);
+                    if (dubboRoute != null) {
+                        return getDubboRouteDestination(dubboRoute, invocation);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    protected DubboRoute getDubboRoute(VirtualServiceRule virtualServiceRule, Invocation invocation) {
+        String serviceName = invocation.getServiceName();
+
+        VirtualServiceSpec spec = virtualServiceRule.getSpec();
+        List<DubboRoute> dubboRouteList = spec.getDubbo();
+        if (dubboRouteList.size() > 0) {
+            for (DubboRoute dubboRoute : dubboRouteList) {
+                List<StringMatch> stringMatchList = dubboRoute.getServices();
+                if (stringMatchList == null || stringMatchList.size() == 0) {
+                    return dubboRoute;
+                }
+                for (StringMatch stringMatch : stringMatchList) {
+                    if (StringMatch.isMatch(stringMatch, serviceName)) {
+                        return dubboRoute;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+
+    protected List<DubboRouteDestination> getDubboRouteDestination(DubboRoute dubboRoute, Invocation invocation) {
+
+        List<DubboRouteDetail> dubboRouteDetailList = dubboRoute.getRoutedetail();
+        if (dubboRouteDetailList.size() > 0) {
+            DubboRouteDetail dubboRouteDetail = findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            if (dubboRouteDetail != null) {
+                return dubboRouteDetail.getRoute();
+            }
+        }
+
+        return null;
+    }
+
+    protected DubboRouteDetail findMatchDubboRouteDetail(List<DubboRouteDetail> dubboRouteDetailList, Invocation invocation) {
+
+        String methodName = invocation.getMethodName();
+        String[] parameterTypeList = invocation.getCompatibleParamSignatures();
+        Object[] parameters = invocation.getArguments();
+
+
+        for (DubboRouteDetail dubboRouteDetail : dubboRouteDetailList) {
+            List<DubboMatchRequest> matchRequestList = dubboRouteDetail.getMatch();
+            if (matchRequestList == null || matchRequestList.size() == 0) {
+                return dubboRouteDetail;
+            }
+
+            boolean match = true;
+
+            //FIXME to deal with headers
+            for (DubboMatchRequest dubboMatchRequest : matchRequestList) {
+                if (!DubboMatchRequest.isMatch(dubboMatchRequest, methodName, parameterTypeList, parameters,
+                        sourcesLables,
+                        new HashMap<>(), invocation.getAttachments(),
+                        new HashMap<>())) {
+                    match = false;
+                    break;
+                }
+            }
+
+            if (match) {
+                return dubboRouteDetail;
+            }
+        }
+        return null;
+    }
+
+
+    protected synchronized void computeSubset() {
+        if (invokerList == null || invokerList.size() == 0) {
+            this.subsetMap = null;
+            return;
+        }
+
+        if (vsDestinationGroup == null) {
+            this.subsetMap = null;
+            return;
+        }
+
+        Map<String, List<Invoker<?>>> subsetMap = computeSubsetMap(invokerList, vsDestinationGroup.getDestinationRuleList());
+
+        if (subsetMap.size() == 0) {
+            this.subsetMap = null;
+        } else {
+            this.subsetMap = subsetMap;
+        }
+    }
+
+
+    protected Map<String, List<Invoker<?>>> computeSubsetMap(List<Invoker<?>> invokers, List<DestinationRule> destinationRules) {
+        Map<String, List<Invoker<?>>> subsetMap = new HashMap<>();
+
+        for (DestinationRule destinationRule : destinationRules) {
+            DestinationRuleSpec destinationRuleSpec = destinationRule.getSpec();
+            String host = destinationRuleSpec.getHost();
+            List<Subset> subsetList = destinationRuleSpec.getSubsets();
+
+            for (Subset subset : subsetList) {
+                String subsetName = subset.getName();
+                List<Invoker<?>> subsetInvokerList = new ArrayList<>();
+                subsetMap.put(subsetName, subsetInvokerList);
+
+                Map<String, String> labels = subset.getLabels();
+
+                for (Invoker<?> invoker : invokers) {
+                    Map<String, String> parameters = invoker.getUrl().getParameters();
+                    if (containMapKeyValue(parameters, labels)) {
+                        subsetInvokerList.add(invoker);
+                    }
+                }
+            }
+        }
+
+        return subsetMap;
+    }
+
+
+    protected boolean containMapKeyValue(Map<String, String> originMap, Map<String, String> inputMap) {
+        if (inputMap == null || inputMap.size() == 0) {
+            return true;
+        }
+
+        for (Map.Entry<String, String> entry : inputMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+
+            String originMapValue = originMap.get(key);
+            if (!value.equals(originMapValue)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    // just for test
+    protected void setVsDestinationGroup(VsDestinationGroup vsDestinationGroup) {
+        this.vsDestinationGroup = vsDestinationGroup;
+    }
+
+    // just for test
+    protected void setSourcesLables(Map<String, String> sourcesLables) {
+        this.sourcesLables = sourcesLables;
+    }
+
+    // just for test
+    protected void setInvokerList(List<Invoker<?>> invokerList) {
+        this.invokerList = invokerList;
+    }
+
+    // just for test
+    protected void setSubsetMap(Map<String, List<Invoker<?>>> subsetMap) {
+        this.subsetMap = subsetMap;
+    }
+
+
+    public VsDestinationGroup getVsDestinationGroup() {
+        return vsDestinationGroup;
+    }
+
+    public Map<String, String> getSourcesLables() {
+        return sourcesLables;
+    }
+
+    public List<Invoker<?>> getInvokerList() {
+        return invokerList;
+    }
+
+    public Map<String, List<Invoker<?>>> getSubsetMap() {
+        return subsetMap;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
similarity index 64%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
index ff77800..fdbe8e9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactory.java
@@ -14,24 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
-
-import java.util.List;
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
 
-@SPI
-public interface AddressListener {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.RouterFactory;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
 
-}
\ No newline at end of file
+@Activate(order = -50)
+public class MeshRuleRouterFactory implements RouterFactory {
+    @Override
+    public Router getRouter(URL url) {
+        return new MeshRuleRouter(url);
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
new file mode 100644
index 0000000..ffb236f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import java.util.Map;
+
+
+public class BaseRule {
+    private String apiVersion;
+    private String kind;
+    private Map<String,String> metadata;
+
+    public String getApiVersion() {
+        return apiVersion;
+    }
+
+    public void setApiVersion(String apiVersion) {
+        this.apiVersion = apiVersion;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public Map<String, String> getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(Map<String, String> metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "BaseRule{" +
+                "apiVersion='" + apiVersion + '\'' +
+                ", kind='" + kind + '\'' +
+                ", metadata=" + metadata +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
new file mode 100644
index 0000000..1d310ee
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class VsDestinationGroup {
+    private String appName;
+    private List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+    private List<DestinationRule> destinationRuleList = new ArrayList<>();
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
+
+    public List<VirtualServiceRule> getVirtualServiceRuleList() {
+        return virtualServiceRuleList;
+    }
+
+    public void setVirtualServiceRuleList(List<VirtualServiceRule> virtualServiceRuleList) {
+        this.virtualServiceRuleList = virtualServiceRuleList;
+    }
+
+    public List<DestinationRule> getDestinationRuleList() {
+        return destinationRuleList;
+    }
+
+    public void setDestinationRuleList(List<DestinationRule> destinationRuleList) {
+        this.destinationRuleList = destinationRuleList;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
index ff77800..32b7af9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/ConnectionPoolSettings.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ConnectionPoolSettings {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
index ff77800..42bc049 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRule.java
@@ -14,24 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class DestinationRule extends BaseRule {
+    private DestinationRuleSpec spec;
 
-}
\ No newline at end of file
+    public DestinationRuleSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(DestinationRuleSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRule{" +
+                "base=" + super.toString() +
+                ", spec=" + spec +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java
new file mode 100644
index 0000000..57d8b06
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/DestinationRuleSpec.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
+
+import java.util.List;
+
+
+public class DestinationRuleSpec {
+    private String host;
+    private List<Subset> subsets;
+    private TrafficPolicy trafficPolicy;
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public List<Subset> getSubsets() {
+        return subsets;
+    }
+
+    public void setSubsets(List<Subset> subsets) {
+        this.subsets = subsets;
+    }
+
+    public TrafficPolicy getTrafficPolicy() {
+        return trafficPolicy;
+    }
+
+    public void setTrafficPolicy(TrafficPolicy trafficPolicy) {
+        this.trafficPolicy = trafficPolicy;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRuleSpec{" +
+                "host='" + host + '\'' +
+                ", subsets=" + subsets +
+                ", trafficPolicy=" + trafficPolicy +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
index ff77800..9438ec4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/Subset.java
@@ -14,24 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import java.util.Map;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class Subset {
+    private String name;
+    private Map<String, String> labels;
 
-}
\ No newline at end of file
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(Map<String, String> labels) {
+        this.labels = labels;
+    }
+
+    @Override
+    public String toString() {
+        return "Subset{" +
+                "name='" + name + '\'' +
+                ", labels=" + labels +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
index ff77800..09dc93a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TCPSettings.java
@@ -14,24 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class TCPSettings {
+    private int maxConnections;
+    private int connectTimeout;
+    private TcpKeepalive tcpKeepalive;
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
index ff77800..3bc43c7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TcpKeepalive.java
@@ -14,24 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class TcpKeepalive {
+    private int probes;
+    private int time;
+    private int interval;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
similarity index 57%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
index ff77800..7f771f0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/TrafficPolicy.java
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.LoadBalancerSettings;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class TrafficPolicy {
+    private LoadBalancerSettings loadBalancer;
 
-}
\ No newline at end of file
+    public LoadBalancerSettings getLoadBalancer() {
+        return loadBalancer;
+    }
+
+    public void setLoadBalancer(LoadBalancerSettings loadBalancer) {
+        this.loadBalancer = loadBalancer;
+    }
+
+    @Override
+    public String toString() {
+        return "TrafficPolicy{" +
+                "loadBalancer=" + loadBalancer +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
index ff77800..213dfee 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/ConsistentHashLB.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ConsistentHashLB {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
similarity index 51%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
index ff77800..1f8b60e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/LoadBalancerSettings.java
@@ -14,24 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class LoadBalancerSettings {
+    private SimpleLB simple;
+    private ConsistentHashLB consistentHash;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public SimpleLB getSimple() {
+        return simple;
+    }
 
-}
\ No newline at end of file
+    public void setSimple(SimpleLB simple) {
+        this.simple = simple;
+    }
+
+    public ConsistentHashLB getConsistentHash() {
+        return consistentHash;
+    }
+
+    public void setConsistentHash(ConsistentHashLB consistentHash) {
+        this.consistentHash = consistentHash;
+    }
+
+    @Override
+    public String toString() {
+        return "LoadBalancerSettings{" +
+                "simple=" + simple +
+                ", consistentHash=" + consistentHash +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
index ff77800..003f1f6 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/destination/loadbalance/SimpleLB.java
@@ -14,24 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public enum SimpleLB {
+    ROUND_ROBIN,
+    LEAST_CONN,
+    RANDOM,
+    PASSTHROUGH
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
new file mode 100644
index 0000000..832bdfe
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DoubleMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+
+import java.util.Map;
+
+
+public class DubboMatchRequest {
+    private String name;
+    private DubboMethodMatch method;
+    private Map<String, String> sourceLabels;
+    private DubboAttachmentMatch attachments;
+    private Map<String, StringMatch> headers;
+    private DoubleMatch threshold;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public DubboMethodMatch getMethod() {
+        return method;
+    }
+
+    public void setMethod(DubboMethodMatch method) {
+        this.method = method;
+    }
+
+    public Map<String, String> getSourceLabels() {
+        return sourceLabels;
+    }
+
+    public void setSourceLabels(Map<String, String> sourceLabels) {
+        this.sourceLabels = sourceLabels;
+    }
+
+    public DubboAttachmentMatch getAttachments() {
+        return attachments;
+    }
+
+    public void setAttachments(DubboAttachmentMatch attachments) {
+        this.attachments = attachments;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    public DoubleMatch getThreshold() {
+        return threshold;
+    }
+
+    public void setThreshold(DoubleMatch threshold) {
+        this.threshold = threshold;
+    }
+
+
+    public static boolean isMatch(DubboMatchRequest dubboMatchRequest,
+                                  String methodName, String[] parameterTypeList, Object[] parameters,
+                                  Map<String, String> sourceLabels,
+                                  Map<String, String> eagleeyeContext, Map<String, String> dubboContext,
+                                  Map<String, String> headers
+    ) {
+        if (dubboMatchRequest.getMethod() != null) {
+            if (!DubboMethodMatch.isMatch(dubboMatchRequest.getMethod(), methodName, parameterTypeList, parameters)) {
+                return false;
+            }
+        }
+
+        if (dubboMatchRequest.getSourceLabels() != null) {
+            for (Map.Entry<String, String> entry : dubboMatchRequest.getSourceLabels().entrySet()) {
+                String value = sourceLabels.get(entry.getKey());
+                if (value == null || !entry.getValue().equals(value)) {
+                    return false;
+                }
+            }
+        }
+
+        if (dubboMatchRequest.getAttachments() != null) {
+            if (!DubboAttachmentMatch.isMatch(dubboMatchRequest.getAttachments(),eagleeyeContext,dubboContext)){
+                return false;
+            }
+        }
+
+        //TODO headers
+
+
+        return true;
+
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMatchRequest{" +
+                "name='" + name + '\'' +
+                ", method=" + method +
+                ", sourceLabels=" + sourceLabels +
+                ", attachments=" + attachments +
+                ", headers=" + headers +
+                ", threshold=" + threshold +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
new file mode 100644
index 0000000..d6bf124
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+
+import java.util.List;
+
+
+public class DubboRoute {
+    private String name;
+    private List<StringMatch> services;
+    private List<DubboRouteDetail> routedetail;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<StringMatch> getServices() {
+        return services;
+    }
+
+    public void setServices(List<StringMatch> services) {
+        this.services = services;
+    }
+
+    public List<DubboRouteDetail> getRoutedetail() {
+        return routedetail;
+    }
+
+    public void setRoutedetail(List<DubboRouteDetail> routedetail) {
+        this.routedetail = routedetail;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRoute{" +
+                "name='" + name + '\'' +
+                ", services=" + services +
+                ", routedetail=" + routedetail +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
new file mode 100644
index 0000000..ce42591
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+
+import java.util.List;
+
+
+public class DubboRouteDetail {
+    private String name;
+    private List<DubboMatchRequest> match;
+    private List<DubboRouteDestination> route;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<DubboMatchRequest> getMatch() {
+        return match;
+    }
+
+    public void setMatch(List<DubboMatchRequest> match) {
+        this.match = match;
+    }
+
+    public List<DubboRouteDestination> getRoute() {
+        return route;
+    }
+
+    public void setRoute(List<DubboRouteDestination> route) {
+        this.route = route;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRouteDetail{" +
+                "name='" + name + '\'' +
+                ", match=" + match +
+                ", route=" + route +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
index ff77800..8f5497a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
@@ -14,24 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class VirtualServiceRule extends BaseRule {
+    private VirtualServiceSpec spec;
 
-}
\ No newline at end of file
+    public VirtualServiceSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(VirtualServiceSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceRule{" +
+                "base=" + super.toString() +
+                ", spec=" + spec +
+                '}';
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
index ff77800..648fe3b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
@@ -14,24 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class VirtualServiceSpec {
+    private List<String> hosts;
+    private List<DubboRoute> dubbo;
 
-}
\ No newline at end of file
+    public List<String> getHosts() {
+        return hosts;
+    }
+
+    public void setHosts(List<String> hosts) {
+        this.hosts = hosts;
+    }
+
+    public List<DubboRoute> getDubbo() {
+        return dubbo;
+    }
+
+    public void setDubbo(List<DubboRoute> dubbo) {
+        this.dubbo = dubbo;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceSpec{" +
+                "hosts=" + hosts +
+                ", dubbo=" + dubbo +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
new file mode 100644
index 0000000..c21a008
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination;
+
+
+public class DubboDestination {
+    private String host;
+    private String subset;
+    private int port;
+    private DubboRouteDestination fallback;
+
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getSubset() {
+        return subset;
+    }
+
+    public void setSubset(String subset) {
+        this.subset = subset;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public DubboRouteDestination getFallback() {
+        return fallback;
+    }
+
+    public void setFallback(DubboRouteDestination fallback) {
+        this.fallback = fallback;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
index ff77800..b9588c4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class DubboRouteDestination {
+    private DubboDestination destination;
+    private int weight;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public DubboDestination getDestination() {
+        return destination;
+    }
 
-}
\ No newline at end of file
+    public void setDestination(DubboDestination destination) {
+        this.destination = destination;
+    }
+
+    public int getWeight() {
+        return weight;
+    }
+
+    public void setWeight(int weight) {
+        this.weight = weight;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
index ff77800..6d76054 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+public class BoolMatch {
+    private Boolean exact;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+    public Boolean getExact() {
+        return exact;
+    }
 
-}
\ No newline at end of file
+    public void setExact(Boolean exact) {
+        this.exact = exact;
+    }
+
+    public static boolean isMatch(BoolMatch boolMatch,boolean input){
+        if (boolMatch.getExact() != null){
+            return input == boolMatch.getExact();
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
new file mode 100644
index 0000000..2bd094e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DoubleMatch {
+    private Double exact;
+    private DoubleRangeMatch range;
+    private Double mod;
+
+    public Double getExact() {
+        return exact;
+    }
+
+    public void setExact(Double exact) {
+        this.exact = exact;
+    }
+
+    public DoubleRangeMatch getRange() {
+        return range;
+    }
+
+    public void setRange(DoubleRangeMatch range) {
+        this.range = range;
+    }
+
+    public Double getMod() {
+        return mod;
+    }
+
+    public void setMod(Double mod) {
+        this.mod = mod;
+    }
+
+
+    public static boolean isMatch(DoubleMatch doubleMatch, Double input) {
+        if (doubleMatch.getExact() != null && doubleMatch.getMod() == null) {
+            return input.equals(doubleMatch.getExact());
+        } else if (doubleMatch.getRange() != null) {
+            return DoubleRangeMatch.isMatch(doubleMatch.getRange(), input);
+        } else if (doubleMatch.getExact() != null && doubleMatch.getMod() != null) {
+            Double result = input % doubleMatch.getMod();
+            return result.equals(doubleMatch.getExact());
+        }
+
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
new file mode 100644
index 0000000..dd69338
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DoubleRangeMatch {
+    private Double start;
+    private Double end;
+
+    public Double getStart() {
+        return start;
+    }
+
+    public void setStart(Double start) {
+        this.start = start;
+    }
+
+    public Double getEnd() {
+        return end;
+    }
+
+    public void setEnd(Double end) {
+        this.end = end;
+    }
+
+
+    public static boolean isMatch(DoubleRangeMatch doubleRangeMatch, Double input) {
+        if (doubleRangeMatch.getStart() != null && doubleRangeMatch.getEnd() != null) {
+            return input.compareTo(doubleRangeMatch.getStart()) >= 0 && input.compareTo(doubleRangeMatch.getEnd()) < 0;
+        } else if (doubleRangeMatch.getStart() != null) {
+            return input.compareTo(doubleRangeMatch.getStart()) >= 0;
+        } else if (doubleRangeMatch.getEnd() != null) {
+            return input.compareTo(doubleRangeMatch.getEnd()) < 0;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
new file mode 100644
index 0000000..b368e5b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.Map;
+
+
+public class DubboAttachmentMatch {
+    private Map<String, StringMatch> eagleeyecontext;
+    private Map<String, StringMatch> dubbocontext;
+
+    public Map<String, StringMatch> getEagleeyecontext() {
+        return eagleeyecontext;
+    }
+
+    public void setEagleeyecontext(Map<String, StringMatch> eagleeyecontext) {
+        this.eagleeyecontext = eagleeyecontext;
+    }
+
+    public Map<String, StringMatch> getDubbocontext() {
+        return dubbocontext;
+    }
+
+    public void setDubbocontext(Map<String, StringMatch> dubbocontext) {
+        this.dubbocontext = dubbocontext;
+    }
+
+    public static boolean isMatch(DubboAttachmentMatch dubboAttachmentMatch, Map<String, String> eagleeyeContext, Map<String, String> dubboContext) {
+        if (dubboAttachmentMatch.getDubbocontext() != null) {
+            for (Map.Entry<String, StringMatch> stringStringMatchEntry : dubboAttachmentMatch.getDubbocontext().entrySet()) {
+                String key = stringStringMatchEntry.getKey();
+                StringMatch stringMatch = stringStringMatchEntry.getValue();
+
+                String dubboContextValue = dubboContext.get(key);
+                if (dubboContextValue == null) {
+                    return false;
+                }
+                if (!StringMatch.isMatch(stringMatch, dubboContextValue)) {
+                    return false;
+                }
+            }
+        }
+
+        if (dubboAttachmentMatch.getEagleeyecontext() != null) {
+            for (Map.Entry<String, StringMatch> stringStringMatchEntry : dubboAttachmentMatch.getEagleeyecontext().entrySet()) {
+                String key = stringStringMatchEntry.getKey();
+                StringMatch stringMatch = stringStringMatchEntry.getValue();
+
+                String eagleeyeContextValue = eagleeyeContext.get(key);
+                if (eagleeyeContextValue == null) {
+                    return false;
+                }
+                if (!StringMatch.isMatch(stringMatch, eagleeyeContextValue)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
new file mode 100644
index 0000000..5d21572
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class DubboMethodArg {
+    private int index;
+    private String type;
+    private ListStringMatch str_value;
+    private ListDoubleMatch num_value;
+    private BoolMatch bool_value;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public ListStringMatch getStr_value() {
+        return str_value;
+    }
+
+    public void setStr_value(ListStringMatch str_value) {
+        this.str_value = str_value;
+    }
+
+    public ListDoubleMatch getNum_value() {
+        return num_value;
+    }
+
+    public void setNum_value(ListDoubleMatch num_value) {
+        this.num_value = num_value;
+    }
+
+    public BoolMatch getBool_value() {
+        return bool_value;
+    }
+
+    public void setBool_value(BoolMatch bool_value) {
+        this.bool_value = bool_value;
+    }
+
+    public static boolean isMatch(DubboMethodArg dubboMethodArg, Object input) {
+
+        if (dubboMethodArg.getStr_value() != null) {
+            return ListStringMatch.isMatch(dubboMethodArg.getStr_value(), (String) input);
+        } else if (dubboMethodArg.getNum_value() != null) {
+            return ListDoubleMatch.isMatch(dubboMethodArg.getNum_value(), Double.valueOf(input.toString()));
+        } else if (dubboMethodArg.getBool_value() != null) {
+            return BoolMatch.isMatch(dubboMethodArg.getBool_value(), (Boolean) input);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodArg{" +
+                "index=" + index +
+                ", type='" + type + '\'' +
+                ", str_value=" + str_value +
+                ", num_value=" + num_value +
+                ", bool_value=" + bool_value +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
new file mode 100644
index 0000000..e68ea40
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.List;
+import java.util.Map;
+
+
+public class DubboMethodMatch {
+    private StringMatch name_match;
+    private Integer argc;
+    private List<DubboMethodArg> args;
+    private List<StringMatch> argp;
+    private Map<String, StringMatch> headers;
+
+    public StringMatch getName_match() {
+        return name_match;
+    }
+
+    public void setName_match(StringMatch name_match) {
+        this.name_match = name_match;
+    }
+
+    public Integer getArgc() {
+        return argc;
+    }
+
+    public void setArgc(Integer argc) {
+        this.argc = argc;
+    }
+
+    public List<DubboMethodArg> getArgs() {
+        return args;
+    }
+
+    public void setArgs(List<DubboMethodArg> args) {
+        this.args = args;
+    }
+
+    public List<StringMatch> getArgp() {
+        return argp;
+    }
+
+    public void setArgp(List<StringMatch> argp) {
+        this.argp = argp;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    public static boolean isMatch(DubboMethodMatch dubboMethodMatch, String methodName, String[] parameterTypeList, Object[] parameters) {
+        StringMatch nameMatch = dubboMethodMatch.getName_match();
+        if (nameMatch != null && !StringMatch.isMatch(nameMatch, methodName)) {
+            return false;
+        }
+
+        Integer argc = dubboMethodMatch.getArgc();
+        if (argc != null &&
+                ((argc != 0 && (parameters == null || parameters.length == 0)) || (argc != parameters.length))) {
+            return false;
+        }
+        List<StringMatch> argp = dubboMethodMatch.getArgp();
+        if (argp != null) {
+            if (((parameterTypeList == null || parameterTypeList.length == 0) && argp.size() > 0)
+                    || (argp.size() != parameterTypeList.length)) {
+                return false;
+            }
+
+            for (int index = 0; index < argp.size(); index++) {
+                if (!StringMatch.isMatch(argp.get(index), parameterTypeList[index])) {
+                    return false;
+                }
+            }
+        }
+
+        List<DubboMethodArg> args = dubboMethodMatch.getArgs();
+
+        if (args != null && args.size() > 0) {
+            if (parameters == null || parameters.length == 0) {
+                return false;
+            }
+
+            for (DubboMethodArg dubboMethodArg : args) {
+                int index = dubboMethodArg.getIndex();
+                if (index >= parameters.length) {
+                    throw new IndexOutOfBoundsException("DubboMethodArg index >= parameters.length");
+                }
+                if (!DubboMethodArg.isMatch(dubboMethodArg, parameters[index])) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodMatch{" +
+                "name_match=" + name_match +
+                ", argc=" + argc +
+                ", args=" + args +
+                ", argp=" + argp +
+                ", headers=" + headers +
+                '}';
+    }
+}
+
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
index ff77800..9418bdf 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
@@ -14,24 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
-
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public class ListBoolMatch {
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
index ff77800..c06397d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
@@ -14,24 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class ListDoubleMatch {
+    private List<DoubleMatch> oneof;
 
-}
\ No newline at end of file
+    public List<DoubleMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<DoubleMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+    public static boolean isMatch(ListDoubleMatch listDoubleMatch, Double input) {
+
+        for (DoubleMatch doubleMatch : listDoubleMatch.getOneof()) {
+            if (DoubleMatch.isMatch(doubleMatch, input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
similarity index 58%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
index ff77800..e23d623 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
@@ -14,24 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
 import java.util.List;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class ListStringMatch {
+    private List<StringMatch> oneof;
 
-}
\ No newline at end of file
+    public List<StringMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<StringMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+
+    public static boolean isMatch(ListStringMatch listStringMatch, String input) {
+
+        for (StringMatch stringMatch : listStringMatch.getOneof()) {
+            if (StringMatch.isMatch(stringMatch, input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
new file mode 100644
index 0000000..555ee5b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+public class StringMatch {
+    private String exact;
+    private String prefix;
+    private String regex;
+    private String noempty;
+    private String empty;
+
+
+    public String getExact() {
+        return exact;
+    }
+
+    public void setExact(String exact) {
+        this.exact = exact;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getRegex() {
+        return regex;
+    }
+
+    public void setRegex(String regex) {
+        this.regex = regex;
+    }
+
+    public String getNoempty() {
+        return noempty;
+    }
+
+    public void setNoempty(String noempty) {
+        this.noempty = noempty;
+    }
+
+    public String getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(String empty) {
+        this.empty = empty;
+    }
+
+
+    public static boolean isMatch(StringMatch stringMatch, String input) {
+        if (stringMatch.getExact() != null && input != null) {
+            if (input.equals(stringMatch.getExact())) {
+                return true;
+            }
+        } else if (stringMatch.getPrefix() != null && input != null) {
+            if (input.startsWith(stringMatch.getPrefix())) {
+                return true;
+            }
+        } else if (stringMatch.getRegex() != null && input != null) {
+            if (input.matches(stringMatch.getRegex())) {
+                return true;
+            }
+        } else if (stringMatch.getEmpty() != null) {
+            return input == null || "".equals(input);
+        } else if (stringMatch.getNoempty() != null) {
+            return input != null && input.length() > 0;
+        } else {
+            return false;
+        }
+
+        return false;
+    }
+
+
+    @Override
+    public String toString() {
+        return "StringMatch{" +
+                "exact='" + exact + '\'' +
+                ", prefix='" + prefix + '\'' +
+                ", regex='" + regex + '\'' +
+                ", noempty='" + noempty + '\'' +
+                ", empty='" + empty + '\'' +
+                '}';
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java
new file mode 100644
index 0000000..4c1fae2
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcher.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+
+import java.util.Set;
+
+
+public class VsDestinationGroupRuleDispatcher {
+
+    private Set<VsDestinationGroupRuleListener> listenerSet = new ConcurrentHashSet<>();
+
+    public synchronized void post(VsDestinationGroup vsDestinationGroup) {
+        for (VsDestinationGroupRuleListener vsDestinationGroupRuleListener : listenerSet) {
+            try {
+                vsDestinationGroupRuleListener.onRuleChange(vsDestinationGroup);
+            } catch (Throwable throwable) {
+
+            }
+        }
+    }
+
+    public synchronized boolean register(VsDestinationGroupRuleListener listener) {
+        if (listener == null) {
+            return false;
+        }
+        return listenerSet.add(listener);
+    }
+
+    public synchronized void unregister(VsDestinationGroupRuleListener listener) {
+        if (listener == null) {
+            return;
+        }
+        listenerSet.remove(listener);
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
similarity index 61%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
index ff77800..9e830ef 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleListener.java
@@ -14,24 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
-
-}
\ No newline at end of file
+public interface VsDestinationGroupRuleListener {
+    void onRuleChange(VsDestinationGroup vsDestinationGroup);
+}
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener
new file mode 100644
index 0000000..754e5ee
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener
@@ -0,0 +1 @@
+mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleAddressListenerInterceptor
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
index 2a807f0..1890efc 100644
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
@@ -5,3 +5,4 @@ service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactor
 app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
 tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
 mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
+mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleRouterFactory
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
new file mode 100644
index 0000000..8b89daa
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class MeshAppRuleListenerTest {
+
+    @Test
+    public void receiveConfigInfo() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("qinliujie");
+
+        MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+        meshAppRuleListener.register(meshRuleRouter);
+
+        meshAppRuleListener.receiveConfigInfo("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" +
+                "---\n" +
+                "\n" +
+                "apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: VirtualService\n" +
+                "metadata: {name: demo-route}\n" +
+                "spec:\n" +
+                "  dubbo:\n" +
+                "    - routedetail:\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: xxx}\n" +
+                "          name: xxx-project\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: isolation}\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                "          name: testing-trunk\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing-trunk}\n" +
+                "        - name: testing\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing}\n" +
+                "      services:\n" +
+                "        - {regex: ccc}\n" +
+                "  hosts: [demo]\n");
+
+
+        ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+        verify(meshRuleRouter, times(1)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup = captor.getValue();
+
+        assertTrue(vsDestinationGroup.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup.getDestinationRuleList().size() == 1);
+        assertTrue(vsDestinationGroup.getVirtualServiceRuleList().size() == 1);
+
+
+        meshAppRuleListener.receiveConfigInfo("");
+        verify(meshRuleRouter, times(2)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup1 = captor.getAllValues().get(captor.getAllValues().size() - 1);
+
+        assertTrue(vsDestinationGroup1.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup1.getDestinationRuleList().size() == 0);
+        assertTrue(vsDestinationGroup1.getVirtualServiceRuleList().size() == 0);
+    }
+
+    @Test
+    public void register() {
+    }
+
+    @Test
+    public void unregister() {
+    }
+
+    @Test
+    public void process() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("qinliujie");
+
+        MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+        meshAppRuleListener.register(meshRuleRouter);
+
+        ConfigChangedEvent configChangedEvent = new ConfigChangedEvent("qinliujie", "HSF", "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" +
+                "---\n" +
+                "\n" +
+                "apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                "kind: VirtualService\n" +
+                "metadata: {name: demo-route}\n" +
+                "spec:\n" +
+                "  dubbo:\n" +
+                "    - routedetail:\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: xxx}\n" +
+                "          name: xxx-project\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: isolation}\n" +
+                "        - match:\n" +
+                "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                "          name: testing-trunk\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing-trunk}\n" +
+                "        - name: testing\n" +
+                "          route:\n" +
+                "            - destination: {host: demo, subset: testing}\n" +
+                "      services:\n" +
+                "        - {regex: ccc}\n" +
+                "  hosts: [demo]\n", ConfigChangeType.MODIFIED);
+
+
+        meshAppRuleListener.process(configChangedEvent);
+
+        ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+        verify(meshRuleRouter, times(1)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup = captor.getValue();
+
+        assertTrue(vsDestinationGroup.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup.getDestinationRuleList().size() == 1);
+        assertTrue(vsDestinationGroup.getVirtualServiceRuleList().size() == 1);
+
+
+        meshAppRuleListener.receiveConfigInfo("");
+        verify(meshRuleRouter, times(2)).onRuleChange(captor.capture());
+
+        VsDestinationGroup vsDestinationGroup1 = captor.getAllValues().get(captor.getAllValues().size() - 1);
+
+        assertTrue(vsDestinationGroup1.getAppName().equals("qinliujie"));
+        assertTrue(vsDestinationGroup1.getDestinationRuleList().size() == 0);
+        assertTrue(vsDestinationGroup1.getVirtualServiceRuleList().size() == 0);
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
new file mode 100644
index 0000000..7638c85
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class MeshRuleManagerTest {
+
+    @Test
+    public void subscribeAppRule() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            MeshRuleManager.subscribeAppRule("test");
+
+            ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+            Mockito.verify(dynamicConfiguration).getConfig(captor.capture(), anyString(), anyLong());
+
+            String result = captor.getValue();
+
+            assertEquals("test.MESHAPPRULE", result);
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+
+
+    }
+
+    @Test
+    public void register() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            when(dynamicConfiguration.getConfig(anyString(), anyString(), anyLong())).thenReturn("apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                    "kind: VirtualService\n" +
+                    "metadata: {name: demo-route}\n" +
+                    "spec:\n" +
+                    "  dubbo:\n" +
+                    "    - routedetail:\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: xxx}\n" +
+                    "          name: xxx-project\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: isolation}\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                    "          name: testing-trunk\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing-trunk}\n" +
+                    "        - name: testing\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing}\n" +
+                    "      services:\n" +
+                    "        - {regex: ccc}\n" +
+                    "  hosts: [demo]\n");
+
+            MeshRuleManager.subscribeAppRule("test");
+
+
+            MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+
+            MeshRuleManager.register("test", meshRuleRouter);
+
+            ArgumentCaptor<VsDestinationGroup> captor = ArgumentCaptor.forClass(VsDestinationGroup.class);
+
+
+            Mockito.verify(meshRuleRouter).onRuleChange(captor.capture());
+
+            VsDestinationGroup result = captor.getValue();
+
+            assertNotNull(result);
+            assertEquals("test", result.getAppName());
+            assertEquals(1, result.getVirtualServiceRuleList().size());
+            assertEquals(0, result.getDestinationRuleList().size());
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+    }
+
+    @Test
+    public void unregister() {
+        Optional<DynamicConfiguration> before = ApplicationModel.getEnvironment().getDynamicConfiguration();
+        try {
+            DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
+
+            ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
+
+            when(dynamicConfiguration.getConfig(anyString(), anyString(), anyLong())).thenReturn("apiVersion: service.dubbo.apache.org/v1alpha1\n" +
+                    "kind: VirtualService\n" +
+                    "metadata: {name: demo-route}\n" +
+                    "spec:\n" +
+                    "  dubbo:\n" +
+                    "    - routedetail:\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: xxx}\n" +
+                    "          name: xxx-project\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: isolation}\n" +
+                    "        - match:\n" +
+                    "            - sourceLabels: {trafficLabel: testing-trunk}\n" +
+                    "          name: testing-trunk\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing-trunk}\n" +
+                    "        - name: testing\n" +
+                    "          route:\n" +
+                    "            - destination: {host: demo, subset: testing}\n" +
+                    "      services:\n" +
+                    "        - {regex: ccc}\n" +
+                    "  hosts: [demo]\n");
+
+            MeshRuleManager.subscribeAppRule("test");
+
+
+            MeshRuleRouter meshRuleRouter = mock(MeshRuleRouter.class);
+
+            MeshRuleManager.register("test", meshRuleRouter);
+
+            MeshRuleManager.unregister(meshRuleRouter);
+
+        } finally {
+            ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
similarity index 63%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
index ff77800..7bdc47a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterFactoryTest.java
@@ -14,24 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+import org.junit.jupiter.api.Test;
 
-import java.util.List;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-@SPI
-public interface AddressListener {
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+public class MeshRuleRouterFactoryTest {
 
+    @Test
+    public void getRouter() {
+        MeshRuleRouterFactory ruleRouterFactory = new MeshRuleRouterFactory();
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("demoService");
+        ruleRouterFactory.getRouter(url);
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
new file mode 100644
index 0000000..8262cd6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
@@ -0,0 +1,1407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class MeshRuleRouterTest {
+
+    @Test
+    public void containMapKeyValue() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Map<String, String> originMap = new HashMap<>();
+
+        originMap.put("key1", "value1");
+        originMap.put("key2", "value2");
+        originMap.put("key3", "value3");
+
+        Map<String, String> inputMap = new HashMap<>();
+
+        inputMap.put("key1", "value1");
+        inputMap.put("key2", "value2");
+
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, inputMap));
+
+        inputMap.put("key4", "value4");
+        assertFalse(meshRuleRouter.containMapKeyValue(originMap, inputMap));
+
+
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, null));
+        assertTrue(meshRuleRouter.containMapKeyValue(originMap, new HashMap<>()));
+
+    }
+
+    @Test
+    public void computeSubsetMap() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        //--
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        //--
+
+
+        Map<String, List<Invoker<?>>> result = meshRuleRouter.computeSubsetMap(invokers, destinationRules);
+
+        assertTrue(result.size() == 3);
+        assertTrue(result.containsKey("test1"));
+        assertTrue(result.containsKey("test2"));
+        assertTrue(result.containsKey("test4"));
+
+        assertTrue(result.get("test1").size() == 1);
+        assertTrue(result.get("test2").size() == 1);
+        assertTrue(result.get("test4").size() == 0);
+
+    }
+
+    @Test
+    public void computeSubset() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+
+        meshRuleRouter.setInvokerList(null);
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        meshRuleRouter.computeSubset();
+
+        assertNotNull(meshRuleRouter.getSubsetMap());
+        assertTrue(meshRuleRouter.getSubsetMap().size() == 3);
+    }
+
+    @Test
+    public void findMatchDubboRouteDetail() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+
+        assertNull(meshRuleRouter.findMatchDubboRouteDetail(new ArrayList<>(), invocation));
+
+        //--
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNotNull(result);
+            assertEquals("test", result.getName());
+        }
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            {
+                DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+                dubboRouteDetail.setName("test");
+
+                List<DubboMatchRequest> match = new ArrayList<>();
+
+                DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+                DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+                StringMatch stringMatch = new StringMatch();
+                stringMatch.setExact("sayHi");
+                dubboMethodMatch.setName_match(stringMatch);
+
+                dubboMatchRequest.setMethod(dubboMethodMatch);
+                match.add(dubboMatchRequest);
+
+                dubboRouteDetail.setMatch(match);
+
+                dubboRouteDetailList.add(dubboRouteDetail);
+            }
+
+
+            {
+                DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+                dubboRouteDetail.setName("test2");
+
+                List<DubboMatchRequest> match = new ArrayList<>();
+
+                DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+                DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+                StringMatch stringMatch = new StringMatch();
+                stringMatch.setExact("sayHello");
+                dubboMethodMatch.setName_match(stringMatch);
+
+                dubboMatchRequest.setMethod(dubboMethodMatch);
+                match.add(dubboMatchRequest);
+
+                dubboRouteDetail.setMatch(match);
+
+                dubboRouteDetailList.add(dubboRouteDetail);
+            }
+
+            DubboRouteDetail result = meshRuleRouter.findMatchDubboRouteDetail(dubboRouteDetailList, invocation);
+            assertNotNull(result);
+            assertEquals("test2", result.getName());
+        }
+
+    }
+
+    @Test
+    public void getDubboRouteDestination() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+
+        DubboRoute dubboRoute = new DubboRoute();
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            assertNull(meshRuleRouter.getDubboRouteDestination(dubboRoute, invocation));
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            assertNotNull(meshRuleRouter.getDubboRouteDestination(dubboRoute, invocation));
+        }
+    }
+
+    @Test
+    public void getDubboRoute() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+        DubboRoute dubboRoute = new DubboRoute();
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+
+
+            dubboRoute.setServices(new ArrayList<>());
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setExact("otherService");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNull(result);
+        }
+
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+        {
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHi");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+            DubboRoute result = meshRuleRouter.getDubboRoute(virtualServiceRule, invocation);
+
+            assertNotNull(result);
+        }
+
+
+    }
+
+    @Test
+    public void testNotify() {
+
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+
+        meshRuleRouter.setInvokerList(null);
+        meshRuleRouter.computeSubset();
+
+        assertNull(meshRuleRouter.getSubsetMap());
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test4");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test4");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+        meshRuleRouter.notify((List) invokers);
+
+        assertNotNull(meshRuleRouter.getSubsetMap());
+
+    }
+
+    @Test
+    public void route() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> inputInvokers = new ArrayList<>();
+
+        URL inputURL = mock(URL.class);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            dubboRouteDestinations.add(new DubboRouteDestination());
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setExact("otherService");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+        }
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test1");
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertNotEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+            assertEquals(1, meshRuleRouter.route((List) inputInvokers, inputURL, invocation).size());
+
+            Map<String, String> invokerParameterMap = new HashMap<>();
+            invokerParameterMap.put("env", "test1");
+
+            assertEquals(invokerParameterMap, ((Invoker) meshRuleRouter.route((List) inputInvokers, inputURL, invocation).get(0)).getUrl().getParameters());
+        }
+    }
+
+
+    @Test
+    public void routeFallback() {
+        URL url = mock(URL.class);
+        when(url.getServiceKey()).thenReturn("test");
+        MeshRuleRouter meshRuleRouter = new MeshRuleRouter(url);
+
+        List<Invoker<?>> inputInvokers = new ArrayList<>();
+
+        URL inputURL = mock(URL.class);
+
+        Invocation invocation = mock(Invocation.class);
+        when(invocation.getMethodName()).thenReturn("sayHello");
+        when(invocation.getArguments()).thenReturn(new Object[]{"qinliujie"});
+        when(invocation.getCompatibleParamSignatures()).thenReturn(new String[]{String.class.getName()});
+        when(invocation.getServiceName()).thenReturn("demoService");
+
+
+        List<Invoker<?>> invokers = new ArrayList<>();
+
+        //--
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test1");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test2");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        {
+            Invoker<?> invoker1 = mock(Invoker.class);
+            URL invoker1URL = mock(URL.class);
+            Map<String, String> invoker1Map = new HashMap<>();
+            invoker1Map.put("env", "test3");
+
+            when(invoker1URL.getParameters()).thenReturn(invoker1Map);
+            when(invoker1.getUrl()).thenReturn(invoker1URL);
+
+            invokers.add(invoker1);
+        }
+
+
+        meshRuleRouter.setInvokerList(invokers);
+
+
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+
+        List<DestinationRule> destinationRules = new ArrayList<>();
+
+        //--
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test1");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test1");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        {
+            DestinationRule destinationRule1 = new DestinationRule();
+
+            DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+            destinationRuleSpec.setHost("test1");
+
+            List<Subset> subsetList = new ArrayList<>();
+
+            Subset subset = new Subset();
+            subset.setName("test2");
+
+            Map<String, String> subsetTest1Lables = new HashMap<>();
+            subsetTest1Lables.put("env", "test2");
+            subset.setLabels(subsetTest1Lables);
+
+            subsetList.add(subset);
+
+            destinationRuleSpec.setSubsets(subsetList);
+
+            destinationRule1.setSpec(destinationRuleSpec);
+            destinationRules.add(destinationRule1);
+        }
+
+        vsDestinationGroup.setDestinationRuleList(destinationRules);
+
+        meshRuleRouter.setVsDestinationGroup(vsDestinationGroup);
+
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test5");
+
+
+            DubboRouteDestination fallbackDubboRouteDestination = new DubboRouteDestination();
+            DubboDestination fallbackDestination = new DubboDestination();
+            fallbackDestination.setSubset("test1");
+            fallbackDubboRouteDestination.setDestination(fallbackDestination);
+
+
+            destination.setFallback(fallbackDubboRouteDestination);
+
+
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+            assertNotEquals(inputInvokers, meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+            assertEquals(1, meshRuleRouter.route((List) inputInvokers, inputURL, invocation).size());
+
+            Map<String, String> invokerParameterMap = new HashMap<>();
+            invokerParameterMap.put("env", "test1");
+
+            assertEquals(invokerParameterMap, ((Invoker) meshRuleRouter.route((List) inputInvokers, inputURL, invocation).get(0)).getUrl().getParameters());
+        }
+
+        {
+            DubboRoute dubboRoute = new DubboRoute();
+            List<DubboRouteDetail> dubboRouteDetailList = new ArrayList<>();
+            DubboRouteDetail dubboRouteDetail = new DubboRouteDetail();
+            dubboRouteDetail.setName("test");
+
+            List<DubboMatchRequest> match = new ArrayList<>();
+
+            DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch stringMatch = new StringMatch();
+            stringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(stringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+
+            match.add(dubboMatchRequest);
+
+            dubboRouteDetail.setMatch(match);
+
+            List<DubboRouteDestination> dubboRouteDestinations = new ArrayList<>();
+            DubboRouteDestination dubboRouteDestination = new DubboRouteDestination();
+            DubboDestination destination = new DubboDestination();
+            destination.setSubset("test5");
+
+
+            DubboRouteDestination fallbackDubboRouteDestination = new DubboRouteDestination();
+            DubboDestination fallbackDestination = new DubboDestination();
+            fallbackDestination.setSubset("test11");
+            fallbackDubboRouteDestination.setDestination(fallbackDestination);
+
+
+            destination.setFallback(fallbackDubboRouteDestination);
+
+
+            dubboRouteDestination.setDestination(destination);
+            dubboRouteDestinations.add(dubboRouteDestination);
+
+
+            dubboRouteDetail.setRoute(dubboRouteDestinations);
+
+            dubboRouteDetailList.add(dubboRouteDetail);
+            dubboRoute.setRoutedetail(dubboRouteDetailList);
+            List<StringMatch> serviceMatchList = new ArrayList<>();
+            StringMatch serviceNameMatch = new StringMatch();
+            serviceNameMatch.setRegex(".*");
+
+            serviceMatchList.add(serviceNameMatch);
+
+            dubboRoute.setServices(serviceMatchList);
+
+            VirtualServiceRule virtualServiceRule = new VirtualServiceRule();
+            //virtualServiceRule.
+
+
+            VirtualServiceSpec spec = new VirtualServiceSpec();
+            List<DubboRoute> dubbo = new ArrayList<>();
+            dubbo.add(dubboRoute);
+
+            spec.setDubbo(dubbo);
+            virtualServiceRule.setSpec(spec);
+
+            List<VirtualServiceRule> virtualServiceRuleList = new ArrayList<>();
+            virtualServiceRuleList.add(virtualServiceRule);
+            vsDestinationGroup.setVirtualServiceRuleList(virtualServiceRuleList);
+            meshRuleRouter.computeSubset();
+
+            assertNull(meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+
+            meshRuleRouter.setSubsetMap(null);
+            assertNotNull(meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
+        }
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
new file mode 100644
index 0000000..91f5ac7
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.SimpleLB;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+public class DestinationRuleTest {
+
+    @Test
+    public void parserTest() {
+        Yaml yaml = new Yaml();
+        DestinationRule destinationRule = yaml.loadAs(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest.yaml"), DestinationRule.class);
+
+        System.out.println(destinationRule);
+
+
+//        apiVersion: service.dubbo.apache.org/v1alpha1
+//        kind: DestinationRule
+//        metadata: { name: demo-route }
+//        spec:
+//        host: demo
+//        subsets:
+//        - labels: { env-sign: xxx,tag1: hello }
+//        name: isolation
+//                - labels: { env-sign: yyy }
+//        name: testing-trunk
+//                - labels: { env-sign: zzz }
+//        name: testing
+
+
+        assertEquals("service.dubbo.apache.org/v1alpha1", destinationRule.getApiVersion());
+        assertEquals("DestinationRule", destinationRule.getKind());
+        assertEquals("demo-route", destinationRule.getMetadata().get("name"));
+        assertEquals("demo", destinationRule.getSpec().getHost());
+        assertEquals(3, destinationRule.getSpec().getSubsets().size());
+
+        assertEquals("isolation", destinationRule.getSpec().getSubsets().get(0).getName());
+        assertEquals(2, destinationRule.getSpec().getSubsets().get(0).getLabels().size());
+        assertEquals("xxx", destinationRule.getSpec().getSubsets().get(0).getLabels().get("env-sign"));
+        assertEquals("hello", destinationRule.getSpec().getSubsets().get(0).getLabels().get("tag1"));
+
+
+        assertEquals("testing-trunk", destinationRule.getSpec().getSubsets().get(1).getName());
+        assertEquals(1, destinationRule.getSpec().getSubsets().get(1).getLabels().size());
+        assertEquals("yyy", destinationRule.getSpec().getSubsets().get(1).getLabels().get("env-sign"));
+
+
+        assertEquals("testing", destinationRule.getSpec().getSubsets().get(2).getName());
+        assertEquals(1, destinationRule.getSpec().getSubsets().get(2).getLabels().size());
+        assertEquals("zzz", destinationRule.getSpec().getSubsets().get(2).getLabels().get("env-sign"));
+
+        assertEquals(SimpleLB.ROUND_ROBIN, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getSimple());
+        assertEquals(null, destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getConsistentHash());
+    }
+
+
+    @Test
+    public void parserMultiRuleTest() {
+        Yaml yaml = new Yaml();
+        Yaml yaml2 = new Yaml();
+        Iterable objectIterable = yaml.loadAll(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest2.yaml"));
+        for (Object result : objectIterable) {
+
+            Map resultMap = (Map) result;
+            if (resultMap.get("kind").equals("DestinationRule")) {
+                DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+                System.out.println(destinationRule);
+                assertNotNull(destinationRule);
+            } else if (resultMap.get("kind").equals("VirtualService")) {
+                VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+                System.out.println(virtualServiceRule);
+                assertNotNull(virtualServiceRule);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
similarity index 55%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
copy to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
index ff77800..ddfb127 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
 
-import java.util.List;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
 
-@SPI
-public interface AddressListener {
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+
+public class VirtualServiceRuleTest {
+
+    @Test
+    public void parserTest() {
+        Yaml yaml = new Yaml();
+        VirtualServiceRule virtualServiceRule = yaml.loadAs(this.getClass().getClassLoader().getResourceAsStream("VirtualServiceTest.yaml"), VirtualServiceRule.class);
+
+        System.out.println(virtualServiceRule);
+        assertNotNull(virtualServiceRule);
+    }
 
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
new file mode 100644
index 0000000..9bb55b4
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class DubboMatchRequestTest {
+
+    @Test
+    public void isMatch() {
+        DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+
+        // methodMatch
+        {
+            DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+            StringMatch nameStringMatch = new StringMatch();
+            nameStringMatch.setExact("sayHello");
+            dubboMethodMatch.setName_match(nameStringMatch);
+
+            dubboMatchRequest.setMethod(dubboMethodMatch);
+        }
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, new HashMap(), new HashMap(), new HashMap(), new HashMap()));
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHi", new String[]{}, new Object[]{}, new HashMap(), new HashMap(), new HashMap(), new HashMap()));
+        // sourceLabels
+        {
+            Map<String, String> sourceLabels = new HashMap<>();
+            sourceLabels.put("key1", "value1");
+            sourceLabels.put("key2", "value2");
+
+            dubboMatchRequest.setSourceLabels(sourceLabels);
+        }
+
+        Map<String, String> inputSourceLablesMap = new HashMap<>();
+        inputSourceLablesMap.put("key1", "value1");
+        inputSourceLablesMap.put("key2", "value2");
+        inputSourceLablesMap.put("key3", "value3");
+
+        Map<String, String> inputSourceLablesMap2 = new HashMap<>();
+        inputSourceLablesMap2.put("key1", "other");
+        inputSourceLablesMap2.put("key2", "value2");
+        inputSourceLablesMap2.put("key3", "value3");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, new HashMap(), new HashMap(), new HashMap()));
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap2, new HashMap(), new HashMap(), new HashMap()));
+
+
+        // eagleeyeContext
+        {
+            DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+            {
+                Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+                StringMatch nameMatch = new StringMatch();
+                nameMatch.setExact("qinliujie");
+                eagleeyecontextMatchMap.put("name", nameMatch);
+                dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+            }
+
+            dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+        }
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, new HashMap(), new HashMap()));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("name", "jack");
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap2, new HashMap(), new HashMap()));
+
+
+        //dubbo context
+
+        {
+            DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+            {
+                Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+                StringMatch nameMatch = new StringMatch();
+                nameMatch.setExact("qinliujie");
+                eagleeyecontextMatchMap.put("name", nameMatch);
+                dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+            }
+
+
+            {
+                Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+                StringMatch dpathMatch = new StringMatch();
+                dpathMatch.setExact("PRE");
+                dubboContextMatchMap.put("dpath", dpathMatch);
+                dubboAttachmentMatch.setDubbocontext(dubboContextMatchMap);
+            }
+
+            dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+        }
+
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+        assertTrue(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, invokeDubboContextMap, new HashMap()));
+
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "other");
+
+        assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, invokeDubboContextMap2, new HashMap()));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
similarity index 55%
rename from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
rename to dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
index ff77800..72d4774 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
@@ -14,24 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.rpc.cluster.Directory;
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
 
-import java.util.List;
 
-@SPI
-public interface AddressListener {
+import org.junit.jupiter.api.Test;
 
-    /**
-     * processing when receiving the address list
-     *
-     * @param addresses            provider address list
-     * @param consumerUrl
-     * @param registryDirectory
-     */
-    List<URL> notify(List<URL> addresses, URL consumerUrl, Directory registryDirectory);
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+public class BoolMatchTest {
+
+    @Test
+    public void isMatch() {
+        BoolMatch boolMatch =  new BoolMatch();
+        boolMatch.setExact(true);
+
+
+        assertTrue(BoolMatch.isMatch(boolMatch,true));
+        assertFalse(BoolMatch.isMatch(boolMatch,false));
+
+        boolMatch.setExact(false);
+        assertFalse(BoolMatch.isMatch(boolMatch,true));
+        assertTrue(BoolMatch.isMatch(boolMatch,false));
+
+    }
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
new file mode 100644
index 0000000..aba44bc
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DoubleMatchTest {
+
+    @Test
+    public void exactMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+        doubleMatch.setExact(10.0);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+    @Test
+    public void rangeStartMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+
+    @Test
+    public void rangeEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.0));
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 9.0));
+    }
+
+
+    @Test
+    public void rangeStartEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(5.0);
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 5.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.0));
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 4.9));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 10.1));
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 6.0));
+
+    }
+
+    @Test
+    public void modMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        doubleMatch.setMod(2.0);
+        doubleMatch.setExact(3.0);
+
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 3.0));
+
+        doubleMatch.setExact(1.0);
+
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 1.0));
+        assertFalse(DoubleMatch.isMatch(doubleMatch, 2.0));
+        assertTrue(DoubleMatch.isMatch(doubleMatch, 3.0));
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
new file mode 100644
index 0000000..99e704f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class DubboAttachmentMatchTest {
+
+    @Test
+    public void dubboContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> dubbocontextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        dubbocontextMatchMap.put("name", nameMatch);
+
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        dubbocontextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setDubbocontext(dubbocontextMatchMap);
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("name", "qinliujie");
+        invokeDubboContextMap.put("machineGroup", "test_host");
+        invokeDubboContextMap.put("other", "other");
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap));
+
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap2.put("name", "jack");
+        invokeDubboContextMap2.put("machineGroup", "test_host");
+        invokeDubboContextMap2.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap2));
+
+
+        Map<String, String> invokeDubboContextMap3 = new HashMap<>();
+        invokeDubboContextMap3.put("name", "qinliujie");
+        invokeDubboContextMap3.put("machineGroup", "my_host");
+        invokeDubboContextMap3.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, new HashMap<>(), invokeDubboContextMap3));
+    }
+
+
+    @Test
+    public void eagleEyeContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        eagleeyecontextMatchMap.put("name", nameMatch);
+
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        eagleeyecontextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, new HashMap<>()));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("name", "jack");
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, new HashMap<>()));
+
+
+        Map<String, String> invokeEagleEyeContextMap3 = new HashMap<>();
+        invokeEagleEyeContextMap3.put("name", "qinliujie");
+        invokeEagleEyeContextMap3.put("machineGroup", "my_host");
+        invokeEagleEyeContextMap3.put("other", "other");
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap3, new HashMap<>()));
+    }
+
+
+    @Test
+    public void contextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        {
+            Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+            StringMatch nameMatch = new StringMatch();
+            nameMatch.setExact("qinliujie");
+            eagleeyecontextMatchMap.put("name", nameMatch);
+            dubboAttachmentMatch.setEagleeyecontext(eagleeyecontextMatchMap);
+        }
+
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        //-------
+
+        {
+            Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+            StringMatch dpathMatch = new StringMatch();
+            dpathMatch.setExact("PRE");
+            dubboContextMatchMap.put("dpath", dpathMatch);
+            dubboAttachmentMatch.setDubbocontext(dubboContextMatchMap);
+        }
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+
+        assertTrue(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap));
+
+
+        Map<String, String> invokeEagleEyeContextMap1 = new HashMap<>();
+        invokeEagleEyeContextMap1.put("name", "jack");
+        invokeEagleEyeContextMap1.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap1.put("other", "other");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap1, invokeDubboContextMap));
+
+
+        Map<String, String> invokeDubboContextMap1 = new HashMap<>();
+        invokeDubboContextMap1.put("dpath", "PRE-2");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap1));
+
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap1, invokeDubboContextMap1));
+
+
+        Map<String, String> invokeEagleEyeContextMap2 = new HashMap<>();
+        invokeEagleEyeContextMap2.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap2.put("other", "other");
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, invokeDubboContextMap));
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap, invokeDubboContextMap2));
+
+
+        assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, invokeDubboContextMap2));
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
new file mode 100644
index 0000000..7c7189b
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DubboMethodMatchTest {
+
+    @Test
+    public void nameMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        StringMatch nameStringMatch = new StringMatch();
+        nameStringMatch.setExact("sayHello");
+
+        dubboMethodMatch.setName_match(nameStringMatch);
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+    }
+
+
+    @Test
+    public void argcMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+        dubboMethodMatch.setArgc(1);
+
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, null));
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{"1"}));
+    }
+
+    @Test
+    public void argpMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<StringMatch> argpMatch = new ArrayList<>();
+
+        StringMatch first = new StringMatch();
+        first.setExact("java.lang.Long");
+
+        StringMatch second = new StringMatch();
+        second.setRegex(".*");
+
+        argpMatch.add(first);
+        argpMatch.add(second);
+
+        dubboMethodMatch.setArgp(argpMatch);
+
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{"java.lang.Long", "java.lang.String"}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{"java.lang.Long", "java.lang.String", "java.lang.String"}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", new String[]{}, new Object[]{}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "sayHello", null, new Object[]{}));
+    }
+
+    @Test
+    public void parametersMatch() {
+
+
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<DubboMethodArg> parametersMatch = new ArrayList<>();
+
+        //----- index 0
+        {
+            DubboMethodArg dubboMethodArg0 = new DubboMethodArg();
+            dubboMethodArg0.setIndex(0);
+
+            ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+            List<DoubleMatch> oneof = new ArrayList<>();
+
+            DoubleMatch doubleMatch1 = new DoubleMatch();
+            doubleMatch1.setExact(10.0);
+
+            oneof.add(doubleMatch1);
+
+            listDoubleMatch.setOneof(oneof);
+
+            dubboMethodArg0.setNum_value(listDoubleMatch);
+
+            parametersMatch.add(dubboMethodArg0);
+        }
+
+        //-----index 1
+
+        {
+
+            DubboMethodArg dubboMethodArg1 = new DubboMethodArg();
+            dubboMethodArg1.setIndex(1);
+
+            ListStringMatch listStringMatch = new ListStringMatch();
+
+            List<StringMatch> oneof = new ArrayList<>();
+
+            StringMatch stringMatch1 = new StringMatch();
+            stringMatch1.setExact("sayHello");
+
+            oneof.add(stringMatch1);
+
+            listStringMatch.setOneof(oneof);
+
+            dubboMethodArg1.setStr_value(listStringMatch);
+
+            parametersMatch.add(dubboMethodArg1);
+        }
+
+        dubboMethodMatch.setArgs(parametersMatch);
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String"}, new Object[]{10, "sayHello"}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String"}, new Object[]{10, "sayHi"}));
+
+
+        //-----index 2
+
+        {
+
+            DubboMethodArg dubboMethodArg2 = new DubboMethodArg();
+            dubboMethodArg2.setIndex(2);
+
+            BoolMatch boolMatch = new BoolMatch();
+            boolMatch.setExact(true);
+
+
+            dubboMethodArg2.setBool_value(boolMatch);
+
+            parametersMatch.add(dubboMethodArg2);
+        }
+
+
+        assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", true}));
+        assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", false}));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
new file mode 100644
index 0000000..f3b30b1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+public class ListDoubleMatchTest {
+
+    @Test
+    public void isMatch() {
+        ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+        List<DoubleMatch> oneof = new ArrayList<>();
+
+        DoubleMatch doubleMatch1 = new DoubleMatch();
+        doubleMatch1.setExact(10.0);
+
+        DoubleMatch doubleMatch2 = new DoubleMatch();
+        doubleMatch2.setExact(11.0);
+
+        oneof.add(doubleMatch1);
+        oneof.add(doubleMatch2);
+
+        listDoubleMatch.setOneof(oneof);
+
+        assertTrue(ListDoubleMatch.isMatch(listDoubleMatch, 10.0));
+        assertTrue(ListDoubleMatch.isMatch(listDoubleMatch, 11.0));
+        assertFalse(ListDoubleMatch.isMatch(listDoubleMatch, 12.0));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
new file mode 100644
index 0000000..0561053
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ListStringMatchTest {
+
+    @Test
+    public void isMatch() {
+        ListStringMatch listStringMatch = new ListStringMatch();
+
+        List<StringMatch> oneof = new ArrayList<>();
+
+        StringMatch stringMatch1 = new StringMatch();
+        stringMatch1.setExact("1");
+
+        StringMatch stringMatch2 = new StringMatch();
+        stringMatch2.setExact("2");
+
+        oneof.add(stringMatch1);
+        oneof.add(stringMatch2);
+
+
+        listStringMatch.setOneof(oneof);
+
+        assertTrue(ListStringMatch.isMatch(listStringMatch, "1"));
+        assertTrue(ListStringMatch.isMatch(listStringMatch, "2"));
+        assertFalse(ListStringMatch.isMatch(listStringMatch, "3"));
+
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
new file mode 100644
index 0000000..13d0225
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class StringMatchTest {
+
+    @Test
+    public void exactMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setExact("qinliujie");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "qinliujie"));
+        assertFalse(StringMatch.isMatch(stringMatch, "other"));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+
+
+    @Test
+    public void prefixMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setPrefix("org.apache.dubbo.rpc.cluster.router.mesh");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+
+
+    @Test
+    public void regxMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setRegex("org.apache.dubbo.rpc.cluster.router.mesh.*");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh"));
+        assertTrue(StringMatch.isMatch(stringMatch, "org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, "com.taobao"));
+    }
+
+
+    @Test
+    public void emptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setEmpty("empty");
+
+        assertFalse(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertTrue(StringMatch.isMatch(stringMatch, ""));
+        assertTrue(StringMatch.isMatch(stringMatch, null));
+    }
+
+    @Test
+    public void noEmptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setNoempty("noempty");
+
+        assertTrue(StringMatch.isMatch(stringMatch, "com.alibaba.hsf"));
+        assertFalse(StringMatch.isMatch(stringMatch, ""));
+        assertFalse(StringMatch.isMatch(stringMatch, null));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java
new file mode 100644
index 0000000..5193c8d
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/VsDestinationGroupRuleDispatcherTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+class VsDestinationGroupRuleDispatcherTest {
+
+    @Test
+    public void post() {
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+
+        vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener);
+
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        verify(vsDestinationGroupRuleListener, times(2)).onRuleChange(anyObject());
+
+    }
+
+    @Test
+    public void register() {
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+
+        assertFalse(vsDestinationGroupRuleDispatcher.register(null));
+        assertTrue(vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener));
+        assertFalse(vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener));
+    }
+
+    @Test
+    public void unregister() {
+
+        VsDestinationGroupRuleDispatcher vsDestinationGroupRuleDispatcher = new VsDestinationGroupRuleDispatcher();
+
+        VsDestinationGroupRuleListener vsDestinationGroupRuleListener = mock(VsDestinationGroupRuleListener.class);
+        vsDestinationGroupRuleDispatcher.register(vsDestinationGroupRuleListener);
+
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        vsDestinationGroupRuleDispatcher.unregister(vsDestinationGroupRuleListener);
+        vsDestinationGroupRuleDispatcher.post(new VsDestinationGroup());
+
+        verify(vsDestinationGroupRuleListener, times(1)).onRuleChange(anyObject());
+    }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml b/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml
new file mode 100644
index 0000000..8f2e135
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/DestinationRuleTest.yaml
@@ -0,0 +1,33 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
diff --git a/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml b/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml
new file mode 100644
index 0000000..178c50e
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/DestinationRuleTest2.yaml
@@ -0,0 +1,58 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
+
+---
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]
diff --git a/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml b/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml
new file mode 100644
index 0000000..4d3454b
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/VirtualServiceTest.yaml
@@ -0,0 +1,41 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]