You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/03/06 15:14:37 UTC

[GitHub] yhs0092 closed pull request #568: [SCB-352] Support provider operation level flow control

yhs0092 closed pull request #568: [SCB-352] Support provider operation level flow control
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/568
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/handlers/handler-flowcontrol-qps/pom.xml b/handlers/handler-flowcontrol-qps/pom.xml
index e9c10bdd1..86a3943ec 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/AbstractQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/AbstractQpsControllerManager.java
new file mode 100644
index 000000000..b9a0b2c19
--- /dev/null
+++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/AbstractQpsControllerManager.java
@@ -0,0 +1,176 @@
+/*
+ * 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 org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.config.DynamicProperty;
+
+public class AbstractQpsControllerManager {
+  private static final Logger LOGGER = LoggerFactory.getLogger(AbstractQpsControllerManager.class);
+
+  /**
+   * 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, qualifiedNameKey -> create(qualifiedNameKey));
+  }
+
+  protected QpsController create(String 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);
+  }
+
+  /**
+   * Try to find a valid qps controller by qualifiedNameKey.
+   *
+   * @param qualifiedNameKey qualifiedNameKey in {@link #qualifiedNameControllerMap}
+   * @return a valid qps controller, lower level controllers with valid qpsLimit have priority.
+   */
+  protected QpsController searchQpsController(String qualifiedNameKey) {
+    QpsController qpsController = searchByKey(qualifiedNameKey);
+
+    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;
+  }
+
+  /**
+   * 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".
+   *
+   * @param qualifiedNameKey qualifiedNameKey in {@link #qualifiedNameControllerMap}
+   * @return a qps controller, lower level controllers with valid qpsLimit have priority.
+   * null will be returned only when {@link #configQpsControllerMap} contains no matched qualifiedNameKey.
+   */
+  protected QpsController searchByKey(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);
+    }
+
+    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 AbstractQpsControllerManager setConfigKeyPrefix(String configKeyPrefix) {
+    this.configKeyPrefix = configKeyPrefix;
+    return this;
+  }
+
+  public AbstractQpsControllerManager 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/main/java/org/apache/servicecomb/qps/ConsumerQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerQpsControllerManager.java
index 076df492d..6b420fd4e 100644
--- 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
@@ -17,141 +17,41 @@
 
 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?????????????????
+ * <p>
+ * Use microservice.schema.operation as key.
+ * If there is only configuration of microservice level, then all invocations are mapped to the qpsController of this configuration.
+ * If there are configurations of schema and operation level, then the corresponding invocations is mapped to the qpsController
+ * separately.
+ * </p>
  *
+ * <p>
+ * When qpsController of schema level counts, the qpsController of microservice level won't count.
+ * It's the same as operation level qpsController.
+ * i.e. the statistics are only taken in qpsController internally. And between the qpsControllers there is no relationship.
+ * </p>
  */
-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;
-  }
+public class ConsumerQpsControllerManager extends AbstractQpsControllerManager {
+  private static volatile ConsumerQpsControllerManager INSTANCE;
 
-  private QpsController findReference(String key) {
-    QpsController qpsController = qpsControllerMap.get(key);
-    if (qpsController == null) {
-      return null;
-    }
-
-    if (qpsController.getQpsLimit() == null) {
-      return null;
-    }
-
-    return qpsController;
+  private ConsumerQpsControllerManager() {
   }
 
-  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;
+  public static ConsumerQpsControllerManager getINSTANCE() {
+    if (null == INSTANCE) {
+      synchronized (ConsumerQpsControllerManager.class) {
+        if (null == INSTANCE) {
+          INSTANCE = new ConsumerQpsControllerManager();
+        }
+      }
     }
-
-    return initQpsLimit(operationMeta.getMicroserviceName(), Integer.MAX_VALUE);
+    return INSTANCE;
   }
 
   @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;
-    }
+  protected DynamicProperty getDynamicProperty(String configKey) {
+    return DynamicProperty.getInstance(Config.CONSUMER_LIMIT_KEY_PREFIX + configKey);
   }
 }
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 b1fd2a669..785dfb123 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,16 @@
 
 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();
+  private ConsumerQpsControllerManager qpsControllerMgr = ConsumerQpsControllerManager.getINSTANCE();
 
   @Override
   public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
@@ -39,10 +37,10 @@ public void handle(Invocation invocation, AsyncResponse asyncResp) throws Except
       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
index d8d6c76ac..36093e4ce 100644
--- 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
@@ -17,127 +17,28 @@
 
 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;
