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

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git

commit 62607c2b604a53db8d5ce0ee79e2d8be3277a6bf
Author: GuoYL <[email protected]>
AuthorDate: Mon Nov 18 15:46:12 2019 +0800

    [SCB-1407] Add gray release feature
---
 handlers/handler-router/pom.xml                    |  58 +++++++
 .../apache/servicecomb/router/RouterFilter.java    |  81 ++++++++++
 .../servicecomb/router/cache/RouterRuleCache.java  | 121 +++++++++++++++
 .../router/constom/CanaryInvokeFilter.java         |  95 ++++++++++++
 .../router/constom/CanaryServerListFilter.java     |  50 ++++++
 .../router/constom/MicroserviceCache.java          |  27 ++++
 .../constom/ServiceCombCanaryDistributer.java      |  18 +++
 .../distribute/AbstractRouterDistributor.java      | 170 +++++++++++++++++++++
 .../router/distribute/RouterDistributor.java       |  35 +++++
 .../exception/RouterIllegalParamException.java     |  16 ++
 .../router/match/RouterHeaderFilterExt.java        |  28 ++++
 .../router/match/RouterRuleMatcher.java            |  60 ++++++++
 .../servicecomb/router/model/HeaderRule.java       | 100 ++++++++++++
 .../apache/servicecomb/router/model/Matcher.java   | 100 ++++++++++++
 .../servicecomb/router/model/PolicyRuleItem.java   | 138 +++++++++++++++++
 .../apache/servicecomb/router/model/RouteItem.java | 111 ++++++++++++++
 .../servicecomb/router/model/ServiceInfoCache.java |  79 ++++++++++
 .../apache/servicecomb/router/model/TagItem.java   | 132 ++++++++++++++++
 .../router/util/VersionCompareUtil.java            |  38 +++++
 handlers/pom.xml                                   |   1 +
 20 files changed, 1458 insertions(+)

