You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/03/14 08:55:56 UTC

[incubator-servicecomb-java-chassis] branch master updated: SCB-352 refactor QpsControllerManager, support operation level qps flow control on provider side

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/incubator-servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new f0fae33  SCB-352 refactor QpsControllerManager, support operation level qps flow control on provider side
f0fae33 is described below

commit f0fae338cd9e617c366a3d52e27a4d59cf8a63e1
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Fri Mar 2 09:11:43 2018 +0800

    SCB-352 refactor QpsControllerManager, support operation level qps flow control on provider side
---
 handlers/handler-flowcontrol-qps/pom.xml           |  55 +++--
 .../qps/ConsumerQpsControllerManager.java          | 157 -------------
 .../qps/ConsumerQpsFlowControlHandler.java         |  15 +-
 .../qps/ProviderQpsControllerManager.java          | 143 ------------
 .../qps/ProviderQpsFlowControlHandler.java         |  22 +-
 .../servicecomb/qps/QpsControllerManager.java      | 175 ++++++++++++++
 .../servicecomb/qps/QpsControllerManagerTest.java  | 258 +++++++++++++++++++++
 .../qps/TestConsumerQpsControllermanager.java      |  75 ------
 .../qps/TestConsumerQpsFlowControlHandler.java     |  58 +++--
 .../qps/TestProviderQpsControllermanager.java      |  44 ----
 .../qps/TestProviderQpsFlowControlHandler.java     |  39 +++-
 11 files changed, 555 insertions(+), 486 deletions(-)

diff --git a/handlers/handler-flowcontrol-qps/pom.xml b/handlers/handler-flowcontrol-qps/pom.xml
index e9c10bd..86a3943 100644
--- a/handlers/handler-flowcontrol-qps/pom.xml
+++ b/handlers/handler-flowcontrol-qps/pom.xml
@@ -16,29 +16,34 @@
   -->
 
 <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">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>org.apache.servicecomb</groupId>
-		<artifactId>handlers</artifactId>
-		<version>1.0.0-m1</version>
-	</parent>
-	<artifactId>handler-flowcontrol-qps</artifactId>
-	<name>Java Chassis::Handlers::Flow Control QPS</name>
-	<dependencies>
-		<dependency>
-			<groupId>org.apache.servicecomb</groupId>
-			<artifactId>java-chassis-core</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-log4j12</artifactId>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>log4j</groupId>
-			<artifactId>log4j</artifactId>
-			<scope>test</scope>
-		</dependency>
-	</dependencies>
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.servicecomb</groupId>
+    <artifactId>handlers</artifactId>
+    <version>1.0.0-m1</version>
+  </parent>
+  <artifactId>handler-flowcontrol-qps</artifactId>
+  <name>Java Chassis::Handlers::Flow Control QPS</name>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>java-chassis-core</artifactId>
+    </dependency>
+    <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>org.apache.servicecomb</groupId>
+      <artifactId>foundation-test-scaffolding</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsControllerManager.java
deleted file mode 100644
index 076df49..0000000
--- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsControllerManager.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.qps;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.servicecomb.core.definition.OperationMeta;
-import org.apache.servicecomb.foundation.common.AbstractObjectManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.netflix.config.DynamicProperty;
-
-/**
- * 以microservice.schema.operation为key
- * 如果配置只到microservice级别,那么该microservice中所有的key都关联到同一个qpsController实例
- * 然后schema、operation级别有独立配置的,单独关联自己的qpsController实例
- *
- * schema级独立的qpsController统计时,并不会导致microservice级别的统计也改变,operation级别规则也相同
- * 即:统计只在qpsController实例内部生效,不会产生实例间的关联
- *
- */
-public class ConsumerQpsControllerManager extends AbstractObjectManager<OperationMeta, String, QpsController> {
-  private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerQpsControllerManager.class);
-  // 最终使用的QpsController实例,实际都是从下面的map中引用出来的,不会独立创建
-
-  // 3种类型的key都保存在这里,不存在冲突
-  // microservice
-  // microservice.schema
-  // microservice.schema.operation
-  private Map<String, QpsController> qpsControllerMap = new ConcurrentHashMap<>();
-
-  // 避免重复watch
-  // 只会在create流程中调用,是有锁保护的,不必考虑多线程并发
-  private Set<String> watchedKeySet = new HashSet<>();
-
-  @Override
-  protected String getKey(OperationMeta operationMeta) {
-    return operationMeta.getMicroserviceQualifiedName();
-  }
-
-  private QpsController initQpsLimit(String key, Integer qpsLimit) {
-    if (qpsLimit == null) {
-      return null;
-    }
-
-    LOGGER.info("qpsLimit of {} init as {}", key, qpsLimit);
-
-    QpsController qpsController = new QpsController(key, qpsLimit);
-    qpsControllerMap.put(key, qpsController);
-    return qpsController;
-  }
-
-  private QpsController updateQpsLimit(String key, Integer qpsLimit) {
-    QpsController qpsController = qpsControllerMap.get(key);
-    if (qpsController == null && qpsLimit != null) {
-      qpsController = new QpsController(key, qpsLimit);
-      qpsControllerMap.put(key, qpsController);
-    }
-
-    if (qpsController != null) {
-      LOGGER.info("qpsLimit of {} changed from {} to {}", key, qpsController.getQpsLimit(), qpsLimit);
-
-      qpsController.setQpsLimit(qpsLimit);
-    }
-
-    return qpsController;
-  }
-
-  private QpsController findReference(String key) {
-    QpsController qpsController = qpsControllerMap.get(key);
-    if (qpsController == null) {
-      return null;
-    }
-
-    if (qpsController.getQpsLimit() == null) {
-      return null;
-    }
-
-    return qpsController;
-  }
-
-  private QpsController findReference(OperationMeta operationMeta) {
-    QpsController qpsController = findReference(operationMeta.getMicroserviceQualifiedName());
-    if (qpsController != null) {
-      return qpsController;
-    }
-
-    qpsController = findReference(operationMeta.getSchemaMeta().getMicroserviceQualifiedName());
-    if (qpsController != null) {
-      return qpsController;
-    }
-
-    qpsController = findReference(operationMeta.getMicroserviceName());
-    if (qpsController != null) {
-      return qpsController;
-    }
-
-    return initQpsLimit(operationMeta.getMicroserviceName(), Integer.MAX_VALUE);
-  }
-
-  @Override
-  protected QpsController create(OperationMeta operationMeta) {
-    // create在父类中是加了锁的,不存在并发的场景
-    initConfig(operationMeta, operationMeta.getMicroserviceQualifiedName());
-    initConfig(operationMeta, operationMeta.getSchemaMeta().getMicroserviceQualifiedName());
-    initConfig(operationMeta, operationMeta.getMicroserviceName());
-
-    return findReference(operationMeta);
-  }
-
-  private void initConfig(OperationMeta operationMeta, String key) {
-    if (watchedKeySet.contains(key)) {
-      return;
-    }
-
-    watchedKeySet.add(key);
-
-    String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key;
-    DynamicProperty property = DynamicProperty.getInstance(configKey);
-    initQpsLimit(key, getIntegerLimitProperty(property));
-
-    property.addCallback(() -> {
-      updateQpsLimit(key, getIntegerLimitProperty(property));
-      QpsController qpsController = findReference(operationMeta);
-
-      objMap.put(operationMeta.getMicroserviceQualifiedName(), qpsController);
-    });
-  }
-
-  private Integer getIntegerLimitProperty(DynamicProperty property) {
-    try {
-      return property.getInteger();
-    } catch (IllegalArgumentException e) {
-      LOGGER.error(e.getMessage());
-      return null;
-    }
-  }
-}
diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsFlowControlHandler.java
index b1fd2a6..101babc 100644
--- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsFlowControlHandler.java
+++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsFlowControlHandler.java
@@ -19,18 +19,17 @@ package org.apache.servicecomb.qps;
 
 import org.apache.servicecomb.core.Handler;
 import org.apache.servicecomb.core.Invocation;