+public class ProviderQpsControllerManager extends AbstractQpsControllerManager {
+  private static volatile ProviderQpsControllerManager INSTANCE;
 
-  @Override
-  protected String getKey(String microServiceName) {
-    return microServiceName;
+  private ProviderQpsControllerManager() {
+    setGlobalQpsController(Config.PROVIDER_LIMIT_KEY_GLOBAL);
   }
 
-  @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));
-            });
-          }
+  public static ProviderQpsControllerManager getINSTANCE() {
+    if (null == INSTANCE) {
+      synchronized (ProviderQpsControllerManager.class) {
+        if (null == INSTANCE) {
+          INSTANCE = new ProviderQpsControllerManager();
         }
       }
-      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;
+    return INSTANCE;
   }
 
   @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;
-    }
+  protected DynamicProperty getDynamicProperty(String configKey) {
+    return DynamicProperty.getInstance(Config.PROVIDER_LIMIT_KEY_PREFIX + configKey);
   }
 }
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 1cf669d81..ae8e03c99 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,10 @@
 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();
+  private ProviderQpsControllerManager qpsControllerMgr = ProviderQpsControllerManager.getINSTANCE();
 
   @Override
   public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
@@ -34,16 +35,12 @@ public void handle(Invocation invocation, AsyncResponse asyncResp) throws Except
       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 + invocation.getOperationMeta().getSchemaQualifiedName());
+    if (isLimitNewRequest(qpsController, asyncResp)) {
       return;
     }
 
diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/AbstractQpsControllerManagerTest.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/AbstractQpsControllerManagerTest.java
new file mode 100644
index 000000000..9004ecdb4
--- /dev/null
+++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/AbstractQpsControllerManagerTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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 AbstractQpsControllerManagerTest {
+
+  @Before
+  public void beforeTest() {
+    ArchaiusUtils.resetConfig();
+  }
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+  }
+
+  @Test
+  public void testGetOrCreate() {
+    AbstractQpsControllerManager testQpsControllerManager = new AbstractQpsControllerManager();
+    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() {
+    AbstractQpsControllerManager testQpsControllerManager = new AbstractQpsControllerManager()
+        .setGlobalQpsController(Config.PROVIDER_LIMIT_KEY_GLOBAL);
+
+    // 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);
+  }
+
+  private void testGetOrCreateCommon(AbstractQpsControllerManager 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(AbstractQpsControllerManager 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 + AbstractQpsControllerManager.SEPARATOR + operationId);
+    Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn(
+        microserviceName + AbstractQpsControllerManager.SEPARATOR + schemaId + AbstractQpsControllerManager.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(AbstractQpsControllerManager 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
index 426f7f8db..35aa4706d 100644
--- 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
@@ -17,59 +17,89 @@
 
 package org.apache.servicecomb.qps;
 
-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 mockit.Expectations;
-import mockit.Mocked;
-
-/**
- *
- *
- */
 public class TestConsumerQpsControllermanager {
-  private static String microserviceName = "pojo";
+  private static final String MICROSERVICE_NAME = "pojo";
+
+  public static final String SCHEMA_ID = "server";
 
-  private static String schemaQualified = microserviceName + ".server";
+  private static final String SCHEMA_QUALIFIED = MICROSERVICE_NAME + AbstractQpsControllerManager.SEPARATOR + SCHEMA_ID;
 
-  private static String operationQualified = schemaQualified + ".test";
+  public static final String OPERATION_ID = "test";
+
+  private static final String OPERATION_QUALIFIED =
+      SCHEMA_QUALIFIED + AbstractQpsControllerManager.SEPARATOR + OPERATION_ID;
+
+  @Before
+  public void beforeTest() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ConsumerQpsControllerManager.getINSTANCE());
+  }
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ConsumerQpsControllerManager.getINSTANCE());
+  }
 
   @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);
+  public void testQpsLimit() {
+    String qualifiedKey = MICROSERVICE_NAME + AbstractQpsControllerManager.SEPARATOR + SCHEMA_ID
+        + AbstractQpsControllerManager.SEPARATOR + OPERATION_ID;
+
+    ConsumerQpsControllerManager mgr = ConsumerQpsControllerManager.getINSTANCE();
+    QpsController qpsController = mgr.getOrCreate(qualifiedKey);
+    Assert.assertNull(qpsController.getQpsLimit());
+    Assert.assertEquals(MICROSERVICE_NAME, qpsController.getKey());
+
+    doTestQpsLimit(mgr, qualifiedKey, MICROSERVICE_NAME, 100, MICROSERVICE_NAME, 100);
+    doTestQpsLimit(mgr, qualifiedKey, SCHEMA_QUALIFIED, 200, SCHEMA_QUALIFIED, 200);
+    doTestQpsLimit(mgr, qualifiedKey, OPERATION_QUALIFIED, 300, OPERATION_QUALIFIED, 300);
+    doTestQpsLimit(mgr, qualifiedKey, OPERATION_QUALIFIED, null, SCHEMA_QUALIFIED, 200);
+    doTestQpsLimit(mgr, qualifiedKey, SCHEMA_QUALIFIED, null, MICROSERVICE_NAME, 100);
+    doTestQpsLimit(mgr, qualifiedKey, MICROSERVICE_NAME, null, MICROSERVICE_NAME, null);
   }
 