diff --git a/handlers/handler-router/pom.xml b/handlers/handler-router/pom.xml
new file mode 100644
index 0000000..51fdc26
--- /dev/null
+++ b/handlers/handler-router/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd";>
+  <parent>
+    <artifactId>handlers</artifactId>
+    <groupId>org.apache.servicecomb</groupId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>handler-router</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+<!--      <scope>test</scope>-->
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+<!--      <scope>test</scope>-->
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.ribbon</groupId>
+      <artifactId>ribbon-loadbalancer</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.hystrix</groupId>
+      <artifactId>hystrix-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-beans</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>common-rest</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>handler-loadbalance</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/RouterFilter.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/RouterFilter.java
new file mode 100644
index 0000000..9cbd49f
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/RouterFilter.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.servicecomb.router;
+
+import com.netflix.loadbalancer.Server;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.servicecomb.router.cache.RouterRuleCache;
+import org.apache.servicecomb.router.distribute.RouterDistributor;
+import org.apache.servicecomb.router.match.RouterRuleMatcher;
+import org.apache.servicecomb.router.model.PolicyRuleItem;
+import org.springframework.util.StringUtils;
+import org.springframework.util.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/16
+ **/
+public class RouterFilter {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(RouterFilter.class);
+
+  public static <T extends Server, E> List<T> getFilteredListOfServers(List<T> 
list,
+      String targetServiceName, Map<String, String> headers, 
RouterDistributor<T, E> distributer) {
+    if (CollectionUtils.isEmpty(list)) {
+      return list;
+    }
+    if (StringUtils.isEmpty(targetServiceName)) {
+      return list;
+    }
+    if (headers == null) {
+      headers = new HashMap<>();
+    }
+    LOGGER.debug("route management headers:{}", headers);
+    /**
+     * 1.初始化--进行cache缓存
+     */
+    if (!RouterRuleCache.doInit(targetServiceName)) {
+      LOGGER.debug("route management init failed");
+      return list;
+    }
+    /**
+     * 2.match--拿到invoke相关信息 (header),匹配到唯一的rule
+     */
+    PolicyRuleItem invokeRule = 
RouterRuleMatcher.getInstance().match(targetServiceName, headers);
+
+    if (invokeRule == null) {
+      LOGGER.debug("route management match rule failed");
+      return list;
+    }
+
+    LOGGER.debug("route management match rule success: {}", invokeRule);
+
+    /**
+     * 3.distribute--拿到server list选择endpoint进行流量分配
+     */
+    List<T> resultList = distributer.distribute(targetServiceName, list, 
invokeRule);
+
+    LOGGER.debug("route management distribute rule success: {}", resultList);
+
+    return resultList;
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java
new file mode 100644
index 0000000..ec438a5
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java
@@ -0,0 +1,121 @@
+/*
+ * 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.cache;
+
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.servicecomb.router.model.PolicyRuleItem;
+import org.apache.servicecomb.router.model.ServiceInfoCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class RouterRuleCache {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(RouterRuleCache.class);
+
+  private static ConcurrentHashMap<String, ServiceInfoCache> 
serviceInfoCacheMap = new ConcurrentHashMap<>();
+
+  private static final String ROUTE_RULE = "servicecomb.routeRule.%s";
+
+  private static Interner<String> servicePool = Interners.newWeakInterner();
+
+  /**
+   * 每次序列化额外缓存,配置更新时触发回调函数 返回false即初始化规则失败: 1. 规则解析错误 2. 规则为空
+   *
+   * @param targetServiceName
+   * @return
+   */
+  public static boolean doInit(String targetServiceName) {
+    if (serviceInfoCacheMap.containsKey(targetServiceName)) {
+      return true;
+    }
+    //这里使用guava包装String.intern():因为String.intern()分配在Old Generation,容易引发fullgc
+    synchronized (servicePool.intern(targetServiceName)) {
+      //Yaml not thread-safe
+      Yaml yaml = new Yaml();
+      DynamicStringProperty ruleStr = 
DynamicPropertyFactory.getInstance().getStringProperty(
+          String.format(ROUTE_RULE, targetServiceName), null, () -> {
+            refresh(targetServiceName);
+            DynamicStringProperty tepRuleStr = 
DynamicPropertyFactory.getInstance()
+                .getStringProperty(String.format(ROUTE_RULE, 
targetServiceName), null);
+            if (tepRuleStr.get() == null) {
+              return;
+            }
+            try {
+              List<PolicyRuleItem> temList = Arrays
+                  .asList(yaml.loadAs(tepRuleStr.get(), 
PolicyRuleItem[].class));
+              RouterRuleCache.addAllRule(targetServiceName, temList);
+            } catch (Exception e) {
+              LOGGER.error("route management Serialization failed {}", 
e.getMessage());
+              return;
+            }
+          });
+      if (ruleStr.get() == null) {
+        return false;
+      }
+      try {
+        addAllRule(targetServiceName,
+            Arrays.asList(yaml.loadAs(ruleStr.get(), PolicyRuleItem[].class)));
+      } catch (Exception e) {
+        LOGGER.error("route management Serialization failed: {}", 
e.getMessage());
+        return false;
+      }
+      return true;
+    }
+  }
+
+  private static void addAllRule(String targetServiceName,
+      List<PolicyRuleItem> policyRuleItemList) {
+    if (CollectionUtils.isEmpty(policyRuleItemList)) {
+      return;
+    }
+    if (serviceInfoCacheMap.get(targetServiceName) == null) {
+      serviceInfoCacheMap.put(targetServiceName, new ServiceInfoCache());
+    }
+    serviceInfoCacheMap.get(targetServiceName).setAllrule(policyRuleItemList);
+    // 这里初始化tagitem
+    serviceInfoCacheMap.get(targetServiceName).getAllrule().forEach(a ->
+        a.getRoute().forEach(b -> b.initTagItem())
+    );
+    // 按照优先级排序
+    serviceInfoCacheMap.get(targetServiceName).sortRule();
+  }
+
+  public static ConcurrentHashMap<String, ServiceInfoCache> 
getServiceInfoCacheMap() {
+    return serviceInfoCacheMap;
+  }
+
+  public static void refresh() {
+    serviceInfoCacheMap = new ConcurrentHashMap<>();
+    servicePool = Interners.newWeakInterner();
+  }
+
+  public static void refresh(String targetServiceName) {
+    serviceInfoCacheMap.remove(targetServiceName);
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryInvokeFilter.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryInvokeFilter.java
new file mode 100644
index 0000000..b41b216
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryInvokeFilter.java
@@ -0,0 +1,95 @@
+package org.apache.servicecomb.router.constom;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.servicecomb.common.rest.filter.HttpServerFilter;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+import org.yaml.snakeyaml.Yaml;
+
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.vertx.core.json.Json;
+
+public class CanaryInvokeFilter implements HttpServerFilter {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(CanaryInvokeFilter.class);
+
+  private static final String PASS_HEADER = "servicecomb.passheader";
+
+  @Override
+  public int getOrder() {
+    return -90;
+  }
+
+  @Override
+  public boolean enabled() {
+    return false;
+  }
+
+  @Override
+  public boolean needCacheRequest(OperationMeta operationMeta) {
+    return false;
+  }
+
+  /**
+   * 透传Header需要在这里实现, 因为无法预知调用链上的服务匹配所需要header, 提供两种模式 
1.取到全量的header并放到context中, 2.从配置中解析并读取
+   *
+   * @param invocation
+   * @param httpServletRequestEx
+   * @return
+   */
+  @Override
+  public Response afterReceiveRequest(Invocation invocation,
+      HttpServletRequestEx httpServletRequestEx) {
+    if (invocation.getContext("canary_context") != null) {
+      Map<String, String> headerMap = 
getHeaderMap(invocation.getMicroserviceName(),
+          httpServletRequestEx);
+      invocation.addContext("canary_context", Json.encode(headerMap));
+    }
+    return null;
+  }
+
+  /**
+   * 取出所用的header
+   *
+   * @param serviceName
+   * @param httpServletRequestEx
+   * @return
+   */
+  public Map<String, String> getHeaderMap(String serviceName,
+      HttpServletRequestEx httpServletRequestEx) {
+    Yaml yaml = new Yaml();
+    String headerStr = 
DynamicPropertyFactory.getInstance().getStringProperty(PASS_HEADER, null)
+        .get();
+    Map<String, String> headerKeyMap = yaml.load(headerStr);
+    Set<String> headerKeySet = headerKeyMap.keySet();
+    Map<String, String> headerMap = new HashMap<>();
+    headerKeySet.forEach(headerKey -> {
+      String val = httpServletRequestEx.getHeader(headerKey);
+      if (!StringUtils.isEmpty(val)) {
+        headerMap.put(headerKey, httpServletRequestEx.getHeader(headerKey));
+      }
+    });
+    return headerMap;
+  }
+
+  @Override
+  public CompletableFuture<Void> beforeSendResponseAsync(Invocation invocation,
+      HttpServletResponseEx responseEx) {
+    return null;
+  }
+
+  @Override
+  public void beforeSendResponse(Invocation invocation, HttpServletResponseEx 
responseEx) {
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryServerListFilter.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryServerListFilter.java
new file mode 100644
index 0000000..dc9211a
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/CanaryServerListFilter.java
@@ -0,0 +1,50 @@
+package org.apache.servicecomb.router.constom;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.loadbalance.ServerListFilterExt;
+import org.apache.servicecomb.loadbalance.ServiceCombServer;
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.vertx.core.json.Json;
+import org.apache.servicecomb.router.RouterFilter;
+import org.apache.servicecomb.router.distribute.RouterDistributor;
+
+public class CanaryServerListFilter implements ServerListFilterExt {
+
+  private static final String ENABLE = "servicecomb.release_way";
+
+  RouterDistributor distributer = new ServiceCombCanaryDistributer();
+
+  @Override
+  public boolean enabled() {
+    return DynamicPropertyFactory.getInstance().getStringProperty(ENABLE, 
"").get()
+        .equals("canary");
+  }
+
+  @Override
+  public List<ServiceCombServer> 
getFilteredListOfServers(List<ServiceCombServer> list,
+      Invocation invocation) {
+    String targetServiceName = invocation.getMicroserviceName();
+    Map<String, String> headers = new HashMap<>();
+    if (invocation.getContext("canary_context") != null) {
+      Map<String, String> canaryContext = Json
+          .decodeValue(invocation.getContext("canary_context"), Map.class);
+      headers.putAll(canaryContext);
+    }
+    for (int i = 0; i < invocation.getArgs().length; i++) {
+      if (invocation.getOperationMeta().getParamName(i) != null &&
+          invocation.getArgs()[i] != null) {
+        headers
+            .put(invocation.getOperationMeta().getParamName(i), 
invocation.getArgs()[i].toString());
+      }
+    }
+    headers.putAll(invocation.getContext());
+    return RouterFilter
+        .getFilteredListOfServers(list, targetServiceName, headers,
+            distributer);
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/MicroserviceCache.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/MicroserviceCache.java
new file mode 100644
index 0000000..a825d35
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/MicroserviceCache.java
@@ -0,0 +1,27 @@
+package org.apache.servicecomb.router.constom;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.api.registry.Microservice;
+
+public final class MicroserviceCache {
+
+  private static MicroserviceCache instance = new MicroserviceCache();
+  private Map<String, Microservice> services = new HashMap<>();
+
+  private MicroserviceCache() {
+  }
+
+  public static MicroserviceCache getInstance() {
+    return instance;
+  }
+
+  public Microservice getService(String serviceId) {
+    Microservice micorservice = services.computeIfAbsent(serviceId, (k) -> {
+      return RegistryUtils.getMicroservice(serviceId);
+    });
+    return micorservice;
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/ServiceCombCanaryDistributer.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/ServiceCombCanaryDistributer.java
new file mode 100644
index 0000000..aaabc93
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/constom/ServiceCombCanaryDistributer.java
@@ -0,0 +1,18 @@
+package org.apache.servicecomb.router.constom;
+
+import org.apache.servicecomb.loadbalance.ServiceCombServer;
+import org.apache.servicecomb.router.distribute.AbstractRouterDistributor;
+import org.apache.servicecomb.serviceregistry.api.registry.Microservice;
+
+public class ServiceCombCanaryDistributer extends
+    AbstractRouterDistributor<ServiceCombServer, Microservice> {
+
+    public ServiceCombCanaryDistributer() {
+        init(server -> MicroserviceCache.getInstance()
+                        .getService(server.getInstance().getServiceId()),
+                Microservice::getVersion,
+                Microservice::getServiceName,
+                Microservice::getProperties);
+    }
+
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
new file mode 100644
index 0000000..83e1cc9
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java
@@ -0,0 +1,170 @@
+/*
+ * 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 com.netflix.loadbalancer.Server;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.servicecomb.router.cache.RouterRuleCache;
+import org.apache.servicecomb.router.model.PolicyRuleItem;
+import org.apache.servicecomb.router.model.RouteItem;
+import org.apache.servicecomb.router.model.TagItem;
+import org.apache.servicecomb.router.util.VersionCompareUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public abstract class AbstractRouterDistributor<T extends Server, E> implements
+    RouterDistributor<T, E> {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(AbstractRouterDistributor.class);
+
+  private Function<T, E> getIns;
+
+  private Function<E, String> getVersion;
+
+  private Function<E, String> getServerName;
+
+  private Function<E, Map<String, String>> getProperties;
+
+  @Override
+  public List<T> distribute(String targetServiceName, List<T> list, 
PolicyRuleItem invokeRule) {
+    //初始化LatestVersion
+    initLatestVersion(targetServiceName, list);
+
+    invokeRule.check(
+        
RouterRuleCache.getServiceInfoCacheMap().get(targetServiceName).getLatestVersionTag());
+
+    // 建立tag list
+    Map<TagItem, List<T>> versionServerMap = 
getDistributList(targetServiceName, list, invokeRule);
+
+    //如果没有匹配到合适的规则,直接返回最新版本的服务列表
+    if (CollectionUtils.isEmpty(versionServerMap)) {
+      LOGGER.debug("route management can not match any rule and route the 
latest version");
+      return getLatestVersionList(list, targetServiceName);
+    }
+
+    // 分配流量,返回结果
+    TagItem targetTag = getFiltedServerTagItem(invokeRule, targetServiceName);
+    if (versionServerMap.containsKey(targetTag)) {
+      return versionServerMap.get(targetTag);
+    }
+    return getLatestVersionList(list, targetServiceName);
+  }
+
+  @Override
+  public void init(Function<T, E> getIns,
+      Function<E, String> getVersion,
+      Function<E, String> getServerName,
+      Function<E, Map<String, String>> getProperties) {
+    this.getIns = getIns;
+    this.getVersion = getVersion;
+    this.getServerName = getServerName;
+    this.getProperties = getProperties;
+  }
+
+  public TagItem getFiltedServerTagItem(PolicyRuleItem rule, String 
targetServiceName) {
+    return RouterRuleCache.getServiceInfoCacheMap().get(targetServiceName)
+        .getNextInvokeVersion(rule);
+  }
+
+  /**
+   * 1.过滤targetService 2.返回按照version和tags分配list 
这里之所以需要建立Map,而不是直接遍历List来分配是因为需要考虑 “多重匹配”
+   * 因为getProperties中除了tag还有其他的无关字段
+   *
+   * @param serviceName
+   * @param list
+   * @return
+   */
+  private Map<TagItem, List<T>> getDistributList(String serviceName,
+      List<T> list,
+      PolicyRuleItem invokeRule) {
+    String latestV = 
RouterRuleCache.getServiceInfoCacheMap().get(serviceName).getLatestVersionTag()
+        .getVersion();
+    Map<TagItem, List<T>> versionServerMap = new HashMap<>();
+    for (T server : list) {
+      //获得目标服务
+      E ms = getIns.apply(server);
+      if (getServerName.apply(ms).equals(serviceName)) {
+        //最多匹配原则
+        TagItem tagItem = new TagItem(getVersion.apply(ms), 
getProperties.apply(ms));
+        TagItem targetTag = null;
+        int maxMatch = 0;
+        for (RouteItem entry : invokeRule.getRoute()) {
+          int nowMatch = entry.getTagitem().matchNum(tagItem);
+          if (nowMatch > maxMatch) {
+            maxMatch = nowMatch;
+            targetTag = entry.getTagitem();
+          }
+        }
+        synchronized (invokeRule) {
+          if (invokeRule.isWeightLess() && 
getVersion.apply(ms).equals(latestV)) {
+            TagItem latestVTag = 
invokeRule.getRoute().get(invokeRule.getRoute().size() - 1)
+                .getTagitem();
+            if (!versionServerMap.containsKey(latestVTag)) {
+              versionServerMap.put(latestVTag, new ArrayList<>());
+            }
+            versionServerMap.get(latestVTag).add(server);
+          }
+          if (targetTag != null) {
+            if (!versionServerMap.containsKey(targetTag)) {
+              versionServerMap.put(targetTag, new ArrayList<>());
+            }
+            versionServerMap.get(targetTag).add(server);
+          }
+        }
+      }
+    }
+    return versionServerMap;
+  }
+
+
+  public void initLatestVersion(String serviceName, List<T> list) {
+    if 
(RouterRuleCache.getServiceInfoCacheMap().get(serviceName).getLatestVersionTag()
 != null) {
+      return;
+    }
+    String latestVersion = null;
+    for (T server : list) {
+      E ms = getIns.apply(server);
+      if (getServerName.apply(ms).equals(serviceName)) {
+        if (latestVersion == null || VersionCompareUtil
+            .compareVersion(latestVersion, getVersion.apply(ms)) == -1) {
+          latestVersion = getVersion.apply(ms);
+        }
+      }
+    }
+    TagItem tagitem = new TagItem(latestVersion);
+    
RouterRuleCache.getServiceInfoCacheMap().get(serviceName).setLatestVersionTag(tagitem);
+  }
+
+
+  public List<T> getLatestVersionList(List<T> list, String targetServiceName) {
+    String latestV = 
RouterRuleCache.getServiceInfoCacheMap().get(targetServiceName)
+        .getLatestVersionTag().getVersion();
+    return list.stream().filter(server ->
+        getVersion.apply(getIns.apply(server)).equals(latestV)
+    ).collect(Collectors.toList());
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java
new file mode 100644
index 0000000..fe5cf37
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java
@@ -0,0 +1,35 @@
+/*
+ * 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 java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import org.apache.servicecomb.router.model.PolicyRuleItem;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public interface RouterDistributor<T, E> {
+
+  void init(Function<T, E> getIns, Function<E, String> getVersion,
+      Function<E, String> getServerName,
+      Function<E, Map<String, String>> getProperties);
+
+  List<T> distribute(String targetServiceName, List<T> list, PolicyRuleItem 
invokeRule);
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java
new file mode 100644
index 0000000..e8046b6
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java
@@ -0,0 +1,16 @@
+package org.apache.servicecomb.router.exception;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/11/4
+ **/
+public class RouterIllegalParamException extends RuntimeException {
+
+  public RouterIllegalParamException(String message) {
+    super(message);
+  }
+
+  public RouterIllegalParamException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterHeaderFilterExt.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterHeaderFilterExt.java
new file mode 100644
index 0000000..62e5161
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterHeaderFilterExt.java
@@ -0,0 +1,28 @@
+/*
+ * 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.match;
+
+import java.util.Map;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public interface RouterHeaderFilterExt {
+
+  Map<String, String> doFilter(Map<String, String> invokeHeader);
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java
new file mode 100644
index 0000000..5133eb0
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.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.servicecomb.router.match;
+
+import java.util.Map;
+import org.apache.servicecomb.router.cache.RouterRuleCache;
+import org.apache.servicecomb.router.model.PolicyRuleItem;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class RouterRuleMatcher {
+
+  @Autowired(required = false)
+  private RouterHeaderFilterExt routerHeaderFilterExt;
+
+  private static RouterRuleMatcher instance = new RouterRuleMatcher();
+
+  private RouterRuleMatcher() {
+  }
+
+  /**
+   * 匹配到合适的rule 匹配规则即: source (目标服务名字) sourceTags (一期先不考虑) headers (匹配header字段)
+   *
+   * @param serviceName
+   * @return
+   */
+  public PolicyRuleItem match(String serviceName, Map<String, String> 
invokeHeader) {
+    if (routerHeaderFilterExt != null) {
+      invokeHeader = routerHeaderFilterExt.doFilter(invokeHeader);
+    }
+    for (PolicyRuleItem rule : 
RouterRuleCache.getServiceInfoCacheMap().get(serviceName)
+        .getAllrule()) {
+      if (rule.getMatch() == null || rule.getMatch().match(invokeHeader)) {
+        return rule;
+      }
+    }
+    return null;
+  }
+
+  public static RouterRuleMatcher getInstance() {
+    return instance;
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java
new file mode 100644
index 0000000..332b9f2
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java
@@ -0,0 +1,100 @@
+/*
+ * 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.model;
+
+import org.apache.servicecomb.router.exception.RouterIllegalParamException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class HeaderRule {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(HeaderRule.class);
+
+  //正则
+  private String regex;
+  //是否区分大小写 false区分 true不区分
+  private Boolean caseInsensitive = false;
+  //精准匹配
+  private String exact;
+
+  public HeaderRule() {
+  }
+
+  public boolean match(String str) {
+    if (str == null) {
+      return false;
+    }
+    if (exact == null && regex == null) {
+      throw new RouterIllegalParamException(
+          "route management regex and exact can not br null at same time.");
+    }
+    if (!caseInsensitive) {
+      str = str.toLowerCase();
+      exact = exact == null ? null : exact.toLowerCase();
+      regex = regex == null ? null : regex.toLowerCase();
+    }
+    if (exact != null && !str.equals(exact)) {
+      return false;
+    }
+    try {
+      if (regex != null && !str.matches(regex)) {
+        return false;
+      }
+    } catch (Exception e) {
+      LOGGER.error("route management wrong regular expression format: {}", 
regex);
+      return false;
+    }
+    return true;
+  }
+
+  public String getRegex() {
+    return regex;
+  }
+
+  public void setRegex(String regex) {
+    this.regex = regex;
+  }
+
+  public Boolean getCaseInsensitive() {
+    return caseInsensitive;
+  }
+
+  public void setCaseInsensitive(Boolean caseInsensitive) {
+    this.caseInsensitive = caseInsensitive;
+  }
+
+  public String getExact() {
+    return exact;
+  }
+
+  public void setExact(String exact) {
+    this.exact = exact;
+  }
+
+  @Override
+  public String toString() {
+    return "HeaderRule{" +
+        "regex='" + regex + '\'' +
+        ", caseInsensitive=" + caseInsensitive +
+        ", exact='" + exact + '\'' +
+        '}';
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/Matcher.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/Matcher.java
new file mode 100644
index 0000000..83f5b66
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/Matcher.java
@@ -0,0 +1,100 @@
+/*
+ * 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.model;
+
+import java.util.Map;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class Matcher {
+
+  //服务级别
+  private String source;
+  //服务级别 -- 这个暂时不考虑
+  private Map<String, String> sourceTags;
+  //invoke级别
+  private Map<String, HeaderRule> headers;
+  //这个暂时不考虑
+  private String refer;
+
+  public Matcher() {
+  }
+
+  public boolean filte(String sourcName, Map<String, String> sourceTags) {
+    if (sourcName != null && !sourcName.equals(source)) {
+      return false;
+    }
+    return true;
+  }
+
+  public boolean match(Map<String, String> realHeaders) {
+    if (headers == null) {
+      return true;
+    }
+    for (Map.Entry<String, HeaderRule> entry : headers.entrySet()) {
+      if (!realHeaders.containsKey(entry.getKey()) || !entry.getValue()
+          .match(realHeaders.get(entry.getKey()))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public String getSource() {
+    return source;
+  }
+
+  public void setSource(String source) {
+    this.source = source;
+  }
+
+  public Map<String, String> getSourceTags() {
+    return sourceTags;
+  }
+
+  public void setSourceTags(Map<String, String> sourceTags) {
+    this.sourceTags = sourceTags;
+  }
+
+  public Map<String, HeaderRule> getHeaders() {
+    return headers;
+  }
+
+  public void setHeaders(Map<String, HeaderRule> headers) {
+    this.headers = headers;
+  }
+
+  public String getRefer() {
+    return refer;
+  }
+
+  public void setRefer(String refer) {
+    this.refer = refer;
+  }
+
+  @Override
+  public String toString() {
+    return "Matcher{" +
+        "source='" + source + '\'' +
+        ", sourceTags=" + sourceTags +
+        ", headers=" + headers +
+        ", refer='" + refer + '\'' +
+        '}';
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java
new file mode 100644
index 0000000..aee0c9a
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java
@@ -0,0 +1,138 @@
+/*
+ * 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.model;
+
+import java.util.List;
+import org.apache.servicecomb.router.exception.RouterIllegalParamException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class PolicyRuleItem implements Comparable<PolicyRuleItem> {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(PolicyRuleItem.class);
+
+  private Integer precedence;
+
+  private Matcher match;
+
+  // any match 只要version符合就算符合匹配规则
+  private List<RouteItem> route;
+
+  private Integer total;
+
+  private boolean weightLess = false;
+
+  public PolicyRuleItem() {
+  }
+
+  /**
+   * 如果weight和小于100,用latestVersion补充
+   *
+   * @param latestVersionTag
+   */
+  public void check(TagItem latestVersionTag) {
+    if (CollectionUtils.isEmpty(route)) {
+      throw new RouterIllegalParamException("canary rule list can not be 
null");
+    }
+    if (route.size() == 1) {
+      route.get(0).setWeight(100);
+      return;
+    }
+    int sum = 0;
+    for (RouteItem item : route) {
+      if (item.getWeight() == null) {
+        throw new RouterIllegalParamException("canary rule weight can not be 
null");
+      }
+      sum += item.getWeight();
+    }
+    if (sum > 100) {
+      LOGGER.warn("canary rule weight sum is more than 100");
+    } else if (sum < 100) {
+      if (latestVersionTag == null) {
+        LOGGER.warn("canary has some error when set default latestVersion");
+      }
+      weightLess = true;
+      route.add(new RouteItem(100 - sum, latestVersionTag));
+    }
+    //Collections.sort(route);
+  }
+
+  @Override
+  public int compareTo(PolicyRuleItem param) {
+    if (param.precedence.equals(this.precedence)) {
+      LOGGER.warn("the same canary precedence is not recommended");
+      return 0;
+    }
+    return param.precedence > this.precedence ? 1 : -1;
+  }
+
+  public Integer getPrecedence() {
+    return precedence;
+  }
+
+  public void setPrecedence(Integer precedence) {
+    this.precedence = precedence;
+  }
+
+  public Matcher getMatch() {
+    return match;
+  }
+
+  public void setMatch(Matcher match) {
+    this.match = match;
+  }
+
+  public List<RouteItem> getRoute() {
+    return route;
+  }
+
+  public void setRoute(List<RouteItem> route) {
+    this.route = route;
+  }
+
+  public Integer getTotal() {
+    return total;
+  }
+
+  public void setTotal(Integer total) {
+    this.total = total;
+  }
+
+  public boolean isWeightLess() {
+    return weightLess;
+  }
+
+  public void setWeightLess(boolean weightLess) {
+    this.weightLess = weightLess;
+  }
+
+  @Override
+  public String toString() {
+    return "PolicyRuleItem{" +
+        "precedence=" + precedence +
+        ", match=" + match +
+        ", route=" + route +
+        ", total=" + total +
+        ", weightLess=" + weightLess +
+        '}';
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
new file mode 100644
index 0000000..6afa2b3
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/RouteItem.java
@@ -0,0 +1,111 @@
+/*
+ * 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.model;
+
+import java.util.Map;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class RouteItem implements Comparable<RouteItem> {
+
+  private Integer weight;
+  /**
+   * 负载均衡参数
+   */
+  private Integer currentWeight = 0;
+  /**
+   * 为了提高序列化速度设置为Map 固定字段 version
+   */
+  private Map<String, String> tags;
+
+  private TagItem tagitem;
+
+
+  public void initTagItem() {
+    if (tags != null && tags.containsKey("version")) {
+      tagitem = new TagItem(tags);
+    }
+  }
+
+  public void addCurrentWeight() {
+    currentWeight += weight;
+  }
+
+  public void reduceCurrentWeight(int total) {
+    currentWeight -= total;
+  }
+
+  public RouteItem() {
+  }
+
+  public RouteItem(Integer weight, TagItem tags) {
+    this.weight = weight;
+    this.tagitem = tags;
+  }
+
+  public Integer getWeight() {
+    return weight;
+  }
+
+  public void setWeight(Integer weight) {
+    this.weight = weight;
+  }
+
+  public Integer getCurrentWeight() {
+    return currentWeight;
+  }
+
+  public void setCurrentWeight(Integer currentWeight) {
+    this.currentWeight = currentWeight;
+  }
+
+  public Map<String, String> getTags() {
+    return tags;
+  }
+
+  public void setTags(Map<String, String> tags) {
+    this.tags = tags;
+  }
+
+  public TagItem getTagitem() {
+    return tagitem;
+  }
+
+  public void setTagitem(TagItem tagitem) {
+    this.tagitem = tagitem;
+  }
+
+  @Override
+  public int compareTo(RouteItem param) {
+    if (param.weight == this.weight) {
+      return 0;
+    }
+    return param.weight > this.weight ? 1 : -1;
+  }
+
+  @Override
+  public String toString() {
+    return "RouteItem{" +
+        "weight=" + weight +
+        ", currentWeight=" + currentWeight +
+        ", tags=" + tags +
+        ", tagitem=" + tagitem +
+        '}';
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java
new file mode 100644
index 0000000..903ef45
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.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.servicecomb.router.model;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class ServiceInfoCache {
+
+  private List<PolicyRuleItem> allrule;
+  //用于default的情况
+  private TagItem latestVersionTag;
+
+  public ServiceInfoCache() {
+  }
+
+  public void sortRule() {
+    allrule = allrule.stream().sorted().collect(Collectors.toList());
+  }
+
+  public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) {
+    List<RouteItem> rule = policyRuleItem.getRoute();
+    if (policyRuleItem.getTotal() == null) {
+      
policyRuleItem.setTotal(rule.stream().mapToInt(RouteItem::getWeight).sum());
+    }
+    rule.stream().forEach(RouteItem::addCurrentWeight);
+    int maxIndex = 0, maxWeight = -1;
+    for (int i = 0; i < rule.size(); i++) {
+      if (maxWeight < rule.get(i).getCurrentWeight()) {
+        maxIndex = i;
+        maxWeight = rule.get(i).getCurrentWeight();
+      }
+    }
+    rule.get(maxIndex).reduceCurrentWeight(policyRuleItem.getTotal());
+    return rule.get(maxIndex).getTagitem();
+  }
+
+  public List<PolicyRuleItem> getAllrule() {
+    return allrule;
+  }
+
+  public void setAllrule(List<PolicyRuleItem> allrule) {
+    this.allrule = allrule;
+  }
+
+  public TagItem getLatestVersionTag() {
+    return latestVersionTag;
+  }
+
+  public void setLatestVersionTag(TagItem latestVersionTag) {
+    this.latestVersionTag = latestVersionTag;
+  }
+
+  @Override
+  public String toString() {
+    return "ServiceInfoCache{" +
+        "allrule=" + allrule +
+        ", latestVersionTag=" + latestVersionTag +
+        '}';
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/TagItem.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/TagItem.java
new file mode 100644
index 0000000..3f3230a
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/model/TagItem.java
@@ -0,0 +1,132 @@
+/*
+ * 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.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/17
+ **/
+public class TagItem {
+
+  private String version;
+  private Map<String, String> param;
+
+  public TagItem() {
+  }
+
+  public TagItem(String version, Map<String, String> param) {
+    this.version = version;
+    this.param = param;
+  }
+
+  public TagItem(String version) {
+    this.version = version;
+    Map<String, String> param = new HashMap<>();
+    param.put("version", version);
+    this.param = param;
+  }
+
+  public TagItem(Map<String, String> param) {
+    if (param.containsKey("version")) {
+      this.version = param.get("version");
+    }
+    this.param = param;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public Map<String, String> getParam() {
+    return param;
+  }
+
+  public void setParam(Map<String, String> param) {
+    this.param = param;
+  }
+
+  /**
+   * map在匹配key调用
+   *
+   * @return
+   */
+  @Override
+  public int hashCode() {
+    int result = Objects.hash(version);
+    if (param != null) {
+      result = 31 * result + param.hashCode();
+    }
+    return result;
+  }
+
+  /**
+   * all match map在匹配key调用
+   *
+   * @param obj
+   * @return
+   */
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj != null && !(obj instanceof TagItem)) {
+      return false;
+    }
+    TagItem item = (TagItem) obj;
+    if (this.param == null && item.getParam() == null) {
+      return this.version.equals(item.getVersion());
+    }
+    if (this.param == null || item.getParam() == null) {
+      return false;
+    }
+    if (this.version != null && item.getVersion() != null) {
+      return this.version.equals(item.getVersion()) && 
this.param.equals(item.getParam());
+    }
+    return this.param.equals(item.getParam());
+  }
+
+
+  /**
+   * 返回匹配的个数
+   *
+   * @param item
+   * @return
+   */
+  public int matchNum(TagItem item) {
+    int cnt = 0;
+    if (version != null && !version.equals(item.version)) {
+      return 0;
+    }
+    for (Map.Entry<String, String> entry : param.entrySet()) {
+      if (item.getParam().containsKey(entry.getKey()) &&
+          !item.getParam().get(entry.getKey()).equals(entry.getValue())) {
+        return 0;
+      }
+      cnt++;
+    }
+    return cnt;
+  }
+}
diff --git 
a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java
 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java
new file mode 100644
index 0000000..e524973
--- /dev/null
+++ 
b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java
@@ -0,0 +1,38 @@
+package org.apache.servicecomb.router.util;
+
+/**
+ * @Author GuoYl123
+ * @Date 2019/10/16
+ **/
+public class VersionCompareUtil {
+    /**
+     * 前者大则返回一个正数
+     *
+     * @param version1
+     * @param version2
+     * @return
+     */
+    public static int compareVersion(String version1, String version2) {
+        if (version1 == null || version2 == null) {
+            throw new RuntimeException("version can not be null");
+        }
+        String[] versionArray1 = version1.split("\\.");
+        String[] versionArray2 = version2.split("\\.");
+        int idx = 0;
+        int diff = 0;
+        int minLength = Math.min(versionArray1.length, versionArray2.length);
+        while (idx < minLength
+                && versionArray1[idx].length() - versionArray2[idx].length() 
== 0
+                && versionArray1[idx].compareTo(versionArray2[idx]) == 0) {
+            ++idx;
+        }
+        idx = idx < minLength ? idx : idx - 1;
+        if (versionArray1[idx].length() - versionArray2[idx].length() == 0) {
+            diff = versionArray1[idx].compareTo(versionArray2[idx]);
+        } else {
+            diff = versionArray1[idx].length() - versionArray2[idx].length();
+        }
+        diff = (diff != 0) ? diff : versionArray1.length - 
versionArray2.length;
+        return diff;
+    }
+}
diff --git a/handlers/pom.xml b/handlers/pom.xml
index 3672dfe..999fcb1 100644
--- a/handlers/pom.xml
+++ b/handlers/pom.xml
@@ -38,6 +38,7 @@
     <module>handler-loadbalance</module>
     <module>handler-fault-injection</module>
     <module>handler-publickey-auth</module>
+    <module>handler-router</module>
   </modules>
 
 </project>

Reply via email to