-import org.apache.servicecomb.core.definition.OperationMeta;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
 import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 
 /**
- * consumer端针对调用目标的qps控制
- * 支持microservice、schema、operation三个级别的控制
- *
+ * For qps flow control on consumer side.
+ * Support 3 levels of microservice/schema/operation.
  */
 public class ConsumerQpsFlowControlHandler implements Handler {
-  private ConsumerQpsControllerManager qpsControllerMgr = new ConsumerQpsControllerManager();
+  static final QpsControllerManager qpsControllerMgr = new QpsControllerManager()
+      .setConfigKeyPrefix(Config.CONSUMER_LIMIT_KEY_PREFIX);
 
   @Override
   public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
@@ -39,10 +38,10 @@ public class ConsumerQpsFlowControlHandler implements Handler {
       return;
     }
 
-    OperationMeta operationMeta = invocation.getOperationMeta();
-    QpsController qpsController = qpsControllerMgr.getOrCreate(operationMeta);
+    QpsController qpsController = qpsControllerMgr.getOrCreate(
+        invocation.getOperationMeta().getMicroserviceQualifiedName());
     if (qpsController.isLimitNewRequest()) {
-      // 429
+      // return http status 429
       CommonExceptionData errorData = new CommonExceptionData("rejected by qps flowcontrol");
       asyncResp.consumerFail(
           new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData));
diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsControllerManager.java
deleted file mode 100644
index d8d6c76..0000000
--- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsControllerManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.qps;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.servicecomb.foundation.common.AbstractObjectManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.netflix.config.DynamicProperty;
-
-public class ProviderQpsControllerManager
-    extends AbstractObjectManager<String, String, QpsController> {
-  private static final Logger LOGGER = LoggerFactory.getLogger(ProviderQpsControllerManager.class);
-
-  private Map<String, QpsController> qpsControllerMap = new ConcurrentHashMap<>();
-
-  // 避免重复watch
-  // 只会在create流程中调用,是有锁保护的,不必考虑多线程并发
-  private Set<String> watchedKeySet = new HashSet<>();
-
-  private QpsController globalQpsController = null;
-
-  @Override
-  protected String getKey(String microServiceName) {
-    return microServiceName;
-  }
-
-  @Override
-  public QpsController getOrCreate(String keyOwner) {
-    if (keyOwner == null) {
-      if (globalQpsController == null) {
-        synchronized (lockObj) {
-          if (globalQpsController == null) {
-            DynamicProperty property =
-                DynamicProperty.getInstance(Config.PROVIDER_LIMIT_KEY_GLOBAL);
-            globalQpsController = new QpsController(keyOwner, getIntegerLimitProperty(property));
-            property.addCallback(() -> {
-              globalQpsController.setQpsLimit(getIntegerLimitProperty(property));
-            });
-          }
-        }
-      }
-      return globalQpsController;
-    } else {
-      return super.getOrCreate(keyOwner);
-    }
-  }
-
-  private QpsController initQpsLimit(String key, Integer qpsLimit) {
-    if (qpsLimit == null) {
-      qpsLimit = null;
-    }
-
-    LOGGER.info("qpsLimit of {} init as {}", key, qpsLimit);
-
-    QpsController qpsController = new QpsController(key, qpsLimit);
-    qpsControllerMap.put(key, qpsController);
-    return qpsController;
-  }
-
-  private QpsController updateQpsLimit(String key, Integer qpsLimit) {
-    QpsController qpsController = qpsControllerMap.get(key);
-    if (qpsController == null && qpsLimit != null) {
-      qpsController = new QpsController(key, qpsLimit);
-      qpsControllerMap.put(key, qpsController);
-    }
-
-    if (qpsController != null) {
-      LOGGER.info("qpsLimit of {} changed from {} to {}",
-          key,
-          qpsController.getQpsLimit(),
-          qpsLimit);
-
-      qpsController.setQpsLimit(qpsLimit);
-    }
-
-    return qpsController;
-  }
-
-  private QpsController findReference(String key) {
-    QpsController qpsController = qpsControllerMap.get(key);
-    if (qpsController == null) {
-      return initQpsLimit(key, Integer.MAX_VALUE);
-    }
-    return qpsController;
-  }
-
-  @Override
-  protected QpsController create(String microServiceName) {
-    // create在父类中是加了锁的,不存在并发的场景
-    initConfig(microServiceName);
-
-    return findReference(microServiceName);
-  }
-
-  private void initConfig(String key) {
-    if (watchedKeySet.contains(key)) {
-      return;
-    }
-
-    watchedKeySet.add(key);
-
-    String configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key;
-    DynamicProperty property = DynamicProperty.getInstance(configKey);
-    initQpsLimit(key, getIntegerLimitProperty(property));
-
-    property.addCallback(() -> {
-      updateQpsLimit(key, getIntegerLimitProperty(property));
-      QpsController qpsController = findReference(key);
-
-      objMap.put(key, qpsController);
-    });
-  }
-
-  private Integer getIntegerLimitProperty(DynamicProperty property) {
-    try {
-      return property.getInteger();
-    } catch (IllegalArgumentException e) {
-      LOGGER.error(e.getMessage());
-      return null;
-    }
-  }
-}
diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsFlowControlHandler.java
index 1cf669d..b81581f 100644
--- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsFlowControlHandler.java
+++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderQpsFlowControlHandler.java
@@ -23,9 +23,12 @@ import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
 import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+import org.springframework.util.StringUtils;
 
 public class ProviderQpsFlowControlHandler implements Handler {
-  private ProviderQpsControllerManager qpsControllerMgr = new ProviderQpsControllerManager();
+  static final QpsControllerManager qpsControllerMgr = new QpsControllerManager()
+      .setConfigKeyPrefix(Config.PROVIDER_LIMIT_KEY_PREFIX)
+      .setGlobalQpsController(Config.PROVIDER_LIMIT_KEY_GLOBAL);
 
   @Override
   public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
@@ -34,16 +37,13 @@ public class ProviderQpsFlowControlHandler implements Handler {
       return;
     }
 
-    String microServiceName = (String) invocation.getContext(Const.SRC_MICROSERVICE);
-    if (microServiceName != null && !microServiceName.isEmpty()) {
-      QpsController qpsController = qpsControllerMgr.getOrCreate(microServiceName);
-      if (isLimitNewRequest(qpsController, asyncResp)) {
-        return;
-      }
-    }
-
-    QpsController globalQpsController = qpsControllerMgr.getOrCreate(null);
-    if (isLimitNewRequest(globalQpsController, asyncResp)) {
+    String microServiceName = invocation.getContext(Const.SRC_MICROSERVICE);
+    QpsController qpsController =
+        StringUtils.isEmpty(microServiceName)
+            ? qpsControllerMgr.getGlobalQpsController()
+            : qpsControllerMgr.getOrCreate(microServiceName + QpsControllerManager.SEPARATOR
+                + invocation.getOperationMeta().getSchemaQualifiedName());
+    if (isLimitNewRequest(qpsController, asyncResp)) {
       return;
     }
 
diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java
new file mode 100644
index 0000000..09a69ed
--- /dev/null
+++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java
@@ -0,0 +1,175 @@
+/*
+ * 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.qps;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.config.DynamicProperty;
+
+public class QpsControllerManager {
+  private static final Logger LOGGER = LoggerFactory.getLogger(QpsControllerManager.class);
+
+  private static final Pattern QUALIFIED_KEY_CHECKER = Pattern.compile("^[^.]+\\.[^.]+\\.[^.]+$");
+
+  /**
+   * Describe the relationship between configuration and qpsController.
+   */
+  protected final Map<String, QpsController> configQpsControllerMap = new ConcurrentHashMapEx<>();
+
+  /**
+   * Describe the relationship between qualifiedKey(format is "microservice.schema.operation") and qpsController.
+   */
+  protected final Map<String, QpsController> qualifiedNameControllerMap = new ConcurrentHashMapEx<>();
+
+  protected QpsController globalQpsController;
+
+  public static final String SEPARATOR = ".";
+
+  private String configKeyPrefix;
+
+  public QpsController getOrCreate(String key) {
+    return qualifiedNameControllerMap.computeIfAbsent(key, this::create);
+  }
+
+  /**
+   * Create relevant qpsLimit dynamicProperty and watch the configuration change.
+   * Search and return a valid qpsController.
+   */
+  protected QpsController create(String qualifiedNameKey) {
+    if (!QUALIFIED_KEY_CHECKER.matcher(qualifiedNameKey).matches()) {
+      throw new IllegalArgumentException("Unexpected qualified name: [" + qualifiedNameKey + "]");
+    }
+    // create "microservice"
+    createQpsControllerIfNotExist(qualifiedNameKey.substring(0, qualifiedNameKey.indexOf(SEPARATOR)));
+    // create "microservice.schema"
+    createQpsControllerIfNotExist(qualifiedNameKey.substring(0, qualifiedNameKey.lastIndexOf(SEPARATOR)));
+    // create "microservice.schema.operation"
+    createQpsControllerIfNotExist(qualifiedNameKey);
+
+    return searchQpsController(qualifiedNameKey);
+  }
+
+  /**
+   * <p> Use qualifiedNameKey to search {@link QpsController}.
+   * Firstly try to search "microservice.schema.operation". If no valid result found, then try "microservice.schema",
+   * and then "microservice" or global qpsController(If there is a global qpsController).</p>
+   * <p> This method ensures that there is always an existing qpsController returned, as the relevant qpsController has
+   * been created and stored in {@link #create(String)}</p>
+   *
+   * @param qualifiedNameKey qualifiedNameKey in {@link #qualifiedNameControllerMap}
+   * @return a qps controller, lower level controllers with valid qpsLimit have priority.
+   */
+  protected QpsController searchQpsController(String qualifiedNameKey) {
+    QpsController qpsController = configQpsControllerMap.get(qualifiedNameKey);
+    if (isValidQpsController(qpsController)) {
+      return qpsController;
+    }
+
+    int index = qualifiedNameKey.lastIndexOf(SEPARATOR);
+    while (index > 0) {
+      qpsController = configQpsControllerMap.get(qualifiedNameKey.substring(0, index));
+      if (isValidQpsController(qpsController)) {
+        return qpsController;
+      }
+
+      index = qualifiedNameKey.lastIndexOf(SEPARATOR, index - 1);
+    }
+
+    if (isValidQpsController(qpsController)) {
+      return qpsController;
+    }
+
+    if (null != globalQpsController) {
+      return globalQpsController;
+    }
+
+    // if null is returned, maybe the operation qps controller is not initiated correctly.
+    // getOrCreateQpsController() should be invoked before.
+    return qpsController;
+  }
+
+  private boolean keyMatch(String configKey, Entry<String, QpsController> controllerEntry) {
+    return controllerEntry.getKey().equals(configKey)
+        || controllerEntry.getKey().startsWith(configKey + SEPARATOR);
+  }
+
+  private boolean isValidQpsController(QpsController qpsController) {
+    return null != qpsController && null != qpsController.getQpsLimit();
+  }
+
+  private void createQpsControllerIfNotExist(String configKey) {
+    if (configQpsControllerMap.keySet().contains(configKey)) {
+      return;
+    }
+
+    LOGGER.info("Create qpsController, configKey = [{}]", configKey);
+    DynamicProperty property = getDynamicProperty(configKey);
+    QpsController qpsController = new QpsController(configKey, property.getInteger());
+
+    configQpsControllerMap.put(configKey, qpsController);
+
+    property.addCallback(() -> {
+      qpsController.setQpsLimit(property.getInteger());
+      LOGGER.info("Qps limit updated, configKey = [{}], value = [{}]", configKey, property.getString());
+      updateObjMap(configKey);
+    });
+  }
+
+  protected void updateObjMap(String configKey) {
+    for (Entry<String, QpsController> controllerEntry : qualifiedNameControllerMap.entrySet()) {
+      if (keyMatch(configKey, controllerEntry)) {
+        QpsController qpsController = searchQpsController(controllerEntry.getKey());
+        controllerEntry.setValue(qpsController);
+        LOGGER.info("QpsController updated, operationId = [{}], configKey = [{}], qpsLimit = [{}]",
+            controllerEntry.getKey(), qpsController.getKey(), qpsController.getQpsLimit());
+      }
+    }
+  }
+
+  public QpsControllerManager setConfigKeyPrefix(String configKeyPrefix) {
+    this.configKeyPrefix = configKeyPrefix;
+    return this;
+  }
+
+  public QpsControllerManager setGlobalQpsController(String globalConfigKey) {
+    DynamicProperty globalQpsProperty = DynamicProperty.getInstance(globalConfigKey);
+    QpsController qpsController = new QpsController(globalConfigKey, globalQpsProperty.getInteger());
+
+    globalQpsProperty.addCallback(() -> {
+      qpsController.setQpsLimit(globalQpsProperty.getInteger());
+      LOGGER.info("Global qps limit update, value = [{}]", globalQpsProperty.getInteger());
+    });
+
+    this.globalQpsController = qpsController;
+    return this;
+  }
+
+  public QpsController getGlobalQpsController() {
+    return globalQpsController;
+  }
+
+  protected DynamicProperty getDynamicProperty(String configKey) {
+    return DynamicProperty.getInstance(configKeyPrefix + configKey);
+  }
+}
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
new file mode 100644
index 0000000..1837ff2
--- /dev/null
+++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.qps;
+
+import java.util.Map;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.definition.SchemaMeta;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import mockit.Deencapsulation;
+
+public class QpsControllerManagerTest {
+
+  @Before
+  public void beforeTest() {
+    ArchaiusUtils.resetConfig();
+  }
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+  }
+
+  @Test
+  public void testGetOrCreate() {
+    QpsControllerManager testQpsControllerManager = new QpsControllerManager()
+        .setConfigKeyPrefix(Config.CONSUMER_LIMIT_KEY_PREFIX);
+    initTestQpsControllerManager(testQpsControllerManager);
+
+    // pojo
+    setConfigWithDefaultPrefix("pojo", 100);
+    QpsController qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals("pojo", qpsController.getKey());
+    Assert.assertTrue(100 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo2.server.test");
+    Assert.assertEquals("pojo2", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("poj.server.test");
+    Assert.assertEquals("poj", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    testGetOrCreateCommon(testQpsControllerManager);
+  }
+
+  @Test
+  public void testGetOrCreateWithGlobalConfig() {
+    QpsControllerManager testQpsControllerManager = new QpsControllerManager()
+        .setGlobalQpsController(Config.PROVIDER_LIMIT_KEY_GLOBAL)
+        .setConfigKeyPrefix(Config.CONSUMER_LIMIT_KEY_PREFIX);
+
+    // global
+    setConfig(Config.PROVIDER_LIMIT_KEY_GLOBAL, 50);
+    QpsController qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
+    Assert.assertTrue(50 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo2.server.test");
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
+    Assert.assertTrue(50 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("poj.server.test");
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
+    Assert.assertTrue(50 == qpsController.getQpsLimit());
+
+    // pojo
+    setConfigWithDefaultPrefix("pojo", 100);
+    qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals("pojo", qpsController.getKey());
+    Assert.assertTrue(100 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo2.server.test");
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
+    Assert.assertTrue(50 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("poj.server.test");
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
+    Assert.assertTrue(50 == qpsController.getQpsLimit());
+
+    testGetOrCreateCommon(testQpsControllerManager);
+  }
+
+  @Test
+  public void testQualifiedNameKey() {
+    QpsControllerManager qpsControllerManager = new QpsControllerManager();
+    QpsController qpsController = qpsControllerManager.getOrCreate("service.schema.opr");
+    Assert.assertEquals("service", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    qpsController = qpsControllerManager.getOrCreate("test_service.test_schema.test_opr");
+    Assert.assertEquals("test_service", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    qpsController = qpsControllerManager.getOrCreate("test-service.test-schema.test-opr");
+    Assert.assertEquals("test-service", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    Exception exception = null;
+    try {
+      qpsControllerManager.getOrCreate("svc.schema.opr.tail");
+    } catch (Exception e) {
+      exception = e;
+    }
+    Assert.assertNotNull(exception);
+    Assert.assertEquals(IllegalArgumentException.class, exception.getClass());
+    Assert.assertEquals("Unexpected qualified name: [svc.schema.opr.tail]", exception.getMessage());
+
+    try {
+      qpsControllerManager.getOrCreate("svc.schema");
+    } catch (Exception e) {
+      exception = e;
+    }
+    Assert.assertNotNull(exception);
+    Assert.assertEquals(IllegalArgumentException.class, exception.getClass());
+    Assert.assertEquals("Unexpected qualified name: [svc.schema]", exception.getMessage());
+
+    try {
+      qpsControllerManager.getOrCreate("...");
+    } catch (Exception e) {
+      exception = e;
+    }
+    Assert.assertNotNull(exception);
+    Assert.assertEquals(IllegalArgumentException.class, exception.getClass());
+    Assert.assertEquals("Unexpected qualified name: [...]", exception.getMessage());
+  }
+
+  private void testGetOrCreateCommon(QpsControllerManager testQpsControllerManager) {
+    // pojo.server
+    setConfigWithDefaultPrefix("pojo.server", 200);
+    QpsController qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals("pojo.server", qpsController.getKey());
+    Assert.assertTrue(200 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo.server2.test");
+    Assert.assertEquals("pojo", qpsController.getKey());
+    Assert.assertTrue(100 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo.serve.test");
+    Assert.assertEquals("pojo", qpsController.getKey());
+    Assert.assertTrue(100 == qpsController.getQpsLimit());
+
+    // pojo.server.test
+    setConfigWithDefaultPrefix("pojo.server.test", 300);
+    qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals("pojo.server.test", qpsController.getKey());
+    Assert.assertTrue(300 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo.server.test2");
+    Assert.assertEquals("pojo.server", qpsController.getKey());
+    Assert.assertTrue(200 == qpsController.getQpsLimit());
+    qpsController = testQpsControllerManager.getOrCreate("pojo.server.tes");
+    Assert.assertEquals("pojo.server", qpsController.getKey());
+    Assert.assertTrue(200 == qpsController.getQpsLimit());
+  }
+
+  /**
+   * Init testQpsControllerManager to test search function.
+   */
+  private void initTestQpsControllerManager(QpsControllerManager testQpsControllerManager) {
+    // pojo.server.test
+    QpsController qpsController = testQpsControllerManager.getOrCreate("pojo.server.test");
+    Assert.assertEquals("pojo", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    // pojo.server.test2
+    testQpsControllerManager.getOrCreate("pojo.server.test2");
+
+    // pojo.server.tes
+    testQpsControllerManager.getOrCreate("pojo.server.tes");
+
+    // pojo.server2.test
+    testQpsControllerManager.getOrCreate("pojo.server2.test");
+
+    // pojo.serve.test
+    testQpsControllerManager.getOrCreate("pojo.serve.test");
+
+    // pojo2.server.test
+    qpsController = testQpsControllerManager.getOrCreate("pojo2.server.test");
+    Assert.assertEquals("pojo2", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+
+    // poj.server.test
+    qpsController = testQpsControllerManager.getOrCreate("poj.server.test");
+    Assert.assertEquals("poj", qpsController.getKey());
+    Assert.assertNull(qpsController.getQpsLimit());
+  }
+
+  @Test
+  public void testMock() {
+    Invocation invocation = getMockInvocation("service", "schema", "oper");
+    OperationMeta operationMeta = invocation.getOperationMeta();
+    SchemaMeta schemaMeta = operationMeta.getSchemaMeta();
+
+    Assert.assertEquals("service", operationMeta.getMicroserviceName());
+    Assert.assertEquals("service.schema.oper", operationMeta.getMicroserviceQualifiedName());
+    Assert.assertEquals("schema.oper", operationMeta.getSchemaQualifiedName());
+    Assert.assertEquals("schema", schemaMeta.getSchemaId());
+  }
+
+  public static Invocation getMockInvocation(String microserviceName, String schemaId, String operationId) {
+    return getMockInvocation(
+        getMockOperationMeta(microserviceName, schemaId, operationId)
+    );
+  }
+
+  private static Invocation getMockInvocation(OperationMeta mockOperationMeta) {
+    Invocation invocation = Mockito.mock(Invocation.class);
+    Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta);
+    return invocation;
+  }
+
+  public static OperationMeta getMockOperationMeta(String microserviceName, String schemaId, String operationId) {
+    OperationMeta operationMeta = Mockito.mock(OperationMeta.class);
+    SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class);
+
+    Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta);
+    Mockito.when(operationMeta.getSchemaQualifiedName())
+        .thenReturn(schemaId + QpsControllerManager.SEPARATOR + operationId);
+    Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn(
+        microserviceName + QpsControllerManager.SEPARATOR + schemaId + QpsControllerManager.SEPARATOR
+            + operationId);
+    Mockito.when(operationMeta.getMicroserviceName()).thenReturn(microserviceName);
+    Mockito.when(schemaMeta.getSchemaId()).thenReturn(schemaId);
+
+    return operationMeta;
+  }
+
+  public static void setConfig(String key, int value) {
+    Utils.updateProperty(key, value);
+  }
+
+  public static void setConfigWithDefaultPrefix(String key, int value) {
+    String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key;
+    Utils.updateProperty(configKey, value);
+  }
+
+  public static void clearState(QpsControllerManager qpsControllerManager) {
+    Map<String, QpsController> objMap = Deencapsulation.getField(qpsControllerManager, "qualifiedNameControllerMap");
+    objMap.clear();
+    Map<String, QpsController> configQpsControllerMap = Deencapsulation
+        .getField(qpsControllerManager, "configQpsControllerMap");
+    configQpsControllerMap.clear();
+  }
+}
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsControllermanager.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsControllermanager.java
deleted file mode 100644
index 426f7f8..0000000
--- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsControllermanager.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.qps;
-
-import org.apache.servicecomb.core.definition.OperationMeta;
-import org.apache.servicecomb.core.definition.SchemaMeta;
-import org.junit.Assert;
-import org.junit.Test;
-
-import mockit.Expectations;
-import mockit.Mocked;
-
-/**
- *
- *
- */
-public class TestConsumerQpsControllermanager {
-  private static String microserviceName = "pojo";
-
-  private static String schemaQualified = microserviceName + ".server";
-
-  private static String operationQualified = schemaQualified + ".test";
-
-  @Test
-  public void testQpsLimit(@Mocked SchemaMeta schemaMeta, @Mocked OperationMeta operationMeta) {
-    new Expectations() {
-      {
-        operationMeta.getMicroserviceQualifiedName();
-        result = operationQualified;
-
-        schemaMeta.getMicroserviceQualifiedName();
-        result = schemaQualified;
-
-        operationMeta.getMicroserviceName();
-        result = microserviceName;
-      }
-    };
-
-    ConsumerQpsControllerManager mgr = new ConsumerQpsControllerManager();
-    QpsController qpsController = mgr.getOrCreate(operationMeta);
-    Assert.assertEquals((Integer) Integer.MAX_VALUE, qpsController.getQpsLimit());
-    Assert.assertEquals(microserviceName, qpsController.getKey());
-
-    doTestQpsLimit(mgr, operationMeta, microserviceName, 100, microserviceName, 100);
-    doTestQpsLimit(mgr, operationMeta, schemaQualified, 200, schemaQualified, 200);
-    doTestQpsLimit(mgr, operationMeta, operationQualified, 300, operationQualified, 300);
-    doTestQpsLimit(mgr, operationMeta, operationQualified, null, schemaQualified, 200);
-    doTestQpsLimit(mgr, operationMeta, schemaQualified, null, microserviceName, 100);
-    doTestQpsLimit(mgr, operationMeta, microserviceName, null, microserviceName, Integer.MAX_VALUE);
-  }
-
-  private void doTestQpsLimit(ConsumerQpsControllerManager mgr, OperationMeta operationMeta, String key,
-      Integer newValue,
-      String expectKey, Integer expectValue) {
-    Utils.updateProperty(Config.CONSUMER_LIMIT_KEY_PREFIX + key, newValue);
-    QpsController qpsController = mgr.getOrCreate(operationMeta);
-    Assert.assertEquals(expectValue, qpsController.getQpsLimit());
-    Assert.assertEquals(expectKey, qpsController.getKey());
-  }
-}
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsFlowControlHandler.java
index 368459e..6359a7e 100644
--- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsFlowControlHandler.java
+++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestConsumerQpsFlowControlHandler.java
@@ -17,20 +17,22 @@
 
 package org.apache.servicecomb.qps;
 
+import java.util.concurrent.ConcurrentHashMap;
+
 import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import mockit.Deencapsulation;
 import mockit.Mock;
 import mockit.MockUp;
 
-/**
- *
- *
- */
 public class TestConsumerQpsFlowControlHandler {
 
   ConsumerQpsFlowControlHandler handler = new ConsumerQpsFlowControlHandler();
@@ -41,8 +43,21 @@ public class TestConsumerQpsFlowControlHandler {
 
   OperationMeta operationMeta = Mockito.mock(OperationMeta.class);
 
+  @Before
+  public void setUP() {
+    ArchaiusUtils.resetConfig();
+    QpsControllerManagerTest.clearState(ConsumerQpsFlowControlHandler.qpsControllerMgr);
+  }
+
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+    QpsControllerManagerTest.clearState(ConsumerQpsFlowControlHandler.qpsControllerMgr);
+  }
+
   @Test
-  public void testQpsController() throws Exception {
+  public void testQpsController() {
     QpsController qpsController = new QpsController("abc", 100);
     Assert.assertEquals(false, qpsController.isLimitNewRequest());
 
@@ -68,9 +83,11 @@ public class TestConsumerQpsFlowControlHandler {
     boolean validAssert;
     try {
       validAssert = true;
+      String key = "svc.schema.opr";
+      QpsController qpsController = new QpsController("key", 12);
       Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta);
-      Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName");
-
+      Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn(key);
+      setQpsController(key, qpsController);
       new MockUp<QpsController>() {
         @Mock
         public boolean isLimitNewRequest() {
@@ -78,15 +95,15 @@ public class TestConsumerQpsFlowControlHandler {
         }
       };
 
-      new MockUp<ConsumerQpsControllerManager>() {
-
+      new MockUp<QpsControllerManager>() {
         @Mock
-        protected QpsController create(OperationMeta operationMeta) {
-          return new QpsController("key", 12);
+        protected QpsController create(String qualifiedNameKey) {
+          return qpsController;
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -97,8 +114,11 @@ public class TestConsumerQpsFlowControlHandler {
     boolean validAssert;
     try {
       validAssert = true;
+      String key = "MicroserviceQualifiedName";
+      QpsController qpsController = new QpsController("key", 12);
       Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta);
-      Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName");
+      Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn(key);
+      setQpsController(key, qpsController);
 
       new MockUp<QpsController>() {
         @Mock
@@ -107,17 +127,25 @@ public class TestConsumerQpsFlowControlHandler {
         }
       };
 
-      new MockUp<ConsumerQpsControllerManager>() {
+      new MockUp<QpsControllerManager>() {
 
         @Mock
-        protected QpsController create(OperationMeta operationMeta) {
-          return new QpsController("key", 12);
+        protected QpsController create(String qualifiedNameKey) {
+          return qpsController;
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
   }
+
+  private void setQpsController(String key, QpsController qpsController) {
+    QpsControllerManager qpsControllerManager = Deencapsulation.getField(handler, "qpsControllerMgr");
+    ConcurrentHashMap<String, QpsController> objMap = Deencapsulation
+        .getField(qpsControllerManager, "qualifiedNameControllerMap");
+    objMap.put(key, qpsController);
+  }
 }
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsControllermanager.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsControllermanager.java
deleted file mode 100644
index c16f709..0000000
--- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsControllermanager.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.qps;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestProviderQpsControllermanager {
-  private static String microserviceName = "pojo";
-
-  @Test
-  public void testQpsLimit() {
-    ProviderQpsControllerManager mgr = new ProviderQpsControllerManager();
-    QpsController qpsController = mgr.getOrCreate(microserviceName);
-    Assert.assertEquals(null, qpsController.getQpsLimit());
-    Assert.assertEquals(microserviceName, qpsController.getKey());
-
-    doTestQpsLimit(mgr, microserviceName, 100, microserviceName, 100);
-    doTestQpsLimit(mgr, microserviceName, null, microserviceName, null);
-  }
-
-  private void doTestQpsLimit(ProviderQpsControllerManager mgr, String key, Integer newValue,
-      String expectKey, Integer expectValue) {
-    Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + key, newValue);
-    QpsController qpsController = mgr.getOrCreate(key);
-    Assert.assertEquals(expectValue, qpsController.getQpsLimit());
-    Assert.assertEquals(expectKey, qpsController.getKey());
-  }
-}
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
index 40303db..1bdf8a2 100644
--- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
+++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestProviderQpsFlowControlHandler.java
@@ -17,10 +17,14 @@
 
 package org.apache.servicecomb.qps;
 
+import static org.junit.Assert.fail;
+
 import org.apache.servicecomb.core.Const;
 import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -42,9 +46,18 @@ public class TestProviderQpsFlowControlHandler {
 
   @Before
   public void setUP() {
+    ArchaiusUtils.resetConfig();
+    QpsControllerManagerTest.clearState(ProviderQpsFlowControlHandler.qpsControllerMgr);
     Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1);
   }
 
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+    QpsControllerManagerTest.clearState(ProviderQpsFlowControlHandler.qpsControllerMgr);
+  }
+
   @Test
   public void testGlobalQpsControl(final @Injectable Invocation invocation,
       final @Injectable AsyncResponse asyncResp) throws Exception {
@@ -52,6 +65,8 @@ public class TestProviderQpsFlowControlHandler {
       {
         invocation.getContext(Const.SRC_MICROSERVICE);
         result = "test";
+        invocation.getOperationMeta();
+        result = QpsControllerManagerTest.getMockOperationMeta("pojo", "server", "opr");
         asyncResp.producerFail((Throwable) any);
         result = new RuntimeException("test error");
       }
@@ -67,7 +82,7 @@ public class TestProviderQpsFlowControlHandler {
       gHandler.handle(invocation, asyncResp);
       count++;
       gHandler.handle(invocation, asyncResp);
-      Assert.assertTrue(false);
+      fail("An exception is expected!");
     } catch (Exception e) {
       Assert.assertEquals(2, count);
       Assert.assertEquals("test error", e.getMessage());
@@ -75,7 +90,7 @@ public class TestProviderQpsFlowControlHandler {
   }
 
   @Test
-  public void testQpsController() throws Exception {
+  public void testQpsController() {
     QpsController qpsController = new QpsController("abc", 100);
     Assert.assertEquals(false, qpsController.isLimitNewRequest());
 
@@ -93,6 +108,7 @@ public class TestProviderQpsFlowControlHandler {
       handler.handle(invocation, asyncResp);
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -104,6 +120,8 @@ public class TestProviderQpsFlowControlHandler {
     try {
       validAssert = true;
       Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test");
+      OperationMeta mockOperationMeta = QpsControllerManagerTest.getMockOperationMeta("pojo", "server", "opr");
+      Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta);
 
       new MockUp<QpsController>() {
         @Mock
@@ -112,15 +130,16 @@ public class TestProviderQpsFlowControlHandler {
         }
       };
 
-      new MockUp<ProviderQpsControllerManager>() {
+      new MockUp<QpsControllerManager>() {
 
         @Mock
-        protected QpsController create(String serviceName) {
-          return new QpsController(serviceName, 12);
+        protected QpsController create(String qualifiedNameKey) {
+          return new QpsController(qualifiedNameKey, 12);
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -132,6 +151,9 @@ public class TestProviderQpsFlowControlHandler {
     try {
       validAssert = true;
       Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test");
+      OperationMeta mockOperationMeta = QpsControllerManagerTest
+          .getMockOperationMeta("pojo", "server", "opr");
+      Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta);
 
       new MockUp<QpsController>() {
         @Mock
@@ -140,15 +162,16 @@ public class TestProviderQpsFlowControlHandler {
         }
       };
 
-      new MockUp<ProviderQpsControllerManager>() {
+      new MockUp<QpsControllerManager>() {
 
         @Mock
-        protected QpsController create(String serviceName) {
-          return new QpsController(serviceName, 12);
+        protected QpsController create(String qualifiedNameKey) {
+          return new QpsController(qualifiedNameKey, 12);
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);

-- 
To stop receiving notification emails like this one, please contact
liubao@apache.org.