-  private void doTestQpsLimit(ConsumerQpsControllerManager mgr, OperationMeta operationMeta, String key,
+  private void doTestQpsLimit(ConsumerQpsControllerManager mgr, String qualifiedKey, 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());
+    QpsController qpsController = mgr.getOrCreate(qualifiedKey);
     Assert.assertEquals(expectKey, qpsController.getKey());
+    Assert.assertEquals(expectValue, qpsController.getQpsLimit());
+  }
+
+  @Test
+  public void testQpsLimitOn2Operation() {
+    // qualifiedKey0 is pojo.server.test
+    // qualifiedKey1 is pojo.server.test1
+    String qualifiedKey0 = MICROSERVICE_NAME + AbstractQpsControllerManager.SEPARATOR + SCHEMA_ID
+        + AbstractQpsControllerManager.SEPARATOR + OPERATION_ID;
+    String qualifiedKey1 = MICROSERVICE_NAME + AbstractQpsControllerManager.SEPARATOR + SCHEMA_ID
+        + AbstractQpsControllerManager.SEPARATOR + OPERATION_ID + "1";
+
+    ConsumerQpsControllerManager mgr = ConsumerQpsControllerManager.getINSTANCE();
+    QpsController qpsController = mgr.getOrCreate(qualifiedKey0);
+    Assert.assertNull(qpsController.getQpsLimit());
+    Assert.assertEquals(MICROSERVICE_NAME, qpsController.getKey());
+
+    qpsController = mgr.getOrCreate(qualifiedKey1);
+    Assert.assertNull(qpsController.getQpsLimit());
+    Assert.assertEquals(MICROSERVICE_NAME, qpsController.getKey());
+
+    // As operationMeta0 and operationMeta1 belong to the same schema, once the qps configuration of the schema level
+    // is changed, both of their qpsControllers should be changed.
+    Utils.updateProperty(Config.CONSUMER_LIMIT_KEY_PREFIX + SCHEMA_QUALIFIED, 200);
+    qpsController = mgr.getOrCreate(qualifiedKey0);
+    Assert.assertEquals(Integer.valueOf(200), qpsController.getQpsLimit());
+    Assert.assertEquals(SCHEMA_QUALIFIED, qpsController.getKey());
+    qpsController = mgr.getOrCreate(qualifiedKey1);
+    Assert.assertEquals(Integer.valueOf(200), qpsController.getQpsLimit());
+    Assert.assertEquals(SCHEMA_QUALIFIED, 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 368459e19..854854412 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,6 +17,8 @@
 
 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.swagger.invocation.AsyncResponse;
@@ -24,13 +26,10 @@
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import mockit.Deencapsulation;
 import mockit.Mock;
 import mockit.MockUp;
 
-/**
- *
- *
- */
 public class TestConsumerQpsFlowControlHandler {
 
   ConsumerQpsFlowControlHandler handler = new ConsumerQpsFlowControlHandler();
@@ -42,7 +41,7 @@
   OperationMeta operationMeta = Mockito.mock(OperationMeta.class);
 
   @Test
-  public void testQpsController() throws Exception {
+  public void testQpsController() {
     QpsController qpsController = new QpsController("abc", 100);
     Assert.assertEquals(false, qpsController.isLimitNewRequest());
 
@@ -68,9 +67,11 @@ public void testHandle() {
     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
         public boolean isLimitNewRequest() {
@@ -82,11 +83,12 @@ public boolean isLimitNewRequest() {
 
         @Mock
         protected QpsController create(OperationMeta operationMeta) {
-          return new QpsController("key", 12);
+          return qpsController;
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -97,8 +99,11 @@ public void testHandleIsLimitNewRequestAsFalse() {
     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
@@ -111,13 +116,21 @@ public boolean isLimitNewRequest() {
 
         @Mock
         protected QpsController create(OperationMeta operationMeta) {
-          return new QpsController("key", 12);
+          return qpsController;
         }
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
   }
+
+  private void setQpsController(String key, QpsController qpsController) {
+    ConsumerQpsControllerManager 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
index c16f7092d..137e6d86d 100644
--- 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
@@ -17,27 +17,53 @@
 
 package org.apache.servicecomb.qps;
 
+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;
 
 public class TestProviderQpsControllermanager {
-  private static String microserviceName = "pojo";
+  @Before
+  public void beforeTest() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ProviderQpsControllerManager.getINSTANCE());
+  }
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ProviderQpsControllerManager.getINSTANCE());
+  }
 
   @Test
   public void testQpsLimit() {
-    ProviderQpsControllerManager mgr = new ProviderQpsControllerManager();
-    QpsController qpsController = mgr.getOrCreate(microserviceName);
+    String microserviceName = "pojo";
+
+    ProviderQpsControllerManager mgr = ProviderQpsControllerManager.getINSTANCE();
+    String schemaId = "server";
+    String operationId = "test";
+    String qualifiedKey = "pojo.server.test";
+    QpsController qpsController = mgr.getOrCreate(qualifiedKey);
     Assert.assertEquals(null, qpsController.getQpsLimit());
-    Assert.assertEquals(microserviceName, qpsController.getKey());
+    Assert.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, qpsController.getKey());
 
-    doTestQpsLimit(mgr, microserviceName, 100, microserviceName, 100);
-    doTestQpsLimit(mgr, microserviceName, null, microserviceName, null);
+    doTestQpsLimit(mgr, microserviceName, qualifiedKey, 100, microserviceName, 100);
+    String schemaKey = microserviceName + AbstractQpsControllerManager.SEPARATOR + schemaId;
+    doTestQpsLimit(mgr, schemaKey, qualifiedKey, 80, schemaKey, 80);
+    String operationKey =
+        microserviceName + AbstractQpsControllerManager.SEPARATOR + schemaId + AbstractQpsControllerManager.SEPARATOR
+            + operationId;
+    doTestQpsLimit(mgr, operationKey, qualifiedKey, 50, operationKey, 50);
+    doTestQpsLimit(mgr, operationKey, qualifiedKey, null, schemaKey, 80);
+    doTestQpsLimit(mgr, schemaKey, qualifiedKey, null, microserviceName, 100);
+    doTestQpsLimit(mgr, microserviceName, qualifiedKey, null, Config.PROVIDER_LIMIT_KEY_GLOBAL, 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);
+  private void doTestQpsLimit(ProviderQpsControllerManager mgr, String configKey, String qualifiedKey,
+      Integer newValue, String expectKey, Integer expectValue) {
+    Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + configKey, newValue);
+    QpsController qpsController = mgr.getOrCreate(qualifiedKey);
     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 40303db8e..41d05fe78 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 @@
 
   @Before
   public void setUP() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ProviderQpsControllerManager.getINSTANCE());
     Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1);
   }
 
+
+  @After
+  public void afterTest() {
+    ArchaiusUtils.resetConfig();
+    AbstractQpsControllerManagerTest.clearState(ProviderQpsControllerManager.getINSTANCE());
+  }
+
   @Test
   public void testGlobalQpsControl(final @Injectable Invocation invocation,
       final @Injectable AsyncResponse asyncResp) throws Exception {
@@ -52,6 +65,8 @@ public void testGlobalQpsControl(final @Injectable Invocation invocation,
       {
         invocation.getContext(Const.SRC_MICROSERVICE);
         result = "test";
+        invocation.getOperationMeta();
+        result = AbstractQpsControllerManagerTest.getMockOperationMeta("pojo", "server", "opr");
         asyncResp.producerFail((Throwable) any);
         result = new RuntimeException("test error");
       }
@@ -67,7 +82,7 @@ public void testGlobalQpsControl(final @Injectable Invocation invocation,
       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 void testGlobalQpsControl(final @Injectable Invocation invocation,
   }
 
   @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 void testHandleWithException() {
       handler.handle(invocation, asyncResp);
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -104,6 +120,8 @@ public void testHandle() {
     try {
       validAssert = true;
       Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test");
+      OperationMeta mockOperationMeta = AbstractQpsControllerManagerTest.getMockOperationMeta("pojo", "server", "opr");
+      Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta);
 
       new MockUp<QpsController>() {
         @Mock
@@ -121,6 +139,7 @@ protected QpsController create(String serviceName) {
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);
@@ -132,6 +151,9 @@ public void testHandleIsLimitNewRequestAsFalse() {
     try {
       validAssert = true;
       Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test");
+      OperationMeta mockOperationMeta = AbstractQpsControllerManagerTest
+          .getMockOperationMeta("pojo", "server", "opr");
+      Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta);
 
       new MockUp<QpsController>() {
         @Mock
@@ -149,6 +171,7 @@ protected QpsController create(String serviceName) {
       };
       handler.handle(invocation, asyncResp);
     } catch (Exception e) {
+      e.printStackTrace();
       validAssert = false;
     }
     Assert.assertTrue(validAssert);


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services