[SCB-2645] Fix route rule will throw NPE when version is empty (#3228)

diff --git a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
index 500812b..d3f8f76 100644
--- a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
+++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
@@ -116,6 +116,9 @@
         TagItem targetTag = null;
         int maxMatch = 0;
         for (RouteItem entry : invokeRule.getRoute()) {
+          if (entry.getTagitem() == null){
+            continue;
+          }
           int nowMatch = entry.getTagitem().matchNum(tagItem);
           if (nowMatch > maxMatch) {
             maxMatch = nowMatch;
diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
index e48d4ee..f2017a9 100644
--- a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
+++ b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
@@ -36,7 +36,7 @@
 
 
   public void initTagItem() {
-    if (tags != null && tags.containsKey("version")) {
+    if (tags != null) {
       tagitem = new TagItem(tags);
     }
   }
diff --git a/governance/src/test/java/org/apache/servicecomb/router/distribute/DistributeTest.java b/governance/src/test/java/org/apache/servicecomb/router/distribute/DistributeTest.java
new file mode 100644
index 0000000..e26e4f0
--- /dev/null
+++ b/governance/src/test/java/org/apache/servicecomb/router/distribute/DistributeTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.servicecomb.router.distribute;
+
+import org.apache.servicecomb.router.RouterFilter;
+import org.apache.servicecomb.router.ServiceIns;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class)
+public class DistributeTest {
+  private static final String TARGET_SERVICE_NAME = "test_server1";
+
+  private RouterFilter routerFilter;
+
+  private RouterDistributor<ServiceIns, ServiceIns> routerDistributor;
+
+  @Autowired
+  public void setRouterFilter(RouterFilter routerFilter) {
+    this.routerFilter = routerFilter;
+  }
+
+  @Autowired
+  public void setRouterDistributor(RouterDistributor<ServiceIns, ServiceIns> routerDistributor) {
+    this.routerDistributor = routerDistributor;
+  }
+
+  @Test
+  public void testDistribute() {
+    List<ServiceIns> list = initServiceList();
+    HashMap<String, String> header = new HashMap<>();
+    List<ServiceIns> listOfServers = routerFilter
+        .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor);
+    Assertions.assertNotNull(listOfServers);
+    for (ServiceIns server : listOfServers) {
+      Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName());
+    }
+    int serverNum1 = 0;
+    int serverNum2 = 0;
+
+    for (int i = 0; i < 10; i++) {
+      List<ServiceIns> serverList = routerFilter
+          .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor);
+      for (ServiceIns serviceIns : serverList) {
+        if ("01".equals(serviceIns.getId())) {
+          serverNum1++;
+        } else if ("02".equals(serviceIns.getId())) {
+          serverNum2++;
+        }
+      }
+    }
+    boolean flag = false;
+    if (Math.round(serverNum2 * 1.0 / serverNum1) == 4) {
+      flag = true;
+    }
+    Assertions.assertTrue(flag);
+  }
+
+  List<ServiceIns> initServiceList() {
+    ServiceIns serviceIns1 = new ServiceIns("01", "test_server1");
+    ServiceIns serviceIns2 = new ServiceIns("02", "test_server1");
+    serviceIns1.setVersion("1.0");
+    serviceIns2.setVersion("2.0");
+    serviceIns1.addTags("x-group", "red");
+    serviceIns2.addTags("x-group", "green");
+    List<ServiceIns> list = new ArrayList<>();
+    list.add(serviceIns1);
+    list.add(serviceIns2);
+    return list;
+  }
+}
diff --git a/governance/src/test/resources/application.yaml b/governance/src/test/resources/application.yaml
index d4cf757..e5c0842 100644
--- a/governance/src/test/resources/application.yaml
+++ b/governance/src/test/resources/application.yaml
@@ -144,3 +144,31 @@
     wrongIngored: |
       delayTime: -1
       type: ERROR
+
+  routeRule:
+    test_server1: |                              # 服务名
+      - precedence: 1                        # 优先级,数字越大,优先级越高。
+        match:                               # 请求匹配规则。0..N个,不配置表示匹配。
+          headers:                           # header 匹配
+            region:                          # 如果配置了多个 header,那么所有的 header 规则都必须和请求匹配
+              exact: 'providerRegion'
+              caseInsensitive: false         # 不区分大小写
+            type:
+              regex: gray_[a-z]+             # java 正则表达式匹配
+              caseInsensitive: true          # 区分大小写
+        route:                               # 路由规则
+          - weight: 20                       # 权重值
+            tags:
+              version: 1.0.0                 # 实例标记。满足标记条件的实例放到这一组。
+          - weight: 80                       # 权重值
+            tags:
+              version: 2.0.0                 # 实例标记。满足标记条件的实例放到这一组。
+
+      - precedence: 2
+        route:
+          - weight: 20
+            tags:
+              x-group: red
+          - weight: 80
+            tags:
+              x-group: green