Repository: sermant-io/Sermant
Pull request #1660 (2024-11-07 03:03:08)

add rocketmq gray plugin workflow test
What type of PR is this?

UT tests

What this PR does / why we need it?

Function coverage test for mq grayscale.

Which issue(s) this PR fixes？

#1659

Does this PR introduce a user-facing change?

No

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sermant-io/Sermant/pull/1660?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sermant-io) Report
All modified and coverable lines are covered by tests :white_check_mark:

| [Flag](https://app.codecov.io/gh/sermant-io/Sermant/pull/1660/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sermant-io) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [unittests](https://app.codecov.io/gh/sermant-io/Sermant/pull/1660/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sermant-io) | `43.96% <ø> (-0.01%)` | `181.00 <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sermant-io#carryforward-flags-in-the-pull-request-comment) to find out more.

[see 1 file with indirect coverage changes](https://app.codecov.io/gh/sermant-io/Sermant/pull/1660/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sermant-io)


Patch:
diff --git a/.github/actions/common/mq-grayscale/rocketmq/action.yml b/.github/actions/common/mq-grayscale/rocketmq/action.yml
new file mode 100644
index 000000000..122d83f55
--- /dev/null
+++ b/.github/actions/common/mq-grayscale/rocketmq/action.yml
@@ -0,0 +1,59 @@
+name: "Rocketmq Message Gray Common operations"
+description: "Do something common for rocketmq message gray"
+runs:
+  using: "composite"
+  steps:
+    - name: Set up JDK ${{ env.javaVersion }}
+      uses: actions/setup-java@v4
+      with:
+        java-version: ${{ env.javaVersion }}
+        distribution: 'adopt'
+        cache: maven
+    - name: download agent
+      uses: actions/cache@v4
+      with:
+        path: sermant-agent-*/
+        key: ${{ runner.os }}-agent-${{ github.run_id }}
+    - name: get cse from cache
+      uses: actions/cache@v4
+      with:
+        path: Local-CSE-2.1.3-linux-amd64.zip
+        key: ${{ runner.os }}-local-cse
+        restore-keys: |
+          ${{ runner.os }}-local-cse
+    - name: start cse
+      shell: bash
+      run: |
+        export ROOT_PATH=$(pwd)
+        bash ./sermant-integration-tests/scripts/startCse.sh
+    - name: get rocketmq from cache
+      uses: actions/cache@v4
+      with:
+        path: rocketmq-all-5.1.4-bin-release.zip
+        key: ${{ runner.os }}-rocketmq-all-5.1.4-bin-release
+        restore-keys: |
+          ${{ runner.os }}-rocketmq-all-5.1.4-bin-release
+    - name: start rocketmq server
+      shell: bash
+      run: |
+        unzip rocketmq-all-5.1.4-bin-release.zip
+        sed -i 's/if \[\[ "$JAVA_MAJOR_VERSION" -lt "9" \]\]/if [ "$JAVA_MAJOR_VERSION" -lt "9" ]/g' rocketmq-all-5.1.4-bin-release/bin/runserver.sh
+        sed -i 's/-Xms4g -Xmx4g -Xmn2g/-Xms1g -Xmx1g -Xmn1g/g' rocketmq-all-5.1.4-bin-release/bin/runserver.sh
+        sed -i '22i enablePropertyFilter = true' rocketmq-all-5.1.4-bin-release/conf/broker.conf
+        nohup bash rocketmq-all-5.1.4-bin-release/bin/mqnamesrv &
+    - name: start rocketmq broker
+      shell: bash
+      run: |
+        sed -i 's/-Xms8g -Xmx8g -Xmn4g/-Xms1g -Xmx1g -Xmn1g/g' rocketmq-all-5.1.4-bin-release/bin/runbroker.sh
+        nohup bash rocketmq-all-5.1.4-bin-release/bin/mqbroker -n localhost:9876 -c rocketmq-all-5.1.4-bin-release/conf/broker.conf &
+    - name: cache dependencies
+      uses: actions/cache@v4
+      with:
+        path: ~/.m2/repository
+        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+        restore-keys: |
+          ${{ runner.os }}-maven-
+    - name: entry
+      uses: ./.github/actions/common/entry
+      with:
+        log-dir: ./logs/rocketmq-grayscale/common
diff --git a/.github/actions/common/plugin-change-check/action.yml b/.github/actions/common/plugin-change-check/action.yml
index 39cd81b57..cf88e6842 100644
--- a/.github/actions/common/plugin-change-check/action.yml
+++ b/.github/actions/common/plugin-change-check/action.yml
@@ -494,6 +494,22 @@ runs:
       shell: bash
       run: |
         echo "crossthreadTagTransmissionChanged=${{ steps.changed-tag-transmission-crossthread.outputs.changed }}" >> $GITHUB_ENV
+    - uses: ktamas77/has-changed-path@v1.0.3
+      id: changed-mq-grayscale-rocketmq
+      with:
+        paths: sermant-plugins/sermant-mq-grayscale/mq-config-common
+          sermant-plugins/sermant-mq-grayscale/mq-config-service
+          sermant-plugins/sermant-mq-grayscale/mq-grayscale-rocketmq-plugin
+          sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo
+          sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo
+          sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test
+          ./.github/workflows/message_gray_integration_test.yml
+          ./.github/actions/common/mq-grayscale/rocketmq
+          ./.github/actions/scenarios/mq-grayscale/rocketmq
+    - name: env mq-grayscale-rocketmq
+      shell: bash
+      run: |
+        echo "mqGrayscaleRocketMqChanged=${{ steps.changed-mq-grayscale-rocketmq.outputs.changed }}" >> $GITHUB_ENV
     - uses: ktamas77/has-changed-path@v1.0.3
       id: changed-dubbo-router-action
       with:
@@ -924,3 +940,10 @@ runs:
             ${{ steps.changed-common-action.outputs.changed }} == 'true' -o ${{ env.triggerPushEvent }} == 'true' ];then
          echo "enableXdsServicAction=true" >> $GITHUB_ENV
         fi
+
+        # ==========mq grayscale rocketmq is needed to test?==========
+        if [ ${{ env.mqGrayscaleRocketMqChanged }} == 'true' -o \
+            ${{ env.sermantAgentCoreChanged }} == 'true' -o \
+            ${{ steps.changed-common-action.outputs.changed }} == 'true' -o ${{ env.triggerPushEvent }} == 'true' ];then
+         echo "enableMqGrayscaleRocketMqAction=true" >> $GITHUB_ENV
+        fi
diff --git a/.github/actions/scenarios/database-write-prohibition/mongodb/action.yml b/.github/actions/scenarios/database-write-prohibition/mongodb/action.yml
index 9f7013444..407d1b7ea 100644
--- a/.github/actions/scenarios/database-write-prohibition/mongodb/action.yml
+++ b/.github/actions/scenarios/database-write-prohibition/mongodb/action.yml
@@ -11,7 +11,7 @@ runs:
       shell: bash
       run: |
         sudo apt-get install gnupg curl
-        curl -fsSL -k https://www.mongodb.org/static/pgp/server-7.0.asc | \
+        curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
            sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
            --dearmor
         echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
diff --git a/.github/actions/scenarios/mq-grayscale/rocketmq/action.yml b/.github/actions/scenarios/mq-grayscale/rocketmq/action.yml
new file mode 100644
index 000000000..93e090d38
--- /dev/null
+++ b/.github/actions/scenarios/mq-grayscale/rocketmq/action.yml
@@ -0,0 +1,86 @@
+name: "RocketMq Grayscale Test"
+description: "Auto test for rocketMq grayscale"
+runs:
+  using: "composite"
+  steps:
+    - name: package rocketmq grayscale tests
+      shell: bash
+      run: mvn package -Drocketmq-client.version=${{ matrix.rocketMqClientVersion }} -DskipTests --file sermant-integration-tests/mq-grayscale-rocketmq-test/pom.xml
+    - name: echo test model
+      shell: bash
+      run: |
+        echo "=======test-model======"-${{ matrix.test-model }}
+    - name: start base producer service
+      shell: bash
+      env:
+        dynamic.config.dynamicConfigType: KIE
+        dynamic.config.serverAddress: 127.0.0.1:30110
+        SERVICE_META_ENVIRONMENT: development
+      run: |
+        nohup java -javaagent:sermant-agent-${{ env.sermantVersion }}/agent/sermant-agent.jar=appName=grayscale-rocketmq-producer -jar \
+        sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/target/grayscale-rocketmq-producer-demo.jar > ${{ env.logDir }}/${{ matrix.rocketMqClientVersion }}-rocketmq-producer-base.log 2>&1 &
+    - name: start gray producer service
+      shell: bash
+      env:
+        dynamic.config.dynamicConfigType: KIE
+        dynamic.config.serverAddress: 127.0.0.1:30110
+        SERVICE_META_ENVIRONMENT: development
+        SERVICE_META_VERSION: 1.0.1
+      run: |
+        nohup java -javaagent:sermant-agent-${{ env.sermantVersion }}/agent/sermant-agent.jar=appName=grayscale-rocketmq-producer -Dserver.port=9040 -jar \
+        sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/target/grayscale-rocketmq-producer-demo.jar > ${{ env.logDir }}/${{ matrix.rocketMqClientVersion }}-rocketmq-producer-gray.log 2>&1 &
+    - name: start base consumer service
+      shell: bash
+      env:
+        dynamic.config.dynamicConfigType: KIE
+        dynamic.config.serverAddress: 127.0.0.1:30110
+        SERVICE_META_ENVIRONMENT: development
+      run: |
+        nohup java -javaagent:sermant-agent-${{ env.sermantVersion }}/agent/sermant-agent.jar=appName=grayscale-rocketmq-consumer -jar \
+        sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/target/grayscale-rocketmq-consumer-demo.jar > ${{ env.logDir }}/${{ matrix.rocketMqClientVersion }}-rocketmq-consumer-base.log 2>&1 &
+    - name: start gray consumer service
+      shell: bash
+      if: matrix.test-model == 'AUTO_BASE_GRAY_PULL' || matrix.test-model == 'AUTO_BASE_GRAY_LITE_PULL' || matrix.test-model == 'AUTO_BASE_GRAY_PUSH' || matrix.test-model == 'AUTO_EXC_BASE_GRAY' || matrix.test-model == 'BASE_BASE_GRAY' || matrix.test-model == 'BASE_EXC_BASE_GRAY'
+      env:
+        dynamic.config.dynamicConfigType: KIE
+        dynamic.config.serverAddress: 127.0.0.1:30110
+        SERVICE_META_ENVIRONMENT: development
+        SERVICE_META_VERSION: 1.0.1
+      run: |
+        nohup java -javaagent:sermant-agent-${{ env.sermantVersion }}/agent/sermant-agent.jar=appName=grayscale-rocketmq-consumer -Dserver.port=9010 -jar \
+        sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/target/grayscale-rocketmq-consumer-demo.jar > ${{ env.logDir }}/${{ matrix.rocketMqClientVersion }}-rocketmq-consumer-gray.log 2>&1 &
+    - name: waiting for service start for only base
+      shell: bash
+      if: matrix.test-model != 'AUTO_BASE_GRAY_PULL' && matrix.test-model != 'AUTO_BASE_GRAY_LITE_PULL' && matrix.test-model != 'AUTO_BASE_GRAY_PUSH' && matrix.test-model != 'AUTO_EXC_BASE_GRAY' && matrix.test-model != 'BASE_BASE_GRAY' && matrix.test-model != 'BASE_EXC_BASE_GRAY'
+      run: |
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9030/actuator/health 200
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9040/actuator/health 200
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9000/actuator/health 200
+    - name: waiting for service start for base and gray
+      shell: bash
+      if: matrix.test-model == 'AUTO_BASE_GRAY_PULL' || matrix.test-model == 'AUTO_BASE_GRAY_LITE_PULL' || matrix.test-model == 'AUTO_BASE_GRAY_PUSH' || matrix.test-model == 'AUTO_EXC_BASE_GRAY' || matrix.test-model == 'BASE_BASE_GRAY' || matrix.test-model == 'BASE_EXC_BASE_GRAY'
+      run: |
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9030/actuator/health 200
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9040/actuator/health 200
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9000/actuator/health 200
+        bash ./sermant-integration-tests/scripts/checkService.sh http://127.0.0.1:9010/actuator/health 200
+    - name: test message grayscale rocketmq
+      shell: bash
+      run: |
+        mvn test -Dgrayscale.rocketmq.integration.test.type=${{ matrix.test-model }} --file \
+        sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/pom.xml
+    - name: exit
+      if: always()
+      uses: ./.github/actions/common/exit
+      with:
+        processor-keyword: grayscale-rocketmq
+    - name: if failure then upload error log
+      uses: actions/upload-artifact@v3
+      if: ${{ failure() || cancelled() }}
+      with:
+        name: (test-for-grayscale-rocketmq)-(${{ matrix.test-model }}})-logs
+        path: |
+          ./*.log
+          ./logs/**/*.log
+        if-no-files-found: warn
+        retention-days: 2
diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRetryPolicy.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRetryPolicy.java
index e31abbf58..c4e031efd 100644
--- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRetryPolicy.java
+++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRetryPolicy.java
@@ -16,8 +16,6 @@
 
 package io.sermant.core.service.xds.entity;
 
-import java.util.List;
-
 /**
  * Xds Retry Policy information
  *
@@ -39,7 +37,12 @@ public class XdsRetryPolicy {
     /**
      * Specifies the conditions under which retry takes place
      */
-    private List<String> retryConditions;
+    private String retryOn;
+
+    /**
+     * Specifies the conditions under which retry takes place
+     */
+    private String retryHostPredicate;
 
     public long getMaxAttempts() {
         return maxAttempts;
@@ -57,11 +60,19 @@ public class XdsRetryPolicy {
         this.perTryTimeout = perTryTimeout;
     }
 
-    public List<String> getRetryConditions() {
-        return retryConditions;
+    public String getRetryOn() {
+        return retryOn;
+    }
+
+    public void setRetryOn(String retryOn) {
+        this.retryOn = retryOn;
+    }
+
+    public String getRetryHostPredicate() {
+        return retryHostPredicate;
     }
 
-    public void setRetryConditions(List<String> retryConditions) {
-        this.retryConditions = retryConditions;
+    public void setRetryHostPredicate(String retryHostPredicate) {
+        this.retryHostPredicate = retryHostPredicate;
     }
 }
diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java
index bbe71b730..4e750e6c3 100644
--- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java
+++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java
@@ -38,7 +38,6 @@ import io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort;
 import io.envoyproxy.envoy.extensions.filters.http.fault.v3.HTTPFault;
 import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
 import io.envoyproxy.envoy.type.v3.FractionalPercent;
-import io.sermant.core.common.CommonConstant;
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.service.xds.entity.XdsAbort;
 import io.sermant.core.service.xds.entity.XdsDelay;
@@ -69,7 +68,6 @@ import io.sermant.implement.service.xds.entity.DenominatorType;
 
 import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -266,12 +264,13 @@ public class RdsProtocolTransformer {
 
     private static XdsRetryPolicy parseRetryPolicy(RetryPolicy retryPolicy) {
         XdsRetryPolicy xdsRetryPolicy = new XdsRetryPolicy();
-        if (!StringUtils.isEmpty(retryPolicy.getRetryOn())) {
-            xdsRetryPolicy.setRetryConditions(Arrays.asList(retryPolicy.getRetryOn().split(CommonConstant.COMMA)));
-        }
+        xdsRetryPolicy.setRetryOn(retryPolicy.getRetryOn());
         xdsRetryPolicy.setMaxAttempts(retryPolicy.getHostSelectionRetryMaxAttempts());
         long perTryTimeout = Duration.ofSeconds(retryPolicy.getPerTryTimeout().getSeconds()).toMillis();
         xdsRetryPolicy.setPerTryTimeout(perTryTimeout);
+        if (retryPolicy.getRetryHostPredicateCount() != 0) {
+            xdsRetryPolicy.setRetryHostPredicate(retryPolicy.getRetryHostPredicate(0).getName());
+        }
         return xdsRetryPolicy;
     }
 
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/RegisterType.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieResponse.java
similarity index 63%
rename from sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/RegisterType.java
rename to sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieResponse.java
index 266ec46b5..47f437c15 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/RegisterType.java
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieResponse.java
@@ -14,22 +14,24 @@
  *   limitations under the License.
  */
 
-package io.sermant.discovery.config;
+package io.sermant.demo.grayscale.rocketmq.integration.support.entity;
+
+import java.util.List;
 
 /**
- * registry type, currently only SC is supported
+ * Kie response
  *
- * @author zhouss
- * @since 2024-11-29
+ * @author chengyouling
+ * @since 2024-10-30
  */
-public enum RegisterType {
-    /**
-     * ZK Registry
-     */
-    ZOOKEEPER,
+public class KieResponse {
+    private List<KieConfigEntity> data;
+
+    public List<KieConfigEntity> getData() {
+        return data;
+    }
 
-    /**
-     * NACOS Registry
-     */
-    NACOS
+    public void setData(List<KieConfigEntity> data) {
+        this.data = data;
+    }
 }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/AbstractRetry.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/AbstractRetry.java
index 83ed5af6e..d0ca324c2 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/AbstractRetry.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/AbstractRetry.java
@@ -19,12 +19,8 @@ package io.sermant.flowcontrol.common.handler.retry;
 
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
-import io.sermant.core.utils.CollectionUtils;
 import io.sermant.flowcontrol.common.config.FlowControlConfig;
 import io.sermant.flowcontrol.common.support.ReflectMethodCacheSupport;
-import io.sermant.flowcontrol.common.xds.retry.RetryCondition;
-import io.sermant.flowcontrol.common.xds.retry.RetryConditionType;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -67,7 +63,7 @@ public abstract class AbstractRetry extends ReflectMethodCacheSupport implements
     }
 
     @Override
-    public boolean isNeedRetry(Set<String> statusList, Object result) {
+    public boolean needRetry(Set<String> statusList, Object result) {
         if (result == null) {
             return false;
         }
@@ -75,54 +71,8 @@ public abstract class AbstractRetry extends ReflectMethodCacheSupport implements
         return code.filter(statusList::contains).isPresent();
     }
 
-    @Override
-    public boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy) {
-        if (result == null) {
-            return false;
-        }
-        List<String> conditions = retryPolicy.getRetryConditions();
-        if (CollectionUtils.isEmpty(conditions)) {
-            return false;
-        }
-        Optional<String> statusCodeOptional = this.getCode(result);
-        if (!statusCodeOptional.isPresent()) {
-            return false;
-        }
-        String statusCode = statusCodeOptional.get();
-        if (conditions.contains(statusCode)) {
-            return true;
-        }
-        for (String conditionName : conditions) {
-            Optional<RetryCondition> retryConditionOptional = RetryConditionType.getRetryConditionByName(conditionName);
-            if (!retryConditionOptional.isPresent()) {
-                continue;
-            }
-            if (retryConditionOptional.get().needRetry(this, null, statusCode, result)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isNeedRetry(Throwable ex, XdsRetryPolicy retryPolicy) {
-        if (ex == null) {
-            return false;
-        }
-        for (String conditionName : retryPolicy.getRetryConditions()) {
-            Optional<RetryCondition> retryConditionOptional = RetryConditionType.getRetryConditionByName(conditionName);
-            if (!retryConditionOptional.isPresent()) {
-                continue;
-            }
-            if (retryConditionOptional.get().needRetry(null, ex, null, null)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
-     * implemented by subclasses， if subclass implement {@link #isNeedRetry(Set, Object)}, no need to implement the get
+     * implemented by subclasses， if subclass implement {@link #needRetry(Set, Object)}, no need to implement the get
      * code method
      *
      * @param result interface response result
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/Retry.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/Retry.java
index ffb2139fb..e3db64144 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/Retry.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/handler/retry/Retry.java
@@ -17,8 +17,6 @@
 
 package io.sermant.flowcontrol.common.handler.retry;
 
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
-
 import java.util.Optional;
 import java.util.Set;
 
@@ -30,33 +28,13 @@ import java.util.Set;
  */
 public interface Retry {
     /**
-     * Retry based on the request result. Retrying is required if the request status is in the statusList.
+     * needToRetry
      *
      * @param statusList List of status codes, valid only for http applications
      * @param result responseResult
      * @return retryOrNot
      */
-    boolean isNeedRetry(Set<String> statusList, Object result);
-
-    /**
-     * Retry based on the request result. If the request result meets the retry conditions in the retry policy,
-     * a retry will be executed
-     *
-     * @param result responseResult
-     * @param retryPolicy retry policy information
-     * @return retryOrNot
-     */
-    boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy);
-
-    /**
-     * Retry based on the throwable. If the throwable during the execution of the request method meets the retry
-     * conditions in the retry policy, a retry will be executed
-     *
-     * @param throwable Exception thrown during retry
-     * @param retryPolicy Xds Retry Policy information
-     * @return retryOrNot
-     */
-    boolean isNeedRetry(Throwable throwable, XdsRetryPolicy retryPolicy);
+    boolean needRetry(Set<String> statusList, Object result);
 
     /**
      * define which exceptions need to be retried
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsRouterUtils.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsRouterUtils.java
index b9ac6301e..517f3a01d 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsRouterUtils.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsRouterUtils.java
@@ -55,13 +55,12 @@ public class XdsRouterUtils {
      */
     public static Optional<XdsLocality> getLocalityInfoOfSelfService() {
         if (localityObtainedFlag) {
-            return Optional.ofNullable(selfServiceLocality);
+            return Optional.of(selfServiceLocality);
         }
         synchronized (XdsRouterUtils.class) {
             if (localityObtainedFlag) {
-                return Optional.ofNullable(selfServiceLocality);
+                return Optional.of(selfServiceLocality);
             }
-            localityObtainedFlag = true;
             String podIp = NetworkUtils.getKubernetesPodIp();
             if (StringUtils.isEmpty(podIp)) {
                 return Optional.empty();
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsThreadLocalUtil.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsThreadLocalUtil.java
index 25627b3ba..f6912bf84 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsThreadLocalUtil.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/util/XdsThreadLocalUtil.java
@@ -18,8 +18,6 @@ package io.sermant.flowcontrol.common.util;
 
 import io.sermant.flowcontrol.common.entity.FlowControlScenario;
 
-import java.net.HttpURLConnection;
-
 /**
  * xds thread local utility class
  *
@@ -31,10 +29,6 @@ public class XdsThreadLocalUtil {
 
     private static final ThreadLocal<FlowControlScenario> FLOW_CONTROL_SCENARIO_THREAD_LOCAL = new ThreadLocal<>();
 
-    private static final ThreadLocal<HttpURLConnection> CONNECTION_IN_CURRENT_THREAD = new ThreadLocal<>();
-
-    private static final ThreadLocal<Boolean> IS_CONNECTED = new ThreadLocal<>();
-
     private XdsThreadLocalUtil() {
     }
 
@@ -63,31 +57,6 @@ public class XdsThreadLocalUtil {
         SEND_BYTE_FLAG.remove();
     }
 
-    /**
-     * save the instance object of HttpURLConnection
-     *
-     * @param connection the instance object of HttpURLConnection
-     */
-    public static void saveHttpUrlConnection(HttpURLConnection connection) {
-        CONNECTION_IN_CURRENT_THREAD.set(connection);
-    }
-
-    /**
-     * get the instance object of HttpURLConnection
-     *
-     * @return the instance object of HttpURLConnection
-     */
-    public static HttpURLConnection getHttpUrlConnection() {
-        return CONNECTION_IN_CURRENT_THREAD.get();
-    }
-
-    /**
-     * remove the instance object of HttpURLConnection
-     */
-    public static void removeHttpUrlConnection() {
-        CONNECTION_IN_CURRENT_THREAD.remove();
-    }
-
     /**
      * Set scenario information
      *
@@ -112,29 +81,4 @@ public class XdsThreadLocalUtil {
     public static void removeScenarioInfo() {
         FLOW_CONTROL_SCENARIO_THREAD_LOCAL.remove();
     }
-
-    /**
-     * Set the connection status of the httpUrlConnection
-     *
-     * @param executeStatus the execution status of the connect method, true: executed, false: Not executed
-     */
-    public static void setConnectionStatus(boolean executeStatus) {
-        IS_CONNECTED.set(executeStatus);
-    }
-
-    /**
-     * Is it already connected
-     *
-     * @return connection status
-     */
-    public static boolean isConnected() {
-        return IS_CONNECTED.get() != null && IS_CONNECTED.get();
-    }
-
-    /**
-     * remove the connection status of the httpUrlConnection
-     */
-    public static void removeConnectionStatus() {
-        IS_CONNECTED.remove();
-    }
 }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/xds/circuit/XdsCircuitBreakerManager.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/xds/circuit/XdsCircuitBreakerManager.java
index c8c720890..dc3cc8186 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/xds/circuit/XdsCircuitBreakerManager.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/main/java/io/sermant/flowcontrol/common/xds/circuit/XdsCircuitBreakerManager.java
@@ -100,7 +100,7 @@ public class XdsCircuitBreakerManager {
         XdsCircuitBreakerInfo circuitBreakerInfo = getCircuitBreakerInfo(scenarioInfo.getServiceName(),
                 scenarioInfo.getRouteName(), scenarioInfo.getAddress());
         if (!XdsThreadLocalUtil.getSendByteFlag() && circuitBreakers.isSplitExternalLocalOriginErrors()
-                && shouldCircuitBreakerByFailure(circuitBreakerInfo.getLocalFailure(),
+                && shouldCircuitBreakerByFailure(circuitBreakerInfo.getGateWayFailure(),
                 circuitBreakers.getConsecutiveLocalOriginFailure(), circuitBreakers.getInterval())) {
             openCircuitBreaker(circuitBreakerInfo, circuitBreakers.getInterval());
         }
@@ -178,7 +178,7 @@ public class XdsCircuitBreakerManager {
         if (failureRequestThreshold <= 0) {
             return;
         }
-        for (int i = times.size(); i >= failureRequestThreshold && !times.isEmpty(); i--) {
+        for (int i = times.size(); i >= failureRequestThreshold - 1 && !times.isEmpty(); i--) {
             times.removeFirst();
         }
         times.add(currentTime);
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/AbstractXdsHttpClientInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/AbstractXdsHttpClientInterceptor.java
index 50e67f9d2..d05e2f345 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/AbstractXdsHttpClientInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/AbstractXdsHttpClientInterceptor.java
@@ -132,12 +132,6 @@ public abstract class AbstractXdsHttpClientInterceptor extends InterceptorSuppor
             if (!handlers.isEmpty() && needRetry(handlers.get(0), result, ex)) {
                 // execute retry logic
                 result = handlers.get(0).executeCheckedSupplier(retryFunc::get);
-                context.skip(result);
-                return;
-            }
-            if (ex != null) {
-                context.setThrowableOut(getRealCause(ex));
-                return;
             }
             context.skip(result);
         } catch (Throwable throwable) {
@@ -217,14 +211,10 @@ public abstract class AbstractXdsHttpClientInterceptor extends InterceptorSuppor
      */
     protected Optional<ServiceInstance> chooseServiceInstanceForXds() {
         FlowControlScenario scenarioInfo = XdsThreadLocalUtil.getScenarioInfo();
-        if (scenarioInfo == null || StringUtils.isEmpty(scenarioInfo.getServiceName())) {
+        if (scenarioInfo == null || io.sermant.core.utils.StringUtils.isBlank(scenarioInfo.getServiceName())
+                || io.sermant.core.utils.StringUtils.isEmpty(scenarioInfo.getClusterName())) {
             return Optional.empty();
         }
-        if (StringUtils.isEmpty(scenarioInfo.getClusterName())) {
-            scenarioInfo.setClusterName(StringUtils.EMPTY);
-            return Optional.ofNullable(chooseServiceInstanceByLoadBalancer(
-                    XdsHandler.INSTANCE.getServiceInstanceByServiceName(scenarioInfo.getServiceName()), scenarioInfo));
-        }
         Set<ServiceInstance> serviceInstanceSet = XdsHandler.INSTANCE.
                 getMatchedServiceInstance(scenarioInfo.getServiceName(), scenarioInfo.getClusterName());
         if (serviceInstanceSet.isEmpty()) {
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/AlibabaDubboInvokerInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/AlibabaDubboInvokerInterceptor.java
index 9fdc29e1f..3f2d03b5f 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/AlibabaDubboInvokerInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/AlibabaDubboInvokerInterceptor.java
@@ -227,7 +227,7 @@ public class AlibabaDubboInvokerInterceptor extends InterceptorSupporter {
      */
     public static class AlibabaDubboRetry extends AbstractRetry {
         @Override
-        public boolean isNeedRetry(Set<String> statusList, Object result) {
+        public boolean needRetry(Set<String> statusList, Object result) {
             // dubbo does not support status codes
             return false;
         }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/ApacheDubboInvokerInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/ApacheDubboInvokerInterceptor.java
index 1c17dd1b3..9e71558d4 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/ApacheDubboInvokerInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/ApacheDubboInvokerInterceptor.java
@@ -247,7 +247,7 @@ public class ApacheDubboInvokerInterceptor extends InterceptorSupporter {
      */
     public static class ApacheDubboRetry extends AbstractRetry {
         @Override
-        public boolean isNeedRetry(Set<String> statusList, Object result) {
+        public boolean needRetry(Set<String> statusList, Object result) {
             // dubbo does not support status codes
             return false;
         }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/FeignRequestInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/FeignRequestInterceptor.java
index c285eddcb..8b0c31bd6 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/FeignRequestInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/FeignRequestInterceptor.java
@@ -21,7 +21,6 @@ import feign.Request;
 import feign.Response;
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.agent.entity.ExecuteContext;
-import io.sermant.core.utils.StringUtils;
 import io.sermant.flowcontrol.common.config.ConfigConst;
 import io.sermant.flowcontrol.common.entity.FlowControlResult;
 import io.sermant.flowcontrol.common.entity.FlowControlServiceMeta;
@@ -122,12 +121,9 @@ public class FeignRequestInterceptor extends InterceptorSupporter {
 
     private Request getRequest(ExecuteContext context) {
         final Request request = (Request) context.getArguments()[0];
-        String serviceName = FlowControlServiceMeta.getInstance().getServiceName();
-        if (StringUtils.isEmpty(serviceName)) {
-            return request;
-        }
         final HashMap<String, Collection<String>> headers = new HashMap<>(request.headers());
-        headers.put(ConfigConst.FLOW_REMOTE_SERVICE_NAME_HEADER_KEY, Collections.singletonList(serviceName));
+        headers.put(ConfigConst.FLOW_REMOTE_SERVICE_NAME_HEADER_KEY,
+                Collections.singletonList(FlowControlServiceMeta.getInstance().getServiceName()));
         final Request newRequest = Request
                 .create(request.method(), request.url(), headers, request.body(), request.charset());
         context.getArguments()[0] = newRequest;
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/HttpRequestInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/HttpRequestInterceptor.java
index 1422258bc..1936630b1 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/HttpRequestInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/HttpRequestInterceptor.java
@@ -20,7 +20,6 @@ package io.sermant.flowcontrol.retry;
 import io.github.resilience4j.retry.Retry;
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.agent.entity.ExecuteContext;
-import io.sermant.core.utils.StringUtils;
 import io.sermant.flowcontrol.common.config.ConfigConst;
 import io.sermant.flowcontrol.common.entity.FlowControlResult;
 import io.sermant.flowcontrol.common.entity.FlowControlServiceMeta;
@@ -89,11 +88,8 @@ public class HttpRequestInterceptor extends InterceptorSupporter {
     protected final ExecuteContext doBefore(ExecuteContext context) {
         final FlowControlResult flowControlResult = new FlowControlResult();
         final HttpRequest request = (HttpRequest) context.getObject();
-        String serviceName = FlowControlServiceMeta.getInstance().getServiceName();
-        if (!StringUtils.isEmpty(serviceName)) {
-            request.getHeaders().put(ConfigConst.FLOW_REMOTE_SERVICE_NAME_HEADER_KEY,
-                    Collections.singletonList(serviceName));
-        }
+        request.getHeaders().put(ConfigConst.FLOW_REMOTE_SERVICE_NAME_HEADER_KEY,
+                Collections.singletonList(FlowControlServiceMeta.getInstance().getServiceName()));
         final Optional<HttpRequestEntity> httpRequestEntity = convertToHttpEntity(request);
         if (!httpRequestEntity.isPresent()) {
             return context;
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpClient4xInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpClient4xInterceptor.java
index a9cbdab49..089cde001 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpClient4xInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpClient4xInterceptor.java
@@ -106,7 +106,6 @@ public class HttpClient4xInterceptor extends AbstractXdsHttpClientInterceptor {
         if (isNeedCircuitBreak()) {
             context.skip(new ErrorCloseableHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, MESSAGE,
                     httpRequest.getProtocolVersion()));
-            return context;
         }
 
         // Execute service invocation and retry logic
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionConnectInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionConnectInterceptor.java
index d539e87ca..d70e17a5d 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionConnectInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionConnectInterceptor.java
@@ -19,7 +19,6 @@ package io.sermant.flowcontrol.retry.client;
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.agent.entity.ExecuteContext;
 import io.sermant.core.service.xds.entity.ServiceInstance;
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
 import io.sermant.core.utils.CollectionUtils;
 import io.sermant.core.utils.MapUtils;
 import io.sermant.core.utils.ReflectUtils;
@@ -29,7 +28,6 @@ import io.sermant.flowcontrol.common.entity.FlowControlResult;
 import io.sermant.flowcontrol.common.entity.FlowControlScenario;
 import io.sermant.flowcontrol.common.entity.HttpRequestEntity;
 import io.sermant.flowcontrol.common.entity.RequestEntity;
-import io.sermant.flowcontrol.common.handler.retry.AbstractRetry;
 import io.sermant.flowcontrol.common.util.XdsRouterUtils;
 import io.sermant.flowcontrol.common.util.XdsThreadLocalUtil;
 
@@ -60,7 +58,8 @@ public class HttpUrlConnectionConnectInterceptor extends AbstractXdsHttpClientIn
      * Constructor
      */
     public HttpUrlConnectionConnectInterceptor() {
-        super(new HttpUrlConnectionRetry(), HttpUrlConnectionConnectInterceptor.class.getName());
+        super(new HttpUrlConnectionResponseStreamInterceptor.HttpUrlConnectionRetry(),
+                HttpUrlConnectionConnectInterceptor.class.getName());
     }
 
     @Override
@@ -99,13 +98,6 @@ public class HttpUrlConnectionConnectInterceptor extends AbstractXdsHttpClientIn
             return context;
         }
 
-        // Save the identifier for the executed connect method, and enhance the HttpURLConnection's getInputStream only
-        // after the connect method has been executed.
-        XdsThreadLocalUtil.setConnectionStatus(true);
-
-        // Save the HttpURLConnection to facilitate retrieving the response status code during retries
-        XdsThreadLocalUtil.saveHttpUrlConnection(connection);
-
         // Execute service invocation and retry logic
         executeWithRetryPolicy(context);
         return context;
@@ -218,26 +210,4 @@ public class HttpUrlConnectionConnectInterceptor extends AbstractXdsHttpClientIn
     private Proxy createProxy(URL newUrl) {
         return new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(newUrl.getHost(), newUrl.getPort()));
     }
-
-    /**
-     * Http url connection retry
-     *
-     * @since 2024-12-31
-     */
-    public static class HttpUrlConnectionRetry extends AbstractRetry {
-        @Override
-        public Class<? extends Throwable>[] retryExceptions() {
-            return getRetryExceptions();
-        }
-
-        @Override
-        public RetryFramework retryType() {
-            return RetryFramework.SPRING;
-        }
-
-        @Override
-        public boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy) {
-            return false;
-        }
-    }
 }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionDisconnectInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionDisconnectInterceptor.java
index 4bf752cce..f8aa44d67 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionDisconnectInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionDisconnectInterceptor.java
@@ -42,29 +42,12 @@ public class HttpUrlConnectionDisconnectInterceptor extends AbstractXdsHttpClien
 
     @Override
     public ExecuteContext doBefore(ExecuteContext context) throws Exception {
+        XdsThreadLocalUtil.removeSendByteFlag();
         HttpURLConnection httpUrlConnection = (HttpURLConnection) context.getObject();
         context.setLocalFieldValue(STATUS_CODE, httpUrlConnection.getResponseCode());
         return context;
     }
 
-    @Override
-    public ExecuteContext doAfter(ExecuteContext context) {
-        XdsThreadLocalUtil.removeConnectionStatus();
-        XdsThreadLocalUtil.removeHttpUrlConnection();
-        super.doAfter(context);
-        return context;
-    }
-
-    @Override
-    public ExecuteContext doThrow(ExecuteContext context) {
-        if (context.getThrowableOut() != null) {
-            XdsThreadLocalUtil.removeConnectionStatus();
-            XdsThreadLocalUtil.removeHttpUrlConnection();
-        }
-        super.doThrow(context);
-        return context;
-    }
-
     @Override
     protected int getStatusCode(ExecuteContext context) {
         Object statusCode = context.getLocalFieldValue(STATUS_CODE);
@@ -77,9 +60,4 @@ public class HttpUrlConnectionDisconnectInterceptor extends AbstractXdsHttpClien
     @Override
     protected void preRetry(Object obj, Method method, Object[] allArguments, Object result, boolean isFirstInvoke) {
     }
-
-    @Override
-    protected boolean canInvoke(ExecuteContext context) {
-        return true;
-    }
 }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionResponseStreamInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionResponseStreamInterceptor.java
index a1abf0110..8df205499 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionResponseStreamInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/HttpUrlConnectionResponseStreamInterceptor.java
@@ -19,16 +19,12 @@ package io.sermant.flowcontrol.retry.client;
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.agent.entity.ExecuteContext;
 import io.sermant.core.service.xds.entity.ServiceInstance;
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
-import io.sermant.core.utils.CollectionUtils;
 import io.sermant.core.utils.MapUtils;
 import io.sermant.core.utils.ReflectUtils;
 import io.sermant.flowcontrol.AbstractXdsHttpClientInterceptor;
 import io.sermant.flowcontrol.common.config.CommonConst;
 import io.sermant.flowcontrol.common.handler.retry.AbstractRetry;
 import io.sermant.flowcontrol.common.util.XdsThreadLocalUtil;
-import io.sermant.flowcontrol.common.xds.retry.RetryCondition;
-import io.sermant.flowcontrol.common.xds.retry.RetryConditionType;
 import sun.net.www.http.HttpClient;
 
 import java.io.Closeable;
@@ -64,8 +60,12 @@ public class HttpUrlConnectionResponseStreamInterceptor extends AbstractXdsHttpC
 
     @Override
     protected ExecuteContext doBefore(ExecuteContext context) throws Exception {
-        // Remove the status to prevent multiple executions of the same request due to enhanced logic
-        XdsThreadLocalUtil.removeConnectionStatus();
+        if (!(context.getObject() instanceof HttpURLConnection)) {
+            return context;
+        }
+        if (XdsThreadLocalUtil.getScenarioInfo() == null) {
+            return context;
+        }
         executeWithRetryPolicy(context);
         return context;
     }
@@ -91,24 +91,17 @@ public class HttpUrlConnectionResponseStreamInterceptor extends AbstractXdsHttpC
         }
     }
 
-    @Override
-    protected boolean canInvoke(ExecuteContext context) {
-        return XdsThreadLocalUtil.isConnected() && XdsThreadLocalUtil.getScenarioInfo() != null;
-    }
-
     @Override
     protected void preRetry(Object obj, Method method, Object[] allArguments, Object result, boolean isFirstInvoke) {
         tryCloseOldInputStream(result);
         if (isFirstInvoke) {
             return;
         }
-        HttpURLConnection connection = (HttpURLConnection) obj;
-        ReflectUtils.setFieldValue(connection,"inputStream", null);
-        ReflectUtils.setFieldValue(connection,"cachedInputStream", null);
         Optional<ServiceInstance> serviceInstanceOptional = chooseServiceInstanceForXds();
         if (!serviceInstanceOptional.isPresent()) {
             return;
         }
+        HttpURLConnection connection = (HttpURLConnection) obj;
         ServiceInstance instance = serviceInstanceOptional.get();
         resetStats(obj);
         URL url = connection.getURL();
@@ -179,7 +172,10 @@ public class HttpUrlConnectionResponseStreamInterceptor extends AbstractXdsHttpC
     public static class HttpUrlConnectionRetry extends AbstractRetry {
         @Override
         public Optional<String> getCode(Object result) {
-            HttpURLConnection connection = XdsThreadLocalUtil.getHttpUrlConnection();
+            if (!(result instanceof HttpURLConnection)) {
+                return Optional.empty();
+            }
+            HttpURLConnection connection = (HttpURLConnection) result;
             try {
                 return Optional.of(String.valueOf(connection.getResponseCode()));
             } catch (IOException io) {
@@ -190,7 +186,10 @@ public class HttpUrlConnectionResponseStreamInterceptor extends AbstractXdsHttpC
 
         @Override
         public Optional<Set<String>> getHeaderNames(Object result) {
-            HttpURLConnection connection = XdsThreadLocalUtil.getHttpUrlConnection();
+            if (!(result instanceof HttpURLConnection)) {
+                return Optional.empty();
+            }
+            HttpURLConnection connection = (HttpURLConnection) result;
             Set<String> headerNames = new HashSet<>();
             if (MapUtils.isEmpty(connection.getHeaderFields())) {
                 return Optional.empty();
@@ -202,35 +201,6 @@ public class HttpUrlConnectionResponseStreamInterceptor extends AbstractXdsHttpC
             return Optional.of(headerNames);
         }
 
-        @Override
-        public boolean isNeedRetry(Throwable throwable, XdsRetryPolicy retryPolicy) {
-            List<String> conditions = retryPolicy.getRetryConditions();
-            if (CollectionUtils.isEmpty(conditions)) {
-                return false;
-            }
-            Optional<String> statusCodeOptional = this.getCode(null);
-            if (!statusCodeOptional.isPresent()) {
-                return false;
-            }
-            String statusCode = statusCodeOptional.get();
-            for (String conditionName : conditions) {
-                Optional<RetryCondition> retryConditionOptional = RetryConditionType.
-                        getRetryConditionByName(conditionName);
-                if (!retryConditionOptional.isPresent()) {
-                    continue;
-                }
-                if (retryConditionOptional.get().needRetry(null, throwable, statusCode, null)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy) {
-            return this.isNeedRetry((Throwable) null, retryPolicy);
-        }
-
         @Override
         public Class<? extends Throwable>[] retryExceptions() {
             return getRetryExceptions();
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttp3ClientInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttp3ClientInterceptor.java
index a10955787..4a9b61161 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttp3ClientInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttp3ClientInterceptor.java
@@ -89,7 +89,6 @@ public class OkHttp3ClientInterceptor extends AbstractXdsHttpClientInterceptor {
         if (flowControlResult.isSkip()) {
             Response.Builder builder = new Response.Builder();
             context.skip(builder.code(flowControlResult.getResponse().getCode())
-                    .protocol(Protocol.HTTP_1_1)
                     .message(flowControlResult.buildResponseMsg()).request(request).build());
             return context;
         }
@@ -100,7 +99,6 @@ public class OkHttp3ClientInterceptor extends AbstractXdsHttpClientInterceptor {
             Response.Builder builder = new Response.Builder();
             context.skip(builder.code(CommonConst.INTERVAL_SERVER_ERROR)
                     .message(MESSAGE).request(request).protocol(Protocol.HTTP_1_1).build());
-            return context;
         }
 
         // Execute service invocation and retry logic
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttpClientInterceptorChainInterceptor.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttpClientInterceptorChainInterceptor.java
index f82895692..b6543cf4c 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttpClientInterceptorChainInterceptor.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/client/OkHttpClientInterceptorChainInterceptor.java
@@ -93,7 +93,6 @@ public class OkHttpClientInterceptorChainInterceptor extends AbstractXdsHttpClie
             Response.Builder builder = new Response.Builder();
             context.skip(builder.code(CommonConst.INTERVAL_SERVER_ERROR)
                     .message(MESSAGE).request(request).protocol(Protocol.HTTP_1_1).build());
-            return context;
         }
 
         // Execute service invocation and retry logic
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/AlibabaDubboClusterInvoker.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/AlibabaDubboClusterInvoker.java
index 181bea568..bca067c05 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/AlibabaDubboClusterInvoker.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/AlibabaDubboClusterInvoker.java
@@ -211,7 +211,7 @@ public class AlibabaDubboClusterInvoker<T> extends AbstractClusterInvoker<T> {
      */
     public static class AlibabaDubboRetry extends AbstractRetry {
         @Override
-        public boolean isNeedRetry(Set<String> statusList, Object result) {
+        public boolean needRetry(Set<String> statusList, Object result) {
             // dubbo does not support status codes
             return false;
         }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/ApacheDubboClusterInvoker.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/ApacheDubboClusterInvoker.java
index e9abdf7a2..85295d855 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/ApacheDubboClusterInvoker.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/cluster/ApacheDubboClusterInvoker.java
@@ -215,7 +215,7 @@ public class ApacheDubboClusterInvoker<T> extends AbstractClusterInvoker<T> {
      */
     public static class ApacheDubboRetry extends AbstractRetry {
         @Override
-        public boolean isNeedRetry(Set<String> statusList, Object result) {
+        public boolean needRetry(Set<String> statusList, Object result) {
             // dubbo does not support status codes
             return false;
         }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreator.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreator.java
index 77db1e659..9906120a0 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreator.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreator.java
@@ -18,10 +18,14 @@
 package io.sermant.flowcontrol.retry.handler;
 
 import io.sermant.core.service.xds.entity.XdsRetryPolicy;
+import io.sermant.core.utils.CollectionUtils;
 import io.sermant.core.utils.ReflectUtils;
+import io.sermant.core.utils.StringUtils;
 import io.sermant.flowcontrol.common.core.rule.RetryRule;
 import io.sermant.flowcontrol.common.exception.InvokerWrapperException;
 import io.sermant.flowcontrol.common.handler.retry.Retry;
+import io.sermant.flowcontrol.common.xds.retry.RetryCondition;
+import io.sermant.flowcontrol.common.xds.retry.RetryConditionType;
 
 import java.io.IOException;
 import java.net.ConnectException;
@@ -69,8 +73,9 @@ public class DefaultRetryPredicateCreator implements RetryPredicateCreator {
     }
 
     @Override
-    public Predicate<Throwable> createExceptionPredicate(Retry retry, XdsRetryPolicy policy) {
-        return (Throwable ex) -> retry.isNeedRetry(ex, policy);
+    public Predicate<Throwable> createExceptionPredicate(Class<? extends Throwable>[] retryExceptions,
+            XdsRetryPolicy policy) {
+        return (Throwable ex) -> needRetry(ex, policy);
     }
 
     private Predicate<Throwable> createExceptionPredicate(Class<? extends Throwable> retryClass) {
@@ -124,11 +129,60 @@ public class DefaultRetryPredicateCreator implements RetryPredicateCreator {
         if (retryOnResponseStatus.isEmpty()) {
             retryOnResponseStatus.addAll(DEFAULT_RETRY_ON_RESPONSE_STATUS);
         }
-        return result -> retry.isNeedRetry(new HashSet<>(retryOnResponseStatus), result);
+        return result -> retry.needRetry(new HashSet<>(retryOnResponseStatus), result);
     }
 
     @Override
     public Predicate<Object> createResultPredicate(Retry retry, XdsRetryPolicy xdsRetryPolicy) {
-        return result -> retry.isNeedRetry(result, xdsRetryPolicy);
+        return result -> needRetry(retry, result, xdsRetryPolicy);
+    }
+
+    private boolean needRetry(Retry retry, Object result, XdsRetryPolicy retryPolicy) {
+        List<String> conditions = getRetryConditions(retryPolicy);
+        if (CollectionUtils.isEmpty(conditions)) {
+            return false;
+        }
+        Optional<String> statusCodeOptional = retry.getCode(result);
+        if (!statusCodeOptional.isPresent()) {
+            return false;
+        }
+        String statusCode = statusCodeOptional.get();
+        if (conditions.contains(statusCode)) {
+            return true;
+        }
+        for (String conditionName : conditions) {
+            Optional<RetryCondition> retryConditionOptional = RetryConditionType.getRetryConditionByName(conditionName);
+            if (!retryConditionOptional.isPresent()) {
+                continue;
+            }
+            boolean needRetry = retryConditionOptional.get().needRetry(retry, null, statusCode, result);
+            if (needRetry) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean needRetry(Throwable ex, XdsRetryPolicy retryPolicy) {
+        List<String> conditions = getRetryConditions(retryPolicy);
+        for (String conditionName : conditions) {
+            Optional<RetryCondition> retryConditionOptional = RetryConditionType.getRetryConditionByName(conditionName);
+            if (!retryConditionOptional.isPresent()) {
+                continue;
+            }
+            boolean needRetry = retryConditionOptional.get().needRetry(null, ex, null, null);
+            if (needRetry) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static List<String> getRetryConditions(XdsRetryPolicy xdsRetryPolicy) {
+        String retryOn = xdsRetryPolicy.getRetryOn();
+        if (StringUtils.isExist(retryOn)) {
+            return Arrays.asList(retryOn.split(","));
+        }
+        return Collections.emptyList();
     }
 }
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryHandlerV2.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryHandlerV2.java
index 912ab850e..270d5c01b 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryHandlerV2.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryHandlerV2.java
@@ -22,7 +22,7 @@ import io.github.resilience4j.retry.Retry;
 import io.github.resilience4j.retry.RetryConfig;
 import io.github.resilience4j.retry.RetryRegistry;
 import io.sermant.core.service.xds.entity.XdsRetryPolicy;
-import io.sermant.core.utils.CollectionUtils;
+import io.sermant.core.utils.StringUtils;
 import io.sermant.flowcontrol.common.core.resolver.RetryResolver;
 import io.sermant.flowcontrol.common.core.rule.RetryRule;
 import io.sermant.flowcontrol.common.entity.FlowControlScenario;
@@ -55,14 +55,14 @@ public class RetryHandlerV2 extends AbstractRequestHandler<Retry, RetryRule> {
             return Optional.empty();
         }
         XdsRetryPolicy retryPolicy = retryPolicyOptional.get();
-        if (retryPolicy.getPerTryTimeout() <= 0 || CollectionUtils.isEmpty(retryPolicy.getRetryConditions())
+        if (retryPolicy.getPerTryTimeout() <= 0 || StringUtils.isEmpty(retryPolicy.getRetryOn())
                 || retryPolicy.getMaxAttempts() <= 0) {
             return Optional.empty();
         }
         final RetryConfig retryConfig = RetryConfig.custom()
                 .maxAttempts((int)retryPolicy.getMaxAttempts())
                 .retryOnResult(retryPredicateCreator.createResultPredicate(retry, retryPolicy))
-                .retryOnException(retryPredicateCreator.createExceptionPredicate(retry, retryPolicy))
+                .retryOnException(retryPredicateCreator.createExceptionPredicate(retry.retryExceptions(), retryPolicy))
                 .intervalFunction(IntervalFunction.of(retryPolicy.getPerTryTimeout()))
                 .failAfterMaxAttempts(false)
                 .build();
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryPredicateCreator.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryPredicateCreator.java
index cf2f93a8c..97f570592 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryPredicateCreator.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/main/java/io/sermant/flowcontrol/retry/handler/RetryPredicateCreator.java
@@ -41,11 +41,11 @@ public interface RetryPredicateCreator {
     /**
      * Create exception Predicate
      *
-     * @param retry retry
+     * @param retryExceptions retry exception set
      * @param policy retry rule
      * @return Predicate
      */
-    Predicate<Throwable> createExceptionPredicate(Retry retry, XdsRetryPolicy policy);
+    Predicate<Throwable> createExceptionPredicate(Class<? extends Throwable>[] retryExceptions, XdsRetryPolicy policy);
 
     /**
      * create retry result predicate
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsBusinessServerRequestHandler.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsBusinessServerRequestHandler.java
index fc8314fa8..a4bc5f6b9 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsBusinessServerRequestHandler.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsBusinessServerRequestHandler.java
@@ -16,10 +16,9 @@
 
 package io.sermant.flowcontrol.res4j.chain.handler;
 
-import io.sermant.core.config.ConfigManager;
-import io.sermant.core.plugin.config.ServiceMeta;
 import io.sermant.flowcontrol.common.core.match.XdsRouteMatchManager;
 import io.sermant.flowcontrol.common.entity.FlowControlScenario;
+import io.sermant.flowcontrol.common.entity.FlowControlServiceMeta;
 import io.sermant.flowcontrol.common.entity.RequestEntity;
 import io.sermant.flowcontrol.common.util.XdsThreadLocalUtil;
 import io.sermant.flowcontrol.res4j.chain.AbstractChainHandler;
@@ -33,8 +32,6 @@ import io.sermant.flowcontrol.res4j.chain.context.RequestContext;
  * @since 2024-12-05
  */
 public class XdsBusinessServerRequestHandler extends AbstractChainHandler {
-    private final ServiceMeta serviceMeta = ConfigManager.getConfig(ServiceMeta.class);
-
     @Override
     public void onBefore(RequestContext context, FlowControlScenario scenarioInfo) {
         if (XdsThreadLocalUtil.getScenarioInfo() != null) {
@@ -42,7 +39,7 @@ public class XdsBusinessServerRequestHandler extends AbstractChainHandler {
             return;
         }
         FlowControlScenario matchedScenarioEntity = XdsRouteMatchManager.INSTANCE.getMatchedScenarioInfo(
-                context.getRequestEntity(), serviceMeta.getService());
+                context.getRequestEntity(), FlowControlServiceMeta.getInstance().getServiceName());
         context.save(MATCHED_SCENARIO_NAMES, matchedScenarioEntity);
         XdsThreadLocalUtil.setScenarioInfo(matchedScenarioEntity);
         super.onBefore(context, matchedScenarioEntity);
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsFaultRequestHandler.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsFaultRequestHandler.java
index 874a72a6b..29eb8c65d 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsFaultRequestHandler.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsFaultRequestHandler.java
@@ -115,8 +115,7 @@ public class XdsFaultRequestHandler extends AbstractChainHandler {
         if (!XDS_FLOW_CONTROL_CONFIG.isEnable()) {
             return true;
         }
-        return scenarioInfo == null || StringUtils.isEmpty(scenarioInfo.getServiceName())
-                || StringUtils.isEmpty(scenarioInfo.getRouteName());
+        return StringUtils.isEmpty(scenarioInfo.getServiceName()) || StringUtils.isEmpty(scenarioInfo.getRouteName());
     }
 
     @Override
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsRateLimitRequestHandler.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsRateLimitRequestHandler.java
index 3b88353b7..6bd3bf5d0 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsRateLimitRequestHandler.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-service/src/main/java/io/sermant/flowcontrol/res4j/chain/handler/XdsRateLimitRequestHandler.java
@@ -91,8 +91,7 @@ public class XdsRateLimitRequestHandler extends AbstractChainHandler {
         if (!XDS_FLOW_CONTROL_CONFIG.isEnable()) {
             return true;
         }
-        return scenarioInfo == null || StringUtils.isEmpty(scenarioInfo.getServiceName())
-                || StringUtils.isEmpty(scenarioInfo.getRouteName());
+        return StringUtils.isEmpty(scenarioInfo.getServiceName()) || StringUtils.isEmpty(scenarioInfo.getRouteName());
     }
 
     @Override
diff --git a/sermant-plugins/sermant-router/router-transmit-plugin/src/main/java/io/sermant/router/transmit/wrapper/AbstractThreadWrapper.java b/sermant-plugins/sermant-router/router-transmit-plugin/src/main/java/io/sermant/router/transmit/wrapper/AbstractThreadWrapper.java
index ee048b0df..cbf2d81e3 100644
--- a/sermant-plugins/sermant-router/router-transmit-plugin/src/main/java/io/sermant/router/transmit/wrapper/AbstractThreadWrapper.java
+++ b/sermant-plugins/sermant-router/router-transmit-plugin/src/main/java/io/sermant/router/transmit/wrapper/AbstractThreadWrapper.java
@@ -45,8 +45,6 @@ public abstract class AbstractThreadWrapper<T> {
 
     private final boolean cannotTransmit;
 
-    private final long threadId;
-
     /**
      * Constructor
      *
@@ -68,7 +66,6 @@ public abstract class AbstractThreadWrapper<T> {
             this.requestData = requestData;
         }
         this.cannotTransmit = cannotTransmit;
-        this.threadId = Thread.currentThread().getId();
     }
 
     /**
@@ -99,9 +96,6 @@ public abstract class AbstractThreadWrapper<T> {
     }
 
     private void before(Object obj) {
-        if (threadId == Thread.currentThread().getId()) {
-            return;
-        }
         if (cannotTransmit) {
             // If you enable normal thread pass through but do not enable thread pool pass through,
             // you need to delete the data that is pass passed by InheritableThreadLocal before executing the method
@@ -123,9 +117,6 @@ public abstract class AbstractThreadWrapper<T> {
     }
 
     private void after() {
-        if (threadId == Thread.currentThread().getId()) {
-            return;
-        }
         ThreadLocalUtils.removeRequestTag();
         ThreadLocalUtils.removeRequestData();
     }
diff --git a/sermant-plugins/sermant-springboot-registry/config/config.yaml b/sermant-plugins/sermant-springboot-registry/config/config.yaml
index f2b0fa244..a19a3b0d3 100644
--- a/sermant-plugins/sermant-springboot-registry/config/config.yaml
+++ b/sermant-plugins/sermant-springboot-registry/config/config.yaml
@@ -7,8 +7,6 @@ sermant.springboot.registry:
   realmName: www.domain.com
   # Whether to enable traffic statistics, after it is enabled, the traffic entering the plug-in will be printed every time
   enableRequestCount: false
-  # The type of the registry center, currently supports NACOS and ZOOKEEPER
-  registryCenterType: ZOOKEEPER
 
 sermant.springboot.registry.lb:
   # The load balancer type currently supports RoundRobin、Random、WeightedResponseTime、
@@ -30,21 +28,3 @@ sermant.springboot.registry.lb:
   # For retry scenarios, for {@link java.util.concurrent.TimeoutException}, whether you need to retry, which is enabled
   # by default, and this timeout is mostly used in asynchronous scenarios, such as Future, MinimalHttpAsyncClient
   enableTimeoutExRetry: true
-
-nacos.config:
-  # Whether to enable authentication
-  enableAuth: false
-  # nacos verifies accounts
-  username: ""
-  # nacos verifies password need encrypts
-  password: ""
-  # Specify the AES privateKey that encrypts and decrypts the password
-  privateKey: ""
-  # Namespace, the nacos configuration creates the id value of the namespace
-  namespace: ""
-  # Service instance weight value
-  weight: 1
-  # Cluster name
-  clusterName: DEFAULT
-  # Whether it is a temporary node. The value can be true or false
-  ephemeral: true
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/DiscoveryPluginConfig.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/DiscoveryPluginConfig.java
index 76d57835b..42fa100df 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/DiscoveryPluginConfig.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/DiscoveryPluginConfig.java
@@ -42,11 +42,6 @@ public class DiscoveryPluginConfig implements PluginConfig {
      */
     private boolean enableRegistry = true;
 
-    /**
-     * Registry type, currently supports NACOS and ZOOKEEPER;
-     */
-    private RegisterType registerType = RegisterType.ZOOKEEPER;
-
     public boolean isEnableRegistry() {
         return enableRegistry;
     }
@@ -70,12 +65,4 @@ public class DiscoveryPluginConfig implements PluginConfig {
     public void setEnableRequestCount(boolean enableRequestCount) {
         this.enableRequestCount = enableRequestCount;
     }
-
-    public RegisterType getRegisterType() {
-        return registerType;
-    }
-
-    public void setRegisterType(RegisterType registerType) {
-        this.registerType = registerType;
-    }
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/LbConfig.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/LbConfig.java
index 62c7a1f7e..e76887ab8 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/LbConfig.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/LbConfig.java
@@ -69,6 +69,11 @@ public class LbConfig implements PluginConfig {
      */
     private String registryAddress = "127.0.0.1:2181";
 
+    /**
+     * Registry type, currently only ZK is supported
+     */
+    private String registryCenterType = "Zookeeper";
+
     /**
      * When it is enabled, if other registries are the same as the registries of this plugin and are registered to the
      * registry at the same time, the registries that are not registered by the plugin will be automatically excluded
@@ -304,6 +309,14 @@ public class LbConfig implements PluginConfig {
         this.onlyCurRegisterInstances = onlyCurRegisterInstances;
     }
 
+    public String getRegistryCenterType() {
+        return registryCenterType;
+    }
+
+    public void setRegistryCenterType(String registryCenterType) {
+        this.registryCenterType = registryCenterType;
+    }
+
     public String getZkServerVersion() {
         return zkServerVersion;
     }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/NacosRegisterConfig.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/NacosRegisterConfig.java
deleted file mode 100644
index bb1d40357..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/NacosRegisterConfig.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.config;
-
-import io.sermant.core.common.LoggerFactory;
-import io.sermant.core.config.ConfigManager;
-import io.sermant.core.config.common.ConfigTypeKey;
-import io.sermant.core.plugin.config.PluginConfig;
-import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.core.plugin.config.ServiceMeta;
-import io.sermant.core.utils.AesUtil;
-import io.sermant.core.utils.StringUtils;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * nacos Register plug-in configuration
- *
- * @author xiaozhao
- * @since 2024-11-16
- */
-@ConfigTypeKey(value = "nacos.config")
-public class NacosRegisterConfig implements PluginConfig {
-    /**
-     * logger
-     */
-    private static final Logger LOGGER = LoggerFactory.getLogger();
-
-    /**
-     * spring boot zone
-     * If you do not configure a zone that uses system environment variables by default,
-     */
-    private String zone;
-
-    /**
-     * Whether it is encrypted or not
-     */
-    private boolean secure = false;
-
-    /**
-     * Whether to enable authentication
-     */
-    private boolean enableAuth;
-
-    /**
-     * Nacos authentication Account
-     */
-    private String username;
-
-    /**
-     * Nacos authentication password
-     */
-    private String password;
-
-    /**
-     * privateKey
-     */
-    private String privateKey;
-
-    /**
-     * Namespace
-     */
-    private String namespace;
-
-    /**
-     * The weight of the service instance
-     */
-    private float weight = 1f;
-
-    /**
-     * The name of the cluster
-     */
-    private String clusterName = "DEFAULT";
-
-    /**
-     * Group
-     */
-    private String group = "DEFAULT_GROUP";
-
-    /**
-     * Whether to load the cache at startup
-     */
-    private String namingLoadCacheAtStart = "false";
-
-    /**
-     * Namespace AK
-     */
-    private String accessKey;
-
-    /**
-     * Namespace SK
-     */
-    private String secretKey;
-
-    /**
-     * Whether the instance is available
-     */
-    private boolean instanceEnabled = true;
-
-    /**
-     * Whether it is a temporary node
-     */
-    private boolean ephemeral = true;
-
-    /**
-     * Instance metadata
-     */
-    private Map<String, String> metadata = new HashMap<>();
-
-    /**
-     * Whether it fails to retrieve cached data quickly, if false is not retrieved, it will fail directly
-     */
-    private boolean failureToleranceEnabled = false;
-
-    /**
-     * Constructor
-     */
-    public NacosRegisterConfig() {
-        final ServiceMeta serviceMeta = ConfigManager.getConfig(ServiceMeta.class);
-        if (serviceMeta == null) {
-            return;
-        }
-        zone = serviceMeta.getZone();
-        group = serviceMeta.getApplication();
-    }
-
-    public String getZone() {
-        return zone;
-    }
-
-    public void setZone(String zone) {
-        this.zone = zone;
-    }
-
-    public boolean isSecure() {
-        return secure;
-    }
-
-    public void setSecure(boolean secure) {
-        this.secure = secure;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public String getNamespace() {
-        return namespace;
-    }
-
-    public void setNamespace(String namespace) {
-        this.namespace = namespace;
-    }
-
-    public float getWeight() {
-        return weight;
-    }
-
-    public void setWeight(float weight) {
-        this.weight = weight;
-    }
-
-    public String getClusterName() {
-        return clusterName;
-    }
-
-    public void setClusterName(String clusterName) {
-        this.clusterName = clusterName;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public void setGroup(String group) {
-        this.group = group;
-    }
-
-    public String getNamingLoadCacheAtStart() {
-        return namingLoadCacheAtStart;
-    }
-
-    public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) {
-        this.namingLoadCacheAtStart = namingLoadCacheAtStart;
-    }
-
-    public String getAccessKey() {
-        return accessKey;
-    }
-
-    public void setAccessKey(String accessKey) {
-        this.accessKey = accessKey;
-    }
-
-    public String getSecretKey() {
-        return secretKey;
-    }
-
-    public void setSecretKey(String secretKey) {
-        this.secretKey = secretKey;
-    }
-
-    public boolean isInstanceEnabled() {
-        return instanceEnabled;
-    }
-
-    public void setInstanceEnabled(boolean instanceEnabled) {
-        this.instanceEnabled = instanceEnabled;
-    }
-
-    public boolean isEphemeral() {
-        return ephemeral;
-    }
-
-    public void setEphemeral(boolean ephemeral) {
-        this.ephemeral = ephemeral;
-    }
-
-    public Map<String, String> getMetadata() {
-        return metadata;
-    }
-
-    public void setMetadata(Map<String, String> metadata) {
-        this.metadata = metadata;
-    }
-
-    public boolean isFailureToleranceEnabled() {
-        return failureToleranceEnabled;
-    }
-
-    public void setFailureToleranceEnabled(boolean failureToleranceEnabled) {
-        this.failureToleranceEnabled = failureToleranceEnabled;
-    }
-
-    public String getPrivateKey() {
-        return privateKey;
-    }
-
-    public void setPrivateKey(String privateKey) {
-        this.privateKey = privateKey;
-    }
-
-    public boolean isEnableAuth() {
-        return enableAuth;
-    }
-
-    public void setEnableAuth(boolean enableAuth) {
-        this.enableAuth = enableAuth;
-    }
-
-    /**
-     * Get configuration parameters
-     *
-     * @return Properties
-     */
-    public Properties getNacosProperties() {
-        LbConfig lbConfig = PluginConfigManager.getPluginConfig(LbConfig.class);
-        Properties properties = new Properties();
-        if (enableAuth) {
-            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)
-                    || StringUtils.isEmpty(privateKey)) {
-                LOGGER.log(Level.SEVERE, "Nacos username, password or privateKey is Empty");
-                return properties;
-            }
-            Optional<String> pwd = AesUtil.decrypt(privateKey, password);
-            if (!pwd.isPresent()) {
-                LOGGER.log(Level.SEVERE, "Nacos password parsing failed");
-                return properties;
-            }
-            properties.put(PropertyKeyConst.USERNAME, username);
-            properties.put(PropertyKeyConst.PASSWORD, pwd.get());
-        }
-        properties.put(PropertyKeyConst.SERVER_ADDR, lbConfig.getRegistryAddress());
-        properties.put(PropertyKeyConst.NAMESPACE, Objects.toString(namespace, ""));
-        properties.put(PropertyKeyConst.ACCESS_KEY, Objects.toString(accessKey, ""));
-        properties.put(PropertyKeyConst.SECRET_KEY, Objects.toString(secretKey, ""));
-        properties.put(PropertyKeyConst.CLUSTER_NAME, clusterName);
-        properties.put(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
-        return properties;
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/PropertyKeyConst.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/PropertyKeyConst.java
deleted file mode 100644
index 61cd2220f..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/config/PropertyKeyConst.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.config;
-
-/**
- * NACOS registers static constants for parameters
- *
- * @author xz
- * @since 2024-11-25
- */
-public class PropertyKeyConst {
-    /**
-     * Profanity
-     */
-    public static final String HTTP_URL_COLON = ":";
-
-    /**
-     * Node
-     */
-    public static final String ENDPOINT = "endpoint";
-
-    /**
-     * Node port
-     */
-    public static final String ENDPOINT_PORT = "endpointPort";
-
-    /**
-     * Namespace
-     */
-    public static final String NAMESPACE = "namespace";
-
-    /**
-     * Username
-     */
-    public static final String USERNAME = "username";
-
-    /**
-     * User password
-     */
-    public static final String PASSWORD = "password";
-
-    /**
-     * AK value
-     */
-    public static final String ACCESS_KEY = "accessKey";
-
-    /**
-     * SK value
-     */
-    public static final String SECRET_KEY = "secretKey";
-
-    /**
-     * Service address
-     */
-    public static final String SERVER_ADDR = "serverAddr";
-
-    /**
-     * The name of the cluster
-     */
-    public static final String CLUSTER_NAME = "clusterName";
-
-    /**
-     * Start naming whether to load the cache
-     */
-    public static final String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart";
-
-    /**
-     * The name of the nacos log file
-     */
-    public static final String NACOS_NAMING_LOG_NAME = "com.alibaba.nacos.naming.log.filename";
-
-    /**
-     * Constructor
-     */
-    private PropertyKeyConst() {
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/entity/RegisterContext.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/entity/RegisterContext.java
index 1d7a3a9d2..505be6e7b 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/entity/RegisterContext.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/java/io/sermant/discovery/entity/RegisterContext.java
@@ -16,8 +16,6 @@
 
 package io.sermant.discovery.entity;
 
-import java.util.Map;
-
 /**
  * Registration Information Class
  *
@@ -25,13 +23,12 @@ import java.util.Map;
  * @since 2022-10-09
  */
 public enum RegisterContext {
+
     /**
      * Instance
      */
     INSTANCE;
 
-    private final ClientInfo clientInfo = new ClientInfo();
-
     private final DefaultServiceInstance serviceInstance = new DefaultServiceInstance();
 
     /**
@@ -43,118 +40,4 @@ public enum RegisterContext {
         return this.serviceInstance;
     }
 
-    public ClientInfo getClientInfo() {
-        return clientInfo;
-    }
-
-    /**
-     * Client information
-     *
-     * @since 2022-03-01
-     */
-    public static class ClientInfo {
-        /**
-         * the service name is obtained by intercepting
-         */
-        private String serviceName;
-
-        /**
-         * domain name
-         */
-        private String host;
-
-        /**
-         * ip
-         */
-        private String ip;
-
-        /**
-         * port
-         */
-        private int port;
-
-        /**
-         * service id
-         */
-        private String serviceId;
-
-        /**
-         * service meta information
-         */
-        private Map<String, String> meta;
-
-        /**
-         * region
-         */
-        private String zone;
-
-        /**
-         * instance state UP DOWN
-         */
-        private String status = "UN_KNOWN";
-
-        public String getStatus() {
-            return status;
-        }
-
-        public void setStatus(String status) {
-            this.status = status;
-        }
-
-        public String getServiceName() {
-            return serviceName;
-        }
-
-        public void setServiceName(String serviceName) {
-            this.serviceName = serviceName;
-        }
-
-        public String getHost() {
-            return host;
-        }
-
-        public void setHost(String host) {
-            this.host = host;
-        }
-
-        public String getIp() {
-            return ip;
-        }
-
-        public void setIp(String ip) {
-            this.ip = ip;
-        }
-
-        public int getPort() {
-            return port;
-        }
-
-        public void setPort(int port) {
-            this.port = port;
-        }
-
-        public String getServiceId() {
-            return serviceId;
-        }
-
-        public void setServiceId(String serviceId) {
-            this.serviceId = serviceId;
-        }
-
-        public String getZone() {
-            return zone;
-        }
-
-        public void setZone(String zone) {
-            this.zone = zone;
-        }
-
-        public Map<String, String> getMeta() {
-            return meta;
-        }
-
-        public void setMeta(Map<String, String> meta) {
-            this.meta = meta;
-        }
-    }
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig
index 942ee0b06..f8585113e 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig
@@ -16,4 +16,3 @@
 
 io.sermant.discovery.config.LbConfig
 io.sermant.discovery.config.DiscoveryPluginConfig
-io.sermant.discovery.config.NacosRegisterConfig
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/pom.xml b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/pom.xml
index 15f2e2e0a..eff5848fd 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/pom.xml
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/pom.xml
@@ -21,7 +21,6 @@
         <cloud.zk.version>3.1.0</cloud.zk.version>
         <jackson.version>2.13.4.2</jackson.version>
         <guava.version>31.1-jre</guava.version>
-        <nacos.version>2.3.3</nacos.version>
         <!--Curator 2.x.x is compatible with Zookeeper's 3.4.x and 3.5.x
             Curator 3.x.x only compatible with Zookeeper 3.5.x -->
         <!--zk  3.4.x, Upward compatible-->
@@ -43,12 +42,6 @@
             <scope>provided</scope>
         </dependency>
 
-        <dependency>
-            <groupId>com.alibaba.nacos</groupId>
-            <artifactId>nacos-client</artifactId>
-            <version>${nacos.version}</version>
-        </dependency>
-
         <!--compile dependency -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/DiscoveryManager.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/DiscoveryManager.java
index de83775fb..fb8a7f2c8 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/DiscoveryManager.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/DiscoveryManager.java
@@ -18,9 +18,7 @@ package io.sermant.discovery.service.lb;
 
 import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.discovery.config.DiscoveryPluginConfig;
 import io.sermant.discovery.config.LbConfig;
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.entity.ServiceInstance;
 import io.sermant.discovery.service.lb.cache.InstanceCacheManager;
 import io.sermant.discovery.service.lb.discovery.InstanceListenable;
@@ -85,16 +83,16 @@ public enum DiscoveryManager {
     }
 
     private void initServiceDiscoveryClient() {
-        RegisterType registerType = PluginConfigManager.getPluginConfig(DiscoveryPluginConfig.class).getRegisterType();
+        final String registryCenterType = lbConfig.getRegistryCenterType();
         for (ServiceDiscoveryClient discoveryClient : ServiceLoader.load(ServiceDiscoveryClient.class, this.getClass()
                 .getClassLoader())) {
-            if (discoveryClient.registerType().equals(registerType)) {
+            if (discoveryClient.name().equalsIgnoreCase(lbConfig.getRegistryCenterType())) {
                 this.serviceDiscoveryClient = discoveryClient;
                 break;
             }
         }
         if (this.serviceDiscoveryClient == null) {
-            throw new IllegalStateException("Can not support register center type: " + registerType);
+            throw new IllegalStateException("Can not support register center type: " + registryCenterType);
         }
         this.serviceDiscoveryClient.init();
     }
@@ -107,10 +105,9 @@ public enum DiscoveryManager {
     }
 
     private void loadListen() {
-        RegisterType registerType = PluginConfigManager.getPluginConfig(DiscoveryPluginConfig.class).getRegisterType();
         for (InstanceListenable listenable : ServiceLoader.load(InstanceListenable.class, this.getClass()
                 .getClassLoader())) {
-            if (listenable.registerType().equals(registerType)) {
+            if (listenable.name().equalsIgnoreCase(lbConfig.getRegistryCenterType())) {
                 this.instanceListenable = listenable;
                 break;
             }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/cache/InstanceCacheManager.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/cache/InstanceCacheManager.java
index bbec224d2..a7d102da1 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/cache/InstanceCacheManager.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/cache/InstanceCacheManager.java
@@ -263,13 +263,6 @@ public class InstanceCacheManager {
             instanceCaches.put(serviceName, instanceCache);
         }
 
-        @Override
-        public void notify(String serviceName, List<ServiceInstance> serviceInstances) {
-            InstanceCache instanceCache = new InstanceCache(serviceName, serviceInstances);
-            instanceCache.setUpdateTimestamp(System.currentTimeMillis());
-            instanceCaches.put(serviceName, instanceCache);
-        }
-
         private void printLog(EventType eventType, ServiceInstance serviceInstance) {
             final Map<String, String> metadata = serviceInstance.getMetadata();
             if (metadata == null) {
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceChangeListener.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceChangeListener.java
index cea63ed74..074979271 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceChangeListener.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceChangeListener.java
@@ -18,8 +18,6 @@ package io.sermant.discovery.service.lb.discovery;
 
 import io.sermant.discovery.entity.ServiceInstance;
 
-import java.util.List;
-
 /**
  * Listeners for instance changes, and notifications are sent when the instance changes
  *
@@ -35,15 +33,6 @@ public interface InstanceChangeListener {
      */
     void notify(EventType eventType, ServiceInstance serviceInstance);
 
-
-    /**
-     * Notice
-     *
-     * @param serviceName Service name
-     * @param serviceInstances All instances
-     */
-    void notify(String serviceName,List<ServiceInstance> serviceInstances);
-
     /**
      * The type of event
      *
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceListenable.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceListenable.java
index 2c7eaf736..b2b47e892 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceListenable.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/InstanceListenable.java
@@ -16,7 +16,6 @@
 
 package io.sermant.discovery.service.lb.discovery;
 
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.service.lb.cache.InstanceCacheManager;
 
 /**
@@ -46,9 +45,9 @@ public interface InstanceListenable {
     void close();
 
     /**
-     * The RegisterType of the listener
+     * The name of the listener
      *
-     * @return RegisterType
+     * @return Name
      */
-    RegisterType registerType();
+    String name();
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/ServiceDiscoveryClient.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/ServiceDiscoveryClient.java
index 694a4b0dc..8e427ae44 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/ServiceDiscoveryClient.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/ServiceDiscoveryClient.java
@@ -16,7 +16,6 @@
 
 package io.sermant.discovery.service.lb.discovery;
 
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.entity.ServiceInstance;
 import io.sermant.discovery.service.ex.QueryInstanceException;
 
@@ -69,7 +68,7 @@ public interface ServiceDiscoveryClient extends Closeable {
     /**
      * The name of the service discovery, associated with the registry type
      *
-     * @return RegisterType
+     * @return Name
      */
-    RegisterType registerType();
+    String name();
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClient.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClient.java
deleted file mode 100644
index 25a8422bc..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClient.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.service.lb.discovery.nacos;
-
-import com.alibaba.nacos.api.exception.NacosException;
-import com.alibaba.nacos.api.naming.NamingService;
-import com.alibaba.nacos.api.naming.pojo.Instance;
-import com.alibaba.nacos.api.naming.pojo.ListView;
-
-import io.sermant.core.common.LoggerFactory;
-import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.core.utils.StringUtils;
-import io.sermant.discovery.config.NacosRegisterConfig;
-import io.sermant.discovery.config.RegisterType;
-import io.sermant.discovery.entity.RegisterContext;
-import io.sermant.discovery.entity.ServiceInstance;
-import io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Optional;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * NACOS connects to the client
- *
- * @author xiaozhao
- * @since 2024-11-16
- */
-public class NacosDiscoveryClient implements ServiceDiscoveryClient {
-    private static final Logger LOGGER = LoggerFactory.getLogger();
-
-    private final NacosServiceManager nacosServiceManager = NacosServiceManager.getInstance();
-
-    private Instance instance;
-
-    private final NacosRegisterConfig nacosRegisterConfig;
-
-    /**
-     * constructor
-     */
-    public NacosDiscoveryClient() {
-        nacosRegisterConfig = PluginConfigManager.getPluginConfig(NacosRegisterConfig.class);
-    }
-
-    @Override
-    public void init() {
-    }
-
-    @Override
-    public boolean registry(ServiceInstance serviceInstance) {
-        String serviceId = serviceInstance.getServiceName();
-        String group = nacosRegisterConfig.getGroup();
-        instance = nacosServiceManager.buildNacosInstanceFromRegistration(serviceInstance);
-        try {
-            NamingService namingService = nacosServiceManager.getNamingService();
-            namingService.registerInstance(serviceId, group, instance);
-            return true;
-        } catch (NacosException e) {
-            LOGGER.log(Level.SEVERE, String.format(Locale.ENGLISH, "failed when registry service，serviceId={%s}",
-                    serviceId), e);
-        }
-        return false;
-    }
-
-    @Override
-    public Collection<String> getServices() {
-        try {
-            String group = nacosRegisterConfig.getGroup();
-            NamingService namingService = nacosServiceManager.getNamingService();
-            ListView<String> services = namingService.getServicesOfServer(1, Integer.MAX_VALUE, group);
-            return services.getData();
-        } catch (NacosException e) {
-            LOGGER.log(Level.SEVERE, String.format(Locale.ENGLISH, "getServices failed，"
-                    + "isFailureToleranceEnabled={%s}", nacosRegisterConfig.isFailureToleranceEnabled()), e);
-        }
-        return Collections.emptyList();
-    }
-
-    @Override
-    public boolean unRegistry() {
-        if (StringUtils.isEmpty(RegisterContext.INSTANCE.getClientInfo().getServiceId())) {
-            LOGGER.warning("No service to de-register for nacos client...");
-            return false;
-        }
-        String serviceId = RegisterContext.INSTANCE.getClientInfo().getServiceId();
-        String group = nacosRegisterConfig.getGroup();
-        try {
-            NamingService namingService = nacosServiceManager.getNamingService();
-            namingService.deregisterInstance(serviceId, group, instance);
-            return true;
-        } catch (NacosException e) {
-            LOGGER.log(Level.SEVERE, String.format(Locale.ENGLISH, "failed when deRegister service，"
-                    + "serviceId={%s}", serviceId), e);
-        }
-        return false;
-    }
-
-    @Override
-    public RegisterType registerType() {
-        return RegisterType.NACOS;
-    }
-
-    @Override
-    public void close() throws IOException {
-    }
-
-    /**
-     * Obtain information about the microservice instance corresponding to the service name
-     *
-     * @param serviceId ServiceId
-     * @return Service information
-     */
-    public List<ServiceInstance> getInstances(String serviceId) {
-        String group = nacosRegisterConfig.getGroup();
-        try {
-            NamingService namingService = nacosServiceManager.getNamingService();
-            List<Instance> instances = namingService.selectInstances(serviceId, group, true);
-            return convertServiceInstanceList(instances, serviceId);
-        } catch (NacosException e) {
-            LOGGER.log(Level.SEVERE, String.format(Locale.ENGLISH, "failed get Instances，"
-                    + "serviceId={%s}", serviceId), e);
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * Convert the instance list to service instance list
-     *
-     * @param instances
-     * @param serviceId
-     * @return ServiceInstance list
-     */
-    public List<ServiceInstance> convertServiceInstanceList(List<Instance> instances, String serviceId) {
-        List<ServiceInstance> result = new ArrayList<>(instances.size());
-        for (Instance nacosInstance : instances) {
-            Optional<ServiceInstance> optional = nacosServiceManager.convertServiceInstance(nacosInstance, serviceId);
-            optional.ifPresent(result::add);
-        }
-        return result;
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManager.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManager.java
deleted file mode 100644
index 9c0b0f905..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManager.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.service.lb.discovery.nacos;
-
-import com.alibaba.nacos.api.exception.NacosException;
-import com.alibaba.nacos.api.naming.NamingMaintainService;
-import com.alibaba.nacos.api.naming.NamingService;
-import com.alibaba.nacos.api.naming.pojo.Instance;
-import com.alibaba.nacos.client.naming.NacosNamingMaintainService;
-import com.alibaba.nacos.client.naming.NacosNamingService;
-
-import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.discovery.config.NacosRegisterConfig;
-import io.sermant.discovery.entity.DefaultServiceInstance;
-import io.sermant.discovery.entity.ServiceInstance;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Properties;
-
-/**
- * NACOS Registration Service Manager
- *
- * @author xiaozhao
- * @since 2024-11-16
- */
-public class NacosServiceManager {
-    private static final int DEFAULT_CAPACITY = 16;
-
-    private static NacosServiceManager nacosServiceManager;
-
-    private volatile NamingService namingService;
-
-    private volatile NamingMaintainService namingMaintainService;
-
-    private final NacosRegisterConfig nacosRegisterConfig;
-
-    /**
-     * Constructor
-     */
-    public NacosServiceManager() {
-        nacosRegisterConfig = PluginConfigManager.getPluginConfig(NacosRegisterConfig.class);
-    }
-
-    /**
-     * Get the singleton instance
-     *
-     * @return NacosServiceManager
-     */
-    public static NacosServiceManager getInstance() {
-        if (nacosServiceManager == null) {
-            synchronized (NacosServiceManager.class) {
-                if (nacosServiceManager == null) {
-                    nacosServiceManager = new NacosServiceManager();
-                }
-            }
-        }
-        return nacosServiceManager;
-    }
-
-    /**
-     * Get registration services
-     *
-     * @return NamingService services
-     * @throws NacosException nacos exception
-     */
-    public NamingService getNamingService() throws NacosException {
-        if (Objects.isNull(this.namingService)) {
-            buildNamingService(nacosRegisterConfig.getNacosProperties());
-        }
-        return namingService;
-    }
-
-    /**
-     * Obtain the namingMaintain service
-     *
-     * @return namingMaintain service
-     * @throws NacosException nacos exception
-     */
-    public NamingMaintainService getNamingMaintainService() throws NacosException {
-        if (Objects.isNull(namingMaintainService)) {
-            buildNamingMaintainService(nacosRegisterConfig.getNacosProperties());
-        }
-        return namingMaintainService;
-    }
-
-    private void buildNamingMaintainService(Properties properties) throws NacosException {
-        if (Objects.isNull(namingMaintainService)) {
-            synchronized (NacosServiceManager.class) {
-                if (Objects.isNull(namingMaintainService)) {
-                    namingMaintainService = createNamingMaintainService(properties);
-                }
-            }
-        }
-    }
-
-    private void buildNamingService(Properties properties) throws NacosException {
-        if (Objects.isNull(namingService)) {
-            synchronized (NacosServiceManager.class) {
-                if (Objects.isNull(namingService)) {
-                    namingService = createNewNamingService(properties);
-                }
-            }
-        }
-    }
-
-    private NamingService createNewNamingService(Properties properties) throws NacosException {
-        return new NacosNamingService(properties);
-    }
-
-    private NamingMaintainService createNamingMaintainService(Properties properties) throws NacosException {
-        return new NacosNamingMaintainService(properties);
-    }
-
-    /**
-     * Build the nacos registry instance
-     *
-     * @param serviceInstance ServiceInstance
-     * @return instance
-     */
-    public Instance buildNacosInstanceFromRegistration(ServiceInstance serviceInstance) {
-        Instance instance = new Instance();
-        instance.setIp(serviceInstance.getIp());
-        instance.setPort(serviceInstance.getPort());
-        instance.setWeight(nacosRegisterConfig.getWeight());
-        instance.setClusterName(nacosRegisterConfig.getClusterName());
-        instance.setEnabled(nacosRegisterConfig.isInstanceEnabled());
-        final HashMap<String, String> metadata = new HashMap<>(serviceInstance.getMetadata());
-        instance.setMetadata(metadata);
-        instance.setEphemeral(nacosRegisterConfig.isEphemeral());
-        return instance;
-    }
-
-    /**
-     * Instance information conversion
-     *
-     * @param instance Instance
-     * @param serviceId id
-     * @return Converted instance information
-     */
-    public Optional<ServiceInstance> convertServiceInstance(Instance instance, String serviceId) {
-        if (instance == null || !instance.isEnabled() || !instance.isHealthy()) {
-            return Optional.empty();
-        }
-        DefaultServiceInstance nacosServiceInstance = new DefaultServiceInstance();
-        nacosServiceInstance.setHost(instance.getIp());
-        nacosServiceInstance.setIp(instance.getIp());
-        nacosServiceInstance.setPort(instance.getPort());
-        nacosServiceInstance.setServiceName(serviceId);
-        nacosServiceInstance.setId(instance.getIp() + ":" + instance.getPort());
-
-        Map<String, String> metadata = new HashMap<>(DEFAULT_CAPACITY);
-        metadata.put("nacos.instanceId", instance.getInstanceId());
-        metadata.put("nacos.weight", instance.getWeight() + "");
-        metadata.put("nacos.healthy", instance.isHealthy() + "");
-        metadata.put("nacos.cluster", instance.getClusterName() + "");
-        if (instance.getMetadata() != null) {
-            metadata.putAll(instance.getMetadata());
-        }
-        metadata.put("nacos.ephemeral", String.valueOf(instance.isEphemeral()));
-        nacosServiceInstance.setMetadata(metadata);
-
-        return Optional.of(nacosServiceInstance);
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/listen/NacosInstanceListenable.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/listen/NacosInstanceListenable.java
deleted file mode 100644
index 7bb7548df..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/nacos/listen/NacosInstanceListenable.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.service.lb.discovery.nacos.listen;
-
-import com.alibaba.nacos.api.exception.NacosException;
-import com.alibaba.nacos.api.naming.NamingService;
-import com.alibaba.nacos.api.naming.listener.EventListener;
-import com.alibaba.nacos.api.naming.pojo.Instance;
-import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
-import com.alibaba.nacos.common.notify.Event;
-import com.alibaba.nacos.common.notify.NotifyCenter;
-import com.alibaba.nacos.common.notify.listener.Subscriber;
-
-import io.sermant.core.common.LoggerFactory;
-import io.sermant.core.utils.CollectionUtils;
-import io.sermant.discovery.config.RegisterType;
-import io.sermant.discovery.entity.ServiceInstance;
-import io.sermant.discovery.service.lb.discovery.InstanceChangeListener;
-import io.sermant.discovery.service.lb.discovery.InstanceListenable;
-import io.sermant.discovery.service.lb.discovery.nacos.NacosServiceManager;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
-
-/**
- * nacos Listen
- *
- * @author xz
- * @since 2024-11-12
- */
-public class NacosInstanceListenable extends Subscriber<InstancesChangeEvent> implements InstanceListenable {
-    private static final Logger LOGGER = LoggerFactory.getLogger();
-
-    private final Map<String, InstanceChangeListener> listenerCache = new ConcurrentHashMap<>();
-
-    private final NacosServiceManager nacosServiceManager = NacosServiceManager.getInstance();
-
-    /**
-     * Construct
-     */
-    public NacosInstanceListenable() {
-    }
-
-    @Override
-    public void onEvent(InstancesChangeEvent instancesChangeEvent) {
-        String serviceName = instancesChangeEvent.getServiceName();
-        List<Instance> instances = instancesChangeEvent.getHosts();
-        InstanceChangeListener listener = listenerCache.get(serviceName);
-        if (Objects.isNull(listener)) {
-            return;
-        }
-        if (CollectionUtils.isEmpty(instances)) {
-            listener.notify(serviceName, Collections.emptyList());
-            return;
-        }
-        List<ServiceInstance> serviceInstances = new ArrayList<>(instances.size());
-        for (Instance instance : instances) {
-            if (instance.isEnabled() && instance.isHealthy()) {
-                Optional<ServiceInstance> optional = nacosServiceManager.convertServiceInstance(instance, serviceName);
-                optional.ifPresent(serviceInstances::add);
-            }
-        }
-        listener.notify(serviceName, serviceInstances);
-    }
-
-    @Override
-    public Class<? extends Event> subscribeType() {
-        return InstancesChangeEvent.class;
-    }
-
-    @Override
-    public void init() {
-    }
-
-    @Override
-    public void tryAdd(String serviceName, InstanceChangeListener listener) {
-        NotifyCenter.registerSubscriber(this);
-        try {
-            NamingService namingService = nacosServiceManager.getNamingService();
-            namingService.subscribe(serviceName, new EventListener() {
-                @Override
-                public void onEvent(com.alibaba.nacos.api.naming.listener.Event event) {
-                    LOGGER.info("Receive nacos instance change event: %s");
-                }
-            });
-            listenerCache.put(serviceName, listener);
-        } catch (NacosException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public void close() {
-        listenerCache.clear();
-    }
-
-    @Override
-    public RegisterType registerType() {
-        return RegisterType.NACOS;
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkClient.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkClient.java
index 94aa1625e..3b243dfd8 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkClient.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkClient.java
@@ -20,7 +20,6 @@ import io.sermant.core.plugin.config.PluginConfigManager;
 import io.sermant.core.plugin.service.PluginService;
 import io.sermant.discovery.config.DiscoveryPluginConfig;
 import io.sermant.discovery.config.LbConfig;
-import io.sermant.discovery.config.RegisterType;
 
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -52,8 +51,7 @@ public class ZkClient implements PluginService {
 
     @Override
     public void start() {
-        DiscoveryPluginConfig pluginConfig = PluginConfigManager.getPluginConfig(DiscoveryPluginConfig.class);
-        if (!pluginConfig.isEnableRegistry() || !RegisterType.ZOOKEEPER.equals(pluginConfig.getRegisterType())) {
+        if (!PluginConfigManager.getPluginConfig(DiscoveryPluginConfig.class).isEnableRegistry()) {
             return;
         }
         this.client = this.buildClient();
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClient.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClient.java
index b0494b1af..b28b3f976 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClient.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClient.java
@@ -25,7 +25,6 @@ import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.config.PluginConfigManager;
 import io.sermant.core.plugin.service.PluginServiceManager;
 import io.sermant.discovery.config.LbConfig;
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.entity.ServiceInstance;
 import io.sermant.discovery.service.ex.QueryInstanceException;
 import io.sermant.discovery.service.lb.LbConstants;
@@ -209,8 +208,8 @@ public class ZkDiscoveryClient implements ServiceDiscoveryClient {
     }
 
     @Override
-    public RegisterType registerType() {
-        return RegisterType.ZOOKEEPER;
+    public String name() {
+        return "Zookeeper";
     }
 
     private ServiceDiscovery<ZookeeperInstance> build() {
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClientProxy.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClientProxy.java
index aa7581989..22fffe672 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClientProxy.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/ZkDiscoveryClientProxy.java
@@ -16,7 +16,6 @@
 
 package io.sermant.discovery.service.lb.discovery.zk;
 
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.entity.ServiceInstance;
 import io.sermant.discovery.service.ex.QueryInstanceException;
 import io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient;
@@ -59,8 +58,8 @@ public class ZkDiscoveryClientProxy implements ServiceDiscoveryClient {
     }
 
     @Override
-    public RegisterType registerType() {
-        return RegisterType.ZOOKEEPER;
+    public String name() {
+        return "Zookeeper";
     }
 
     @Override
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/listen/ZkInstanceListenable.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/listen/ZkInstanceListenable.java
index 2817c3a3b..0db7866de 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/listen/ZkInstanceListenable.java
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/discovery/zk/listen/ZkInstanceListenable.java
@@ -20,7 +20,6 @@ import io.sermant.core.common.LoggerFactory;
 import io.sermant.core.plugin.config.PluginConfigManager;
 import io.sermant.core.plugin.service.PluginServiceManager;
 import io.sermant.discovery.config.LbConfig;
-import io.sermant.discovery.config.RegisterType;
 import io.sermant.discovery.service.lb.discovery.InstanceChangeListener;
 import io.sermant.discovery.service.lb.discovery.InstanceChangeListener.EventType;
 import io.sermant.discovery.service.lb.discovery.InstanceListenable;
@@ -201,7 +200,7 @@ public class ZkInstanceListenable implements InstanceListenable {
     }
 
     @Override
-    public RegisterType registerType() {
-        return RegisterType.ZOOKEEPER;
+    public String name() {
+        return "Zookeeper";
     }
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRule.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRule.java
deleted file mode 100644
index 23e30d267..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRule.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
- *
- *   Licensed 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 io.sermant.discovery.service.lb.rule;
-
-import io.sermant.core.utils.StringUtils;
-import io.sermant.discovery.entity.ServiceInstance;
-
-import java.util.List;
-import java.util.Random;
-import java.util.stream.Collectors;
-
-/**
- * nacos Weight
- *
- * @author xiaozhao
- * @since 2024-11-16
- */
-public class NacosWeightRandomRule extends AbstractLoadbalancer {
-    @Override
-    protected ServiceInstance doChoose(String serviceName, List<ServiceInstance> instances) {
-        List<InstanceWithWeight> withWeights = instances.stream()
-                .map(instance -> {
-                    String nacosWeight = instance.getMetadata().get("nacos.weight");
-                    int weight = StringUtils.isBlank(nacosWeight) ? 1 : Integer.parseInt(nacosWeight);
-                    return new InstanceWithWeight(instance, weight);
-                }).collect(Collectors.toList());
-        return this.weightRandom(withWeights);
-    }
-
-    @Override
-    public String lbType() {
-        return "NacosWeight";
-    }
-
-    /**
-     * nacos Weight
-     *
-     * @author xiaozhao
-     * @since 2024-11-16
-     */
-    public static class InstanceWithWeight {
-        private ServiceInstance server;
-        private Integer weight;
-
-        /**
-         * constructor
-         *
-         * @param instance instance
-         * @param weight weight
-         */
-        public InstanceWithWeight(ServiceInstance instance, int weight) {
-            this.server = instance;
-            this.weight = weight;
-        }
-
-        public ServiceInstance getServer() {
-            return server;
-        }
-
-        public Integer getWeight() {
-            return weight;
-        }
-    }
-
-    /**
-     * Random by weight
-     *
-     * @param list instance list
-     * @return just a random result
-     * @throws IllegalArgumentException The parameter error is abnormal
-     */
-    public ServiceInstance weightRandom(List<InstanceWithWeight> list) {
-        int totalWeight = list.stream().mapToInt(InstanceWithWeight::getWeight).sum();
-        int randomWeight = new Random().nextInt(totalWeight);
-        int currentWeight = 0;
-        for (InstanceWithWeight instanceWithWeight : list) {
-            currentWeight += instanceWithWeight.getWeight();
-            if (randomWeight < currentWeight) {
-                return instanceWithWeight.getServer();
-            }
-        }
-        throw new IllegalArgumentException("Should never reach here if weight logic is correct");
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.InstanceListenable b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.InstanceListenable
index 5384daa27..e5086a73e 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.InstanceListenable
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.InstanceListenable
@@ -15,4 +15,3 @@
 #
 
 io.sermant.discovery.service.lb.discovery.zk.listen.ZkInstanceListenable
-io.sermant.discovery.service.lb.discovery.nacos.listen.NacosInstanceListenable
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient
index 4f8b25447..48e1c03d1 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.discovery.ServiceDiscoveryClient
@@ -15,4 +15,3 @@
 #
 
 io.sermant.discovery.service.lb.discovery.zk.ZkDiscoveryClientProxy
-io.sermant.discovery.service.lb.discovery.nacos.NacosDiscoveryClient
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.rule.AbstractLoadbalancer b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.rule.AbstractLoadbalancer
index 5dbcb0bb7..e57660603 100644
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.rule.AbstractLoadbalancer
+++ b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/main/resources/META-INF/services/io.sermant.discovery.service.lb.rule.AbstractLoadbalancer
@@ -18,4 +18,3 @@ io.sermant.discovery.service.lb.rule.RoundRobinLoadbalancer
 io.sermant.discovery.service.lb.rule.RandomLoadbalancer
 io.sermant.discovery.service.lb.rule.BestAvailableLoadbalancer
 io.sermant.discovery.service.lb.rule.WeightedResponseTimeLoadbalancer
-io.sermant.discovery.service.lb.rule.NacosWeightRandomRule
diff --git a/sermant-plugins/sermant-tag-transmission/tag-transmission-crossthread-plugin/src/main/java/io/sermant/tag/transmission/crossthread/wrapper/AbstractThreadWrapper.java b/sermant-plugins/sermant-tag-transmission/tag-transmission-crossthread-plugin/src/main/java/io/sermant/tag/transmission/crossthread/wrapper/AbstractThreadWrapper.java
index c41260f8f..cc0a8f5df 100644
--- a/sermant-plugins/sermant-tag-transmission/tag-transmission-crossthread-plugin/src/main/java/io/sermant/tag/transmission/crossthread/wrapper/AbstractThreadWrapper.java
+++ b/sermant-plugins/sermant-tag-transmission/tag-transmission-crossthread-plugin/src/main/java/io/sermant/tag/transmission/crossthread/wrapper/AbstractThreadWrapper.java
@@ -51,8 +51,6 @@ public abstract class AbstractThreadWrapper<T> {
 
     private final boolean cannotTransmit;
 
-    private final long threadId;
-
     /**
      * constructor
      *
@@ -75,7 +73,6 @@ public abstract class AbstractThreadWrapper<T> {
         }
         this.cannotTransmit = cannotTransmit;
         this.executorName = executorName;
-        this.threadId = Thread.currentThread().getId();
     }
 
     /**
@@ -111,9 +108,6 @@ public abstract class AbstractThreadWrapper<T> {
      * @param obj thread object
      */
     protected void before(Object obj) {
-        if (threadId == Thread.currentThread().getId()) {
-            return;
-        }
         if (cannotTransmit) {
             // When ordinary thread transparent transmission is enabled and thread pool transparent transmission is not
             // enabled, the data transparently transmitted by InheritableThreadLocal needs to be deleted before
@@ -139,9 +133,6 @@ public abstract class AbstractThreadWrapper<T> {
      * Post method executed by thread object
      */
     protected void after() {
-        if (threadId == Thread.currentThread().getId()) {
-            return;
-        }
         TrafficUtils.removeTrafficTag();
         TrafficUtils.removeTrafficData();
     }


Test patch:
diff --git a/.github/workflows/message_gray_integration_test.yml b/.github/workflows/message_gray_integration_test.yml
new file mode 100644
index 000000000..97a025412
--- /dev/null
+++ b/.github/workflows/message_gray_integration_test.yml
@@ -0,0 +1,330 @@
+name: Message gray integration test
+env:
+  sermantVersion: 1.0.0
+on:
+  push:
+  pull_request:
+    branches:
+      - '*'
+    paths:
+      - 'sermant-agentcore/**'
+      - 'sermant-integration-tests/mq-grayscale-rocketmq-test/**'
+      - 'sermant-plugins/sermant-mq-grayscale/**'
+      - '.github/workflows/message_gray_integration_test.yml'
+      - '.github/actions/common/mq-grayscale/rocketmq/**'
+      - '.github/actions/scenarios/mq-grayscale/rocketmq/**'
+      - '.github/actions/common/plugin-change-check/action.yml'
+      - '.github/actions/common/entry/action.yml'
+      - '.github/actions/common/exit/action.yml'
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.head_ref }}
+  cancel-in-progress: true
+jobs:
+  set-execution-conditions:
+    name: set-execution-conditions
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: plugin-change-check
+        id: plugin-change-check
+        uses: ./.github/actions/common/plugin-change-check
+      - name: set-outputs
+        id: set-outputs
+        run: |
+          echo "enableMqGrayscaleRocketMqAction=${{env.enableMqGrayscaleRocketMqAction}}" >> $GITHUB_OUTPUT
+    outputs:
+      enableMqGrayscaleRocketMqAction: ${{ steps.set-outputs.outputs.enableMqGrayscaleRocketMqAction }}
+  download-midwares-and-cache:
+    name: download midwares and cache
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: cache local cse
+        uses: actions/cache@v3
+        with:
+          path: Local-CSE-2.1.3-linux-amd64.zip
+          key: ${{ runner.os }}-local-cse
+          restore-keys: |
+            ${{ runner.os }}-local-cse
+      - name: download cse
+        run: |
+          export ROOT_PATH=$(pwd)
+          bash ./sermant-integration-tests/scripts/tryDownloadMidware.sh cse
+      - name: cache rocketmq
+        uses: actions/cache@v3
+        with:
+          path: rocketmq-all-5.1.4-bin-release.zip
+          key: ${{ runner.os }}-rocketmq-all-5.1.4-bin-release
+          restore-keys: |
+            ${{ runner.os }}-rocketmq-all-5.1.4-bin-release
+      - name: download rocketmq
+        run: |
+          export ROOT_PATH=$(pwd)
+          bash ./sermant-integration-tests/scripts/tryDownloadMidware.sh rocketmq514
+  build-agent-and-cache:
+    name: build agent and cache
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up JDK 8
+        uses: actions/setup-java@v3
+        with:
+          java-version: '8'
+          distribution: 'adopt'
+          cache: maven
+      - name: cache agent
+        uses: actions/cache@v3
+        with:
+          path: sermant-agent-*/
+          key: ${{ runner.os }}-agent-${{ github.run_id }}
+      - name: package agent
+        run: |
+          sed -i '/sermant-backend/d' pom.xml
+          sed -i '/sermant-integration-tests/d' pom.xml
+          sed -i '/sermant-injector/d' pom.xml
+          sed -i '/sermant-flowcontrol/d' sermant-plugins/pom.xml
+          sed -i '/sermant-database-write-prohibition/d' sermant-plugins/pom.xml
+          sed -i '/sermant-spring-beans-deal/d' sermant-plugins/pom.xml
+          sed -i '/sermant-service-removal/d' sermant-plugins/pom.xml
+          sed -i '/sermant-service-visibility/d' sermant-plugins/pom.xml
+          sed -i '/sermant-monitor/d' sermant-plugins/pom.xml
+          sed -i '/sermant-mq-consume-prohibition/d' sermant-plugins/pom.xml
+          sed -i '/sermant-springboot-registry/d' sermant-plugins/pom.xml
+          sed -i '/sermant-service-registry/d' sermant-plugins/pom.xml
+          sed -i '/sermant-dynamic-config/d' sermant-plugins/pom.xml
+          sed -i '/sermant-router/d' sermant-plugins/pom.xml
+          sed -i '/sermant-loadbalancer/d' sermant-plugins/pom.xml
+          mvn package -DskipTests -Ptest --file pom.xml
+  test-for-plugin-false:
+    name: Test for grayscale rocketmq pull plugin false
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache]
+    strategy:
+      matrix:
+        test-model: ['PLUGIN_ENABLED_FALSE']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-only-base:
+    name: Test for pull base only for auto rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-plugin-false]
+    strategy:
+      matrix:
+        test-model: ['AUTO_ONLY_BASE']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-exc-only-base:
+    name: Test for pull base only for auto exclude rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-only-base]
+    strategy:
+      matrix:
+        test-model: ['AUTO_EXC_ONLY_BASE']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-base-gray:
+    name: Test for pull base gray for auto rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-exc-only-base]
+    strategy:
+      matrix:
+        test-model: ['AUTO_BASE_GRAY_PULL']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-base-gray-lite:
+    name: Test for pull base gray for auto rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-exc-only-base]
+    strategy:
+      matrix:
+        test-model: ['AUTO_BASE_GRAY_LITE_PULL']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-base-gray-push:
+    name: Test for pull base gray for auto rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-exc-only-base]
+    strategy:
+      matrix:
+        test-model: ['AUTO_BASE_GRAY_PUSH']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-auto-exc-base-gray:
+    name: Test for pull base gray for auto exclude rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-base-gray]
+    strategy:
+      matrix:
+        test-model: ['AUTO_EXC_BASE_GRAY']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-base-only-base:
+    name: Test for pull base only for base rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-auto-exc-base-gray]
+    strategy:
+      matrix:
+        test-model: ['BASE_ONLY_BASE']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-base-exc-only-base:
+    name: Test for pull base only for base exclude rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-base-only-base]
+    strategy:
+      matrix:
+        test-model: ['BASE_EXC_ONLY_BASE']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-base-base-gray:
+    name: Test for pull base gray for base rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-base-exc-only-base]
+    strategy:
+      matrix:
+        test-model: ['BASE_BASE_GRAY']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
+  test-for-base-exc-base-gray:
+    name: Test for pull base gray for base exclude rule
+    runs-on: ubuntu-latest
+    if: needs.set-execution-conditions.outputs.enableMqGrayscaleRocketMqAction=='true'
+    needs: [set-execution-conditions, build-agent-and-cache, download-midwares-and-cache, test-for-base-base-gray]
+    strategy:
+      matrix:
+        test-model: ['BASE_EXC_BASE_GRAY']
+        rocketMqClientVersion: ["4.8.0", "4.9.8", "5.1.4", "5.2.0", "5.3.1"]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 100
+      - name: set java version to environment
+        run: |
+          echo "javaVersion=8" >> $GITHUB_ENV
+      - name: common operations
+        uses: ./.github/actions/common/mq-grayscale/rocketmq
+      - name: message gray test for test-model=${{ matrix.test-model }} rocketMqClientVersion=${{ matrix.rocketMqClientVersion }}
+        uses: ./.github/actions/scenarios/mq-grayscale/rocketmq
diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRetryPolicyTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRetryPolicyTest.java
index 235345ccd..def6954bc 100644
--- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRetryPolicyTest.java
+++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRetryPolicyTest.java
@@ -19,8 +19,6 @@ package io.sermant.core.service.xds.entity;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.util.Collections;
-
 /**
  * XdsRetryPolicyTest
  *
@@ -31,10 +29,12 @@ public class XdsRetryPolicyTest {
     @Test
     public void testXdsRetryPolicy() {
         XdsRetryPolicy xdsRetryPolicy = new XdsRetryPolicy();
-        xdsRetryPolicy.setRetryConditions(Collections.singletonList("503"));
+        xdsRetryPolicy.setRetryHostPredicate("PreviousHostsPredicate");
+        xdsRetryPolicy.setRetryOn("503");
         xdsRetryPolicy.setMaxAttempts(8);
         xdsRetryPolicy.setPerTryTimeout(2000);
-        Assert.assertEquals("503", xdsRetryPolicy.getRetryConditions().get(0));
+        Assert.assertEquals("PreviousHostsPredicate", xdsRetryPolicy.getRetryHostPredicate());
+        Assert.assertEquals("503", xdsRetryPolicy.getRetryOn());
         Assert.assertEquals(8, xdsRetryPolicy.getMaxAttempts());
         Assert.assertEquals(2000, xdsRetryPolicy.getPerTryTimeout());
     }
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/pom.xml b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/pom.xml
new file mode 100644
index 000000000..0e92b41e4
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>mq-grayscale-rocketmq-test</artifactId>
+        <groupId>io.sermant.integration</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>grayscale-rocketmq-consumer-demo</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerApplication.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerApplication.java
new file mode 100644
index 000000000..db4b9ae9a
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerApplication.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * springboot starter
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ **/
+@SpringBootApplication
+public class RocketMqConsumerApplication {
+    /**
+     * main
+     *
+     * @param args parameters
+     */
+    public static void main(String[] args) {
+        SpringApplication.run(RocketMqConsumerApplication.class, args);
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerController.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerController.java
new file mode 100644
index 000000000..35de7388b
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqConsumerController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * consumer controller
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ **/
+@RestController
+public class RocketMqConsumerController {
+    @Value("${rocketmq.address}")
+    private String nameServer;
+
+    @Value("${rocketmq.topic}")
+    private String topic;
+
+    @Autowired
+    private RocketMqPullConsumer pullConsumer;
+
+    @Autowired
+    private RocketMqPushConsumer pushConsumer;
+
+    @Autowired
+    private RocketMqLitePullConsumer litePullConsumer;
+
+    /**
+     * clear cache count info
+     *
+     * @return is success
+     */
+    @GetMapping("/clearMessageCount")
+    public String clearMessageCount() {
+        RocketMqMessageUtils.clearMessageCount();
+        return "success";
+    }
+
+    /**
+     * init consumer
+     *
+     * @param consumerType consumerType
+     * @return init status
+     */
+    @GetMapping("/initConsumer")
+    public String initConsumer(@RequestParam("consumerType") String consumerType) {
+        if ("PUSH".equals(consumerType)) {
+            pushConsumer.initPushConsumer(topic + "-PUSH", nameServer);
+        } else if ("PULL".equals(consumerType)) {
+            pullConsumer.initPullConsumer(nameServer, topic + "-PULL");
+        } else {
+            litePullConsumer.initLitePullConsumer(topic + "-LITE-PULL", nameServer);
+        }
+        return "success";
+    }
+
+    /**
+     * shutdown consumer
+     *
+     * @param consumerType consumerType
+     * @return status
+     */
+    @GetMapping("/shutdownConsumer")
+    public String shutdownConsumer(@RequestParam("consumerType") String consumerType) {
+        if ("PUSH".equals(consumerType)) {
+            pushConsumer.shutdownPushConsumer();
+        } else if ("PULL".equals(consumerType)) {
+            pullConsumer.shutdownPullConsumer();
+        } else {
+            litePullConsumer.shutdownLitePullConsumer();
+        }
+        return "ok";
+    }
+
+    /**
+     * pull message
+     *
+     * @return message count
+     */
+    @GetMapping("/getPullConsumerMessageCount")
+    public Map<String, Object> getPullConsumerMessageCount() {
+        return pullConsumer.getMessageCount();
+    }
+
+    /**
+     * get push consumer message count
+     *
+     * @return message count
+     */
+    @GetMapping("/getPushConsumerMessageCount")
+    public Map<String, Object> getPushConsumerMessageCount() {
+        return pushConsumer.getMessageCount(topic + "-PUSH", nameServer);
+    }
+
+    /**
+     * get lite pull consumer message count
+     *
+     * @return message count
+     */
+    @GetMapping("/getLitePullConsumerMessageCount")
+    public Map<String, Object> getLitePullConsumerMessageCount() {
+        return litePullConsumer.getMessageCount(topic + "-LITE-PULL", nameServer);
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqLitePullConsumer.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqLitePullConsumer.java
new file mode 100644
index 000000000..f361fdc2b
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqLitePullConsumer.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * litePull consumer
+ *
+ * @author chengyouling
+ * @since 2024-11-30
+ **/
+@Component
+public class RocketMqLitePullConsumer {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RocketMqLitePullConsumer.class);
+
+    private DefaultLitePullConsumer litePullConsumer;
+
+    private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+    private final long delay = 2L;
+
+    private final long initialDelay = 0L;
+
+    /**
+     * init lite pull consumer
+     *
+     * @param mqTopic topic
+     * @param mqAddress address
+     */
+    public void initLitePullConsumer(String mqTopic, String mqAddress) {
+        if (litePullConsumer == null) {
+            try {
+                litePullConsumer = new DefaultLitePullConsumer("default");
+                litePullConsumer.setNamesrvAddr(mqAddress);
+                litePullConsumer.subscribe(mqTopic, "*");
+                litePullConsumer.start();
+                executorService.scheduleWithFixedDelay(new LitePullRunnable(litePullConsumer), initialDelay, delay,
+                        TimeUnit.SECONDS);
+            } catch (MQClientException e) {
+                LOGGER.error("init lite pull consumer error!", e);
+            }
+        }
+    }
+
+    /**
+     * get lite pull message count
+     *
+     * @param mqTopic topic
+     * @param mqAddress address
+     * @return message count
+     */
+    public Map<String, Object> getMessageCount(String mqTopic, String mqAddress) {
+        if (litePullConsumer == null) {
+            initLitePullConsumer(mqTopic, mqAddress);
+        }
+        return RocketMqMessageUtils.getMessageCount();
+    }
+
+    /**
+     * shutdown consumer
+     */
+    public void shutdownLitePullConsumer() {
+        executorService.shutdown();
+        if (litePullConsumer != null) {
+            litePullConsumer.shutdown();
+        }
+    }
+
+    /**
+     * lite pull consumer runnable
+     *
+     * @author chengyouling
+     * @since 2024-11-30
+     **/
+    static class LitePullRunnable implements Runnable {
+        private final DefaultLitePullConsumer litePullConsumer;
+
+        private final long pullTimeout = 30000L;
+
+        LitePullRunnable(DefaultLitePullConsumer litePullConsumer) {
+            this.litePullConsumer = litePullConsumer;
+        }
+
+        @Override
+        public void run() {
+            List<MessageExt> messageExts = litePullConsumer.poll(pullTimeout);
+            messageExts.forEach(RocketMqMessageUtils::convertMessageCount);
+        }
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqMessageUtils.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqMessageUtils.java
new file mode 100644
index 000000000..13bc301e5
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqMessageUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.apache.rocketmq.common.message.MessageExt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * message count utils
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ */
+public class RocketMqMessageUtils {
+    private static final String GRAY = "gray";
+
+    private static int grayMessageCount = 0;
+
+    private static int baseMessageCount = 0;
+
+    private RocketMqMessageUtils() {
+    }
+
+    /**
+     * convert base/gray message
+     *
+     * @param messageExt MessageExt
+     */
+    public static void convertMessageCount(MessageExt messageExt) {
+        if (messageExt.getProperties() != null && GRAY.equals(messageExt.getProperties().get(
+                "x_lane_canary"))) {
+            grayMessageCount++;
+            return;
+        }
+        baseMessageCount++;
+    }
+
+    /**
+     * get base/gray message count
+     *
+     * @return message count
+     */
+    public static Map<String, Object> getMessageCount() {
+        Map<String, Object> countMap = new HashMap<>();
+        countMap.put("baseMessageCount", baseMessageCount);
+        countMap.put("grayMessageCount", grayMessageCount);
+        return countMap;
+    }
+
+    /**
+     * clear cache count info
+     */
+    public static void clearMessageCount() {
+        baseMessageCount = 0;
+        grayMessageCount = 0;
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPullConsumer.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPullConsumer.java
new file mode 100644
index 000000000..100a0ea48
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPullConsumer.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.client.consumer.PullStatus;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.impl.consumer.PullResultExt;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * pull consumer
+ *
+ * @author chengyouling
+ * @since 2024-11-30
+ **/
+@Component
+public class RocketMqPullConsumer {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RocketMqPullConsumer.class);
+
+    private static final Map<MessageQueue, Long> OFF_SET_TABLE = new HashMap<>();
+
+    private final int maxReconsumeTimes = 10000;
+
+    private final long pullTimeout = 300000L;
+
+    private final long delay = 2L;
+
+    private final long initialDelay = 0L;
+
+    private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+
+    private DefaultMQPullConsumer pullConsumer;
+
+    /**
+     * init pull consumer
+     *
+     * @param mqAddress address
+     * @param topic topic
+     */
+    public void initPullConsumer(String mqAddress, String topic) {
+        try {
+            if (pullConsumer == null) {
+                pullConsumer = new DefaultMQPullConsumer("default");
+                pullConsumer.setNamesrvAddr(mqAddress);
+                pullConsumer.setMaxReconsumeTimes(maxReconsumeTimes);
+                pullConsumer.setConsumerPullTimeoutMillis(pullTimeout);
+                pullConsumer.start();
+                executorService.scheduleWithFixedDelay(new PullRunnable(pullConsumer, topic), initialDelay, delay,
+                        TimeUnit.SECONDS);
+            }
+        } catch (MQClientException e) {
+            LOGGER.error("init pull consumer error!", e);
+        }
+    }
+
+    /**
+     * get pull message count
+     *
+     * @return message count
+     */
+    public Map<String, Object> getMessageCount() {
+        return RocketMqMessageUtils.getMessageCount();
+    }
+
+    private static void putMessageQueueOffSet(MessageQueue mq, PullResultExt pullResult) {
+        OFF_SET_TABLE.put(mq, pullResult.getNextBeginOffset());
+    }
+
+    private static long getMessageQueueOffSet(MessageQueue mq) {
+        return OFF_SET_TABLE.getOrDefault(mq, 0L);
+    }
+
+    /**
+     * shutdown consumer
+     */
+    public void shutdownPullConsumer() {
+        executorService.shutdown();
+        if (pullConsumer != null) {
+            pullConsumer.shutdown();
+        }
+    }
+
+    /**
+     * pull consumer runnable
+     *
+     * @author chengyouling
+     * @since 2024-11-30
+     **/
+    static class PullRunnable implements Runnable {
+        private final DefaultMQPullConsumer pullConsumer;
+
+        private final String topic;
+
+        private final int maxNums = 32;
+
+        PullRunnable(DefaultMQPullConsumer pullConsumer, String topic) {
+            this.pullConsumer = pullConsumer;
+            this.topic = topic;
+        }
+
+        @Override
+        public void run() {
+            try {
+                Set<MessageQueue> messageQueues = pullConsumer.fetchSubscribeMessageQueues(topic);
+                if (messageQueues.isEmpty()) {
+                    return;
+                }
+                for (MessageQueue mq : messageQueues) {
+                    PullResultExt pullResult = (PullResultExt) pullConsumer.pullBlockIfNotFound(mq, null,
+                            getMessageQueueOffSet(mq), maxNums);
+                    putMessageQueueOffSet(mq, pullResult);
+                    if (pullResult.getPullStatus() == PullStatus.FOUND) {
+                        List<MessageExt> messageExts = pullResult.getMsgFoundList();
+                        for (MessageExt messageExt: messageExts) {
+                            RocketMqMessageUtils.convertMessageCount(messageExt);
+                        }
+                    }
+                }
+            } catch (MQClientException | MQBrokerException | RemotingException | InterruptedException e) {
+                LOGGER.error("get pull message error!", e);
+            }
+        }
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPushConsumer.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPushConsumer.java
new file mode 100644
index 000000000..358f1bb46
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/consumer/RocketMqPushConsumer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.consumer;
+
+import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
+import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * push consumer
+ *
+ * @author chengyouling
+ * @since 2024-11-30
+ **/
+@Component
+public class RocketMqPushConsumer {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RocketMqPushConsumer.class);
+
+    private DefaultMQPushConsumer pushConsumer;
+
+    private final long pushTimeout = 300000L;
+
+    /**
+     * init push consumer
+     *
+     * @param mqTopic topic
+     * @param mqAddress address
+     */
+    public void initPushConsumer(String mqTopic, String mqAddress) {
+        try {
+            if (pushConsumer == null) {
+                pushConsumer = new DefaultMQPushConsumer("default");
+                pushConsumer.setNamesrvAddr(mqAddress);
+                pushConsumer.subscribe(mqTopic, "*");
+                pushConsumer.setConsumeTimeout(pushTimeout);
+                pushConsumer.registerMessageListener((MessageListenerConcurrently)(messages, context) -> {
+                    for (MessageExt messageExt : messages) {
+                        RocketMqMessageUtils.convertMessageCount(messageExt);
+                    }
+                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
+                });
+                pushConsumer.start();
+            }
+        } catch (MQClientException e) {
+            LOGGER.error("init push consumer error!", e);
+        }
+    }
+
+    /**
+     * get push message count
+     *
+     * @param mqTopic topic
+     * @param mqAddress address
+     * @return message count
+     */
+    public Map<String, Object> getMessageCount(String mqTopic, String mqAddress) {
+        if (pushConsumer == null) {
+            initPushConsumer(mqTopic, mqAddress);
+        }
+        return RocketMqMessageUtils.getMessageCount();
+    }
+
+    /**
+     * shutdown consumer
+     */
+    public void shutdownPushConsumer() {
+        if (pushConsumer != null) {
+            pushConsumer.shutdown();
+        }
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/resources/application.yaml b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/resources/application.yaml
new file mode 100644
index 000000000..2c8192617
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-consumer-demo/src/main/resources/application.yaml
@@ -0,0 +1,10 @@
+server:
+  port: 9000
+rocketmq:
+  address: 127.0.0.1:9876
+  topic: MESSAGE-GRAY
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/pom.xml b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/pom.xml
new file mode 100644
index 000000000..17cca01e7
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>mq-grayscale-rocketmq-test</artifactId>
+        <groupId>io.sermant.integration</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>grayscale-rocketmq-integration-test</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>5.3.20</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>2.0.9</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/GrayscaleRocketmqTest.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/GrayscaleRocketmqTest.java
new file mode 100644
index 000000000..fee26aa65
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/GrayscaleRocketmqTest.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.integration;
+
+import io.sermant.demo.grayscale.rocketmq.integration.support.KieClient;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.fastjson.JSON;
+
+/**
+ * test method class
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ */
+public class GrayscaleRocketmqTest {
+    private static final RestTemplate restTemplate = new RestTemplate();
+
+    private static final KieClient kieClient = new KieClient(restTemplate);
+
+    private static final String CONFIG_KEY = "grayscale.mq.config";
+
+    private static final String CONSUMER_TYPE_PULL = "PULL";
+
+    private static final String CONSUMER_TYPE_LITE_PULL = "LITE-PULL";
+
+    private static final String CONSUMER_TYPE_PUSH = "PUSH";
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "PLUGIN_ENABLED_FALSE")
+    public void testPluginEnabledFalseConsumeMessage() throws InterruptedException {
+        testPluginEnabledFalsePull();
+        Thread.sleep(10000);
+        testPluginEnabledFalseLitePull();
+        Thread.sleep(10000);
+        testPluginEnabledFalsePush();
+    }
+
+    private void testPluginEnabledFalsePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 2 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PULL);
+    }
+
+    private void testPluginEnabledFalseLitePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 2 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testPluginEnabledFalsePush() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 2 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PUSH);
+    }
+
+    private int parseBaseMessageCount(String messageCount) {
+        return (int) JSON.parseObject(messageCount).get("baseMessageCount");
+    }
+
+    private int parseGrayMessageCount(String messageCount) {
+        return (int) JSON.parseObject(messageCount).get("grayMessageCount");
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_ONLY_BASE")
+    public void testAutoOnlyBaseConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "");
+        testAutoOnlyBasePull();
+        Thread.sleep(10000);
+        testAutoOnlyBaseLitePull();
+        Thread.sleep(10000);
+        testAutoOnlyBasePush();
+    }
+
+    private void testAutoOnlyBasePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_PULL);
+    }
+
+    private void testAutoOnlyBaseLitePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testAutoOnlyBasePush() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_EXC_ONLY_BASE")
+    public void testAutoExcOnlyBaseConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "gray");
+        testAutoExcOnlyBasePull();
+        Thread.sleep(10000);
+        testAutoExcOnlyBaseLitePull();
+        Thread.sleep(10000);
+        testAutoExcOnlyBasePush();
+    }
+
+    private void testAutoExcOnlyBasePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PULL);
+    }
+
+    private void testAutoExcOnlyBaseLitePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testAutoExcOnlyBasePush() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_BASE_GRAY")
+    public void testAutoBaseGrayConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "");
+        testAutoBaseGrayPull();
+        Thread.sleep(10000);
+        testAutoBaseGrayLitePull();
+        Thread.sleep(10000);
+        testAutoBaseGrayPush();
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_BASE_GRAY_PULL")
+    public void testAutoBaseGrayPull() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "");
+        initAndProduceMessage(true, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String grayResult = getPullGrayResult();
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && grayGrayCount == 1);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_BASE_GRAY_LITE_PULL")
+    public void testAutoBaseGrayLitePull() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "");
+        initAndProduceMessage(true, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String grayResult = getLitePullGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_BASE_GRAY_PUSH")
+    public void testAutoBaseGrayPush() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "");
+        initAndProduceMessage(true, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPushGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "AUTO_EXC_BASE_GRAY")
+    public void testAutoExcBaseGrayConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("AUTO", "gray");
+        testAutoExcBaseGrayPull();
+        Thread.sleep(10000);
+        testAutoExcBaseGrayLitePull();
+        Thread.sleep(10000);
+        testAutoExcBaseGrayPush();
+    }
+
+    private void testAutoExcBaseGrayPull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String grayResult = getPullGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_PULL);
+    }
+
+    private void testAutoExcBaseGrayLitePull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getLitePullGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testAutoExcBaseGrayPush() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPushGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "BASE_ONLY_BASE")
+    public void testBaseOnlyBaseConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("BASE", "");
+        testBaseOnlyBasePull();
+        Thread.sleep(10000);
+        testBaseOnlyBaseLitePull();
+        Thread.sleep(10000);
+        testBaseOnlyBasePush();
+    }
+
+    private void testBaseOnlyBasePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_PULL);
+    }
+
+    private void testBaseOnlyBaseLitePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testBaseOnlyBasePush() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 1);
+        shutdownConsumer(false, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "BASE_EXC_ONLY_BASE")
+    public void testBaseExcOnlyBaseConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("BASE", "gray");
+        testBaseExcOnlyBasePull();
+        Thread.sleep(10000);
+        testBaseExcOnlyBaseLitePull();
+        Thread.sleep(10000);
+        testBaseExcOnlyBasePush();
+    }
+
+    private void testBaseExcOnlyBasePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PULL);
+    }
+
+    private void testBaseExcOnlyBaseLitePull() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testBaseExcOnlyBasePush() throws InterruptedException {
+        initAndProduceMessage(false, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        Assertions.assertTrue( baseBaseCount == 1 && baseGrayCount == 0);
+        shutdownConsumer(false, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "BASE_BASE_GRAY")
+    public void testBaseBaseGrayConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("BASE", "");
+        testBaseBaseGrayPull();
+        Thread.sleep(10000);
+        testBaseBaseGrayLitePull();
+        Thread.sleep(10000);
+        testBaseBaseGrayPush();
+    }
+
+    private void testBaseBaseGrayPull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPullGrayResult();
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1 && grayGrayCount == 1 && grayBaseCount == 0);
+        shutdownConsumer(true, CONSUMER_TYPE_PULL);
+    }
+
+    private void testBaseBaseGrayLitePull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getLitePullGrayResult();
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1 && grayGrayCount == 1 && grayBaseCount == 0);
+        shutdownConsumer(true, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testBaseBaseGrayPush() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPushGrayResult();
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 1 && grayGrayCount == 1 && grayBaseCount == 0);
+        shutdownConsumer(true, CONSUMER_TYPE_PUSH);
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "grayscale.rocketmq.integration.test.type", matches = "BASE_EXC_BASE_GRAY")
+    public void testBaseExcBaseGrayConsumeMessage() throws InterruptedException {
+        createGrayscaleConfig("BASE", "gray");
+        testBaseExcBaseGrayPull();
+        Thread.sleep(10000);
+        testBaseExcBaseGrayLitePull();
+        Thread.sleep(10000);
+        testBaseExcBaseGrayPush();
+    }
+
+    private void testBaseExcBaseGrayPull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PULL);
+        Thread.sleep(50000);
+        String baseResult = getPullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPullGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_PULL);
+    }
+
+    private void testBaseExcBaseGrayLitePull() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_LITE_PULL);
+        Thread.sleep(180000);
+        String baseResult = getLitePullBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getLitePullGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_LITE_PULL);
+    }
+
+    private void testBaseExcBaseGrayPush() throws InterruptedException {
+        initAndProduceMessage(true, CONSUMER_TYPE_PUSH);
+        Thread.sleep(180000);
+        String baseResult = getPushBaseResult();
+        int baseBaseCount = parseBaseMessageCount(baseResult);
+        int baseGrayCount = parseGrayMessageCount(baseResult);
+        String grayResult = getPushGrayResult();
+        int grayBaseCount = parseBaseMessageCount(grayResult);
+        int grayGrayCount = parseGrayMessageCount(grayResult);
+        Assertions.assertTrue(baseBaseCount == 1 && baseGrayCount == 0 && grayBaseCount == 0 && grayGrayCount == 1);
+        shutdownConsumer(true, CONSUMER_TYPE_PUSH);
+    }
+
+    private void initAndProduceMessage(boolean isGrayInstanceInit, String consumerType)
+            throws InterruptedException {
+        clearCacheCount(isGrayInstanceInit);
+        initProducer();
+        produceMessage(consumerType);
+        if (isGrayInstanceInit) {
+            // Trigger start gray consumer.
+            restTemplate.getForObject("http://127.0.0.1:9010/initConsumer?consumerType={1}", String.class,
+                    consumerType);
+            Thread.sleep(50000);
+        }
+
+        // Trigger start base consumer.
+        restTemplate.getForObject("http://127.0.0.1:9000/initConsumer?consumerType={1}", String.class, consumerType);
+    }
+
+    private void shutdownConsumer(boolean isGrayInstanceInit, String consumerType) {
+        if (isGrayInstanceInit) {
+            restTemplate.getForObject("http://127.0.0.1:9010/shutdownConsumer?consumerType={1}", String.class,
+                    consumerType);
+        }
+        restTemplate.getForObject("http://127.0.0.1:9000/shutdownConsumer?consumerType={1}", String.class,
+                consumerType);
+    }
+
+    private String getPullBaseResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9000/getPullConsumerMessageCount", String.class);
+    }
+
+    private String getPushBaseResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9000/getPushConsumerMessageCount", String.class);
+    }
+
+    private String getLitePullBaseResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9000/getLitePullConsumerMessageCount", String.class);
+    }
+
+    private String getPullGrayResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9010/getPullConsumerMessageCount", String.class);
+    }
+
+    private String getPushGrayResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9010/getPushConsumerMessageCount", String.class);
+    }
+
+    private String getLitePullGrayResult() {
+        return restTemplate.getForObject("http://127.0.0.1:9010/getLitePullConsumerMessageCount", String.class);
+    }
+
+    private void initProducer() {
+        restTemplate.getForObject("http://127.0.0.1:9030/initProducer", String.class);
+        restTemplate.getForObject("http://127.0.0.1:9040/initProducer", String.class);
+    }
+
+    private void produceMessage(String consumerType) {
+        String path = consumerType.equals(CONSUMER_TYPE_PULL) ? "producePullMessage" :
+                consumerType.equals(CONSUMER_TYPE_LITE_PULL) ? "produceLitePullMessage" : "producePushMessage";
+        restTemplate.getForObject("http://127.0.0.1:9030/" + path + "?message={1}", String.class, "message");
+        restTemplate.getForObject("http://127.0.0.1:9040/" + path + "?message={1}", String.class, "gray-message");
+    }
+
+    private void clearCacheCount(boolean isGrayInstanceInit) {
+        if (isGrayInstanceInit) {
+            restTemplate.getForObject("http://127.0.0.1:9010/clearMessageCount", String.class);
+        }
+        restTemplate.getForObject("http://127.0.0.1:9000/clearMessageCount", String.class);
+    }
+
+    private void createGrayscaleConfig(String consumeMode, String excludeTag) {
+        String CONTENT = "enabled: true\n"
+                + "grayscale:\n"
+                + "  - consumerGroupTag: gray\n"
+                + "    serviceMeta:\n"
+                + "      version: 1.0.1\n"
+                + "    trafficTag:\n"
+                + "      x_lane_canary: gray\n"
+                + "base:\n"
+                + "  autoCheckDelayTime: 5\n"
+                + "  consumeMode: " + consumeMode + "\n"
+                + "  excludeGroupTags: [\""+ excludeTag + "\"]\n";
+        Assertions.assertTrue(kieClient.publishConfig(CONFIG_KEY, CONTENT));
+    }
+
+    @AfterEach
+    public void deleteGrayscaleConfig() {
+        try {
+            kieClient.deleteKey(CONFIG_KEY);
+            TimeUnit.SECONDS.sleep(2);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/KieClient.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/KieClient.java
new file mode 100644
index 000000000..76ea11824
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/KieClient.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.integration.support;
+
+import com.alibaba.fastjson.JSONObject;
+
+import io.sermant.demo.grayscale.rocketmq.integration.support.entity.KieConfigEntity;
+import io.sermant.demo.grayscale.rocketmq.integration.support.entity.KieResponse;
+import io.sermant.demo.grayscale.rocketmq.integration.support.utils.LabelGroupUtils;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Kie response client
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ */
+public class KieClient {
+    private final RestTemplate restTemplate;
+
+    private final String url;
+
+    private final Map<String, String> labels;
+
+    /**
+     * 构造函数
+     *
+     * @param restTemplate 请求器
+     */
+    public KieClient(RestTemplate restTemplate) {
+        this(restTemplate, null);
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param restTemplate 请求器
+     * @param url 地址
+     */
+    public KieClient(RestTemplate restTemplate, String url) {
+        this(restTemplate, url, null);
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param restTemplate 请求器
+     * @param url 地址
+     * @param labels 标签
+     */
+    public KieClient(RestTemplate restTemplate, String url, Map<String, String> labels) {
+        this.restTemplate = restTemplate;
+        this.url = url == null ? "http://127.0.0.1:30110/v1/default/kie/kv" : url;
+        this.labels = labels;
+    }
+
+    /**
+     * 发布配置
+     *
+     * @param key 键
+     * @param value 值
+     * @return 是否发布成功
+     */
+    public boolean publishConfig(String key, String value) {
+        final KieConfigEntity configEntity = queryTargetKeyId(key);
+        if (configEntity != null) {
+            // 更新操作
+            return updateKey(configEntity, value);
+        } else {
+            // 新增操作
+            return addKey(key, value);
+        }
+    }
+
+    /**
+     * 更新配置
+     *
+     * @param configEntity 查询的config信息
+     * @param value 新的配置内容
+     * @return 是否更新成功
+     */
+    public boolean updateKey(KieConfigEntity configEntity, String value) {
+        HttpHeaders headers = new HttpHeaders();
+        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+        headers.setContentType(type);
+        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
+
+        HttpEntity<String> entity = new HttpEntity<>(JSONObject.toJSONString(new KieRequest(configEntity.getKey(),
+                value, labels)),
+                headers);
+        String address = this.url + "/" + configEntity.getId();
+        restTemplate.put(address, entity);
+        return true;
+    }
+
+    /**
+     * 根据key删除配置
+     *
+     * @param key 键
+     * @return 是否删除成功
+     */
+    public boolean deleteKey(String key) {
+        final KieConfigEntity configEntity = this.queryTargetKeyId(key);
+        if (configEntity == null) {
+            return false;
+        }
+        String address = this.url + "/" + configEntity.getId();
+        restTemplate.delete(address);
+        return true;
+    }
+
+    /**
+     * 添加新配置
+     *
+     * @param key 键
+     * @param value 值
+     * @return 是否添加成功
+     */
+    public boolean addKey(String key, String value) {
+        HttpHeaders headers = new HttpHeaders();
+        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+        headers.setContentType(type);
+        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
+
+        HttpEntity<String> entity = new HttpEntity<>(JSONObject.toJSONString(new KieRequest(key, value, labels)), headers);
+
+        try {
+            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, entity, String.class);
+            return responseEntity.getStatusCode() == HttpStatus.OK;
+        } catch (HttpClientErrorException ex) {
+            // ignored
+        }
+        return false;
+    }
+
+    private KieConfigEntity queryTargetKeyId(String key) {
+        final List<KieConfigEntity> query = query(null);
+        return query.stream().filter(kieConfigEntity -> kieConfigEntity.getKey().equals(key))
+                .findFirst().orElse(null);
+    }
+
+    /**
+     * 根据标签查询kie配置
+     *
+     * @param labels 新增标签
+     * @return kie响应
+     */
+    public List<KieConfigEntity> query(Map<String, String> labels) {
+        Map<String, String> curLabels = labels;
+        if (labels == null) {
+            curLabels = this.labels;
+        }
+        if (curLabels == null) {
+            curLabels = new HashMap<>();
+            curLabels.put("app", "default");
+            curLabels.put("environment", "development");
+        }
+        final String labelGroup = LabelGroupUtils.createLabelGroup(curLabels);
+        final String labelCondition = LabelGroupUtils.getLabelCondition(labelGroup);
+        String address = this.url + "?" + labelCondition + "&match=exact&revision=";
+        final ResponseEntity<String> configs = restTemplate.getForEntity(address, String.class);
+        JSONObject result = JSONObject.parseObject(configs.getBody());
+        if (configs.getStatusCode().value() == 200) {
+            final KieResponse kieResponse = result.toJavaObject(KieResponse.class);
+            return kieResponse.getData();
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * kie请求
+     *
+     * @since 2022-07-12
+     */
+    static class KieRequest implements Serializable {
+        private String key;
+        private String value;
+        private Map<String, String> labels;
+        private String status = "enabled";
+
+        public KieRequest(String key, String value, Map<String, String> labels) {
+            this.key = key;
+            this.value = value;
+            this.labels = labels;
+            if (this.labels == null) {
+                this.labels = new HashMap<>();
+                this.labels.put("app", "default");
+                this.labels.put("environment", "development");
+            }
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        public Map<String, String> getLabels() {
+            return labels;
+        }
+
+        public void setLabels(Map<String, String> labels) {
+            this.labels = labels;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        public void setStatus(String status) {
+            this.status = status;
+        }
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieConfigEntity.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieConfigEntity.java
new file mode 100644
index 000000000..d5035f3df
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/entity/KieConfigEntity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.integration.support.entity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Kie response entity
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ */
+public class KieConfigEntity {
+    private String id;
+    private String key;
+    private Map<String, String> labels = new HashMap<String, String>();
+    private String value;
+    private String valueType;
+    private String status;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(Map<String, String> labels) {
+        this.labels = labels;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getValueType() {
+        return valueType;
+    }
+
+    public void setValueType(String valueType) {
+        this.valueType = valueType;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    @Override
+    public String toString() {
+        return "KieConfigEntity{" +
+                "id='" + id + '\'' +
+                ", key='" + key + '\'' +
+                ", labels=" + labels +
+                ", value='" + value + '\'' +
+                ", valueType='" + valueType + '\'' +
+                ", status='" + status + '\'' +
+                '}';
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/utils/LabelGroupUtils.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/utils/LabelGroupUtils.java
new file mode 100644
index 000000000..daabdfd9b
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-integration-test/src/test/java/io/sermant/demo/grayscale/rocketmq/integration/support/utils/LabelGroupUtils.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.integration.support.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * group生成工具
+ *
+ * @author zhouss
+ * @since 2021-11-23
+ */
+public class LabelGroupUtils {
+    private static final String GROUP_SEPARATOR = "&";
+
+    private static final String KV_SEPARATOR = "=";
+
+    /**
+     * 查询时使用的kv分隔符
+     */
+    private static final String LABEL_QUERY_SEPARATOR = ":";
+
+    /**
+     * 查询标签前缀
+     */
+    private static final String LABEL_PREFIX = "label=";
+
+    /**
+     * 键值对长度
+     */
+    private static final int KV_LEN = 2;
+
+    /**
+     * 默认组
+     */
+    private static final String DEFAULT_GROUP_KEY = "GROUP";
+
+    private LabelGroupUtils() {
+    }
+
+    /**
+     * 创建标签组
+     *
+     * @param labels 标签组
+     * @return labelGroup 例如: app=sc&service=helloService
+     */
+    public static String createLabelGroup(Map<String, String> labels) {
+        if (labels == null || labels.isEmpty()) {
+            return "";
+        }
+        final StringBuilder group = new StringBuilder();
+        final List<String> keys = new ArrayList<>(labels.keySet());
+
+        // 防止相同map因排序不同而导致最后的label不一致
+        Collections.sort(keys);
+        for (String key : keys) {
+            String value = labels.get(key);
+            if (key == null || value == null) {
+                continue;
+            }
+            group.append(key).append(KV_SEPARATOR).append(value).append(GROUP_SEPARATOR);
+        }
+        if (group.length() == 0) {
+            return "";
+        }
+        return group.deleteCharAt(group.length() - 1).toString();
+    }
+
+    /**
+     * 重组group, 防止因多个标签因顺序问题而导致group不同
+     *
+     * @param group 标签组
+     * @return group
+     */
+    public static String rebuildGroup(String group) {
+        if (isLabelGroup(group)) {
+            return createLabelGroup(resolveGroupLabels(group));
+        }
+        return LabelGroupUtils.createLabelGroup(Collections.singletonMap(DEFAULT_GROUP_KEY, group));
+    }
+
+    /**
+     * 是否为标签组key
+     *
+     * @param group 监听键
+     * @return 是否为标签组
+     */
+    public static boolean isLabelGroup(String group) {
+        return group != null && group.contains(KV_SEPARATOR);
+    }
+
+    /**
+     * 解析标签为map
+     *
+     * @param group 标签组  app=sc&service=helloService
+     * @return 标签键值对, 返回键值将会是有序的
+     */
+    public static Map<String, String> resolveGroupLabels(String group) {
+        final Map<String, String> result = new LinkedHashMap<>();
+        if (group == null) {
+            return result;
+        }
+        String curGroup = group;
+        if (!isLabelGroup(curGroup)) {
+            // 如果非group标签（ZK配置中心场景适配），则为该group创建标签
+            curGroup = LabelGroupUtils.createLabelGroup(Collections.singletonMap(DEFAULT_GROUP_KEY, curGroup));
+        }
+        try {
+            final String decode = URLDecoder.decode(curGroup, "UTF-8");
+            final String[] labels = decode.split("&");
+            for (String label : labels) {
+                final String[] labelKv = label.split("=");
+                if (labelKv.length == KV_LEN) {
+                    result.put(labelKv[0], labelKv[1]);
+                } else if (labelKv.length == 1) {
+                    // 仅配置了KEY的情况, 使用空串代替
+                    result.put(labelKv[0], "");
+                }
+            }
+        } catch (UnsupportedEncodingException ignored) {
+            // ignored
+        }
+        return result;
+    }
+
+    /**
+     * 获取标签信息
+     *
+     * @param group 分组  app=sc&service=helloService转换label=app:sc&label=service:helloService
+     * @return 标签组条件
+     */
+    public static String getLabelCondition(String group) {
+        if (group == null || "".equals(group)) {
+            return group;
+        }
+        String curGroup = rebuildGroup(group);
+        final Map<String, String> labels = resolveGroupLabels(curGroup);
+        final StringBuilder finalGroup = new StringBuilder();
+        for (Map.Entry<String, String> entry : labels.entrySet()) {
+            finalGroup.append(LABEL_PREFIX)
+                .append(buildSingleLabel(entry.getKey(), entry.getValue()))
+                .append(GROUP_SEPARATOR);
+        }
+        return finalGroup.deleteCharAt(finalGroup.length() - 1).toString();
+    }
+
+    private static String buildSingleLabel(String key, String value) {
+        return key + LABEL_QUERY_SEPARATOR + value;
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/pom.xml b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/pom.xml
new file mode 100644
index 000000000..98a7d4752
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>mq-grayscale-rocketmq-test</artifactId>
+        <groupId>io.sermant.integration</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>grayscale-rocketmq-producer-demo</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/MqProducerController.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/MqProducerController.java
new file mode 100644
index 000000000..d355fa90e
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/MqProducerController.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.producer;
+
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * producer controller
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ */
+@RestController
+public class MqProducerController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MqProducerController.class);
+
+    @Value("${rocketmq.address}")
+    private String mqAddress;
+
+    @Value("${rocketmq.topic}")
+    private String mqTopic;
+
+    private DefaultMQProducer producer;
+
+    private final String errorMessage = "error";
+
+    private final String successMessage = "success";
+
+    private final int sendMsgTimeout = 60000;
+
+    /**
+     * init push producer
+     *
+     * @return is success
+     */
+    @GetMapping("/initProducer")
+    public String initProducer() {
+        try {
+            if (producer == null) {
+                producer = new DefaultMQProducer("default");
+                producer.setNamesrvAddr(mqAddress);
+                producer.setSendMsgTimeout(sendMsgTimeout);
+                producer.start();
+            }
+        } catch (MQClientException e) {
+            LOGGER.error("init pull producer error!", e);
+        }
+        return successMessage;
+    }
+
+    /**
+     * pull producer produce message
+     *
+     * @param message message
+     * @return is success
+     */
+    @GetMapping("/producePullMessage")
+    public String producePullMessage(@RequestParam("message") String message) {
+        try {
+            if (producer == null) {
+                initProducer();
+            }
+            Message mqMessage = new Message(mqTopic + "-PULL", message.getBytes(StandardCharsets.UTF_8));
+            producer.send(mqMessage);
+        } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
+            LOGGER.error("send pull message error, address={}, message={}", mqAddress, message, e);
+            return errorMessage;
+        }
+        return successMessage;
+    }
+
+    /**
+     * lite pull producer produce message
+     *
+     * @param message message
+     * @return is success
+     */
+    @GetMapping("/produceLitePullMessage")
+    public String produceLitePullMessage(@RequestParam("message") String message) {
+        try {
+            if (producer == null) {
+                initProducer();
+            }
+            Message mqMessage = new Message(mqTopic + "-LITE-PULL", message.getBytes(StandardCharsets.UTF_8));
+            producer.send(mqMessage);
+        } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
+            LOGGER.error("send lite pull message error, address={}, message={}", mqAddress, message, e);
+            return errorMessage;
+        }
+        return successMessage;
+    }
+
+    /**
+     * push producer produce message
+     *
+     * @param message message
+     * @return is success
+     */
+    @GetMapping("/producePushMessage")
+    public String producePushMessage(@RequestParam("message") String message) {
+        try {
+            if (producer == null) {
+                initProducer();
+            }
+            Message mqMessage = new Message(mqTopic + "-PUSH", message.getBytes(StandardCharsets.UTF_8));
+            producer.send(mqMessage);
+        } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
+            LOGGER.error("send push message error, address={}, message={}", mqAddress, message, e);
+            return errorMessage;
+        }
+        return successMessage;
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/RocketMqProducerApplication.java b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/RocketMqProducerApplication.java
new file mode 100644
index 000000000..4c9d33b13
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/java/io/sermant/demo/grayscale/rocketmq/producer/RocketMqProducerApplication.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ *   Licensed 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 io.sermant.demo.grayscale.rocketmq.producer;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * springboot starer
+ *
+ * @author chengyouling
+ * @since 2024-10-30
+ **/
+@SpringBootApplication
+public class RocketMqProducerApplication {
+    /**
+     * 启动类
+     *
+     * @param args 进程启动入参
+     */
+    public static void main(String[] args) {
+        SpringApplication.run(RocketMqProducerApplication.class, args);
+    }
+}
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/resources/application.yaml b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/resources/application.yaml
new file mode 100644
index 000000000..5351a0272
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/grayscale-rocketmq-producer-demo/src/main/resources/application.yaml
@@ -0,0 +1,10 @@
+server:
+  port: 9030
+rocketmq:
+  address: 127.0.0.1:9876
+  topic: MESSAGE-GRAY
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"
diff --git a/sermant-integration-tests/mq-grayscale-rocketmq-test/pom.xml b/sermant-integration-tests/mq-grayscale-rocketmq-test/pom.xml
new file mode 100644
index 000000000..6f304c109
--- /dev/null
+++ b/sermant-integration-tests/mq-grayscale-rocketmq-test/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.15</version>
+        <relativePath/>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>io.sermant.integration</groupId>
+    <artifactId>mq-grayscale-rocketmq-test</artifactId>
+    <version>1.0.0</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <spring-boot.version>2.7.15</spring-boot.version>
+        <spring-cloud.version>2021.0.9</spring-cloud.version>
+        <rocketmq-client.version>5.0.0</rocketmq-client.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.rocketmq</groupId>
+                <artifactId>rocketmq-client</artifactId>
+                <version>${rocketmq-client.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.rocketmq</groupId>
+                <artifactId>rocketmq-common</artifactId>
+                <version>${rocketmq-client.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <modules>
+        <module>grayscale-rocketmq-integration-test</module>
+        <module>grayscale-rocketmq-producer-demo</module>
+        <module>grayscale-rocketmq-consumer-demo</module>
+    </modules>
+
+</project>
diff --git a/sermant-integration-tests/pom.xml b/sermant-integration-tests/pom.xml
index 2883cd1e4..d426e3f62 100644
--- a/sermant-integration-tests/pom.xml
+++ b/sermant-integration-tests/pom.xml
@@ -21,5 +21,6 @@
         <module>mq-consume-prohibition-test</module>
         <module>database-write-prohibition-test</module>
         <module>xds-service-test</module>
+        <module>mq-grayscale-rocketmq-test</module>
     </modules>
 </project>
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/handler/retry/RetryContextTest.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/handler/retry/RetryContextTest.java
index 3509cf6cb..eae232f73 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/handler/retry/RetryContextTest.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/handler/retry/RetryContextTest.java
@@ -17,7 +17,6 @@
 
 package io.sermant.flowcontrol.common.handler.retry;
 
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
 import io.sermant.flowcontrol.common.core.rule.RetryRule;
 
 import org.junit.Assert;
@@ -76,17 +75,7 @@ public class RetryContextTest {
     private Retry buildRetry() {
         return new Retry() {
             @Override
-            public boolean isNeedRetry(Set<String> statusList, Object result) {
-                return false;
-            }
-
-            @Override
-            public boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy) {
-                return false;
-            }
-
-            @Override
-            public boolean isNeedRetry(Throwable throwable, XdsRetryPolicy retryPolicy) {
+            public boolean needRetry(Set<String> statusList, Object result) {
                 return false;
             }
 
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/util/XdsAbstractTest.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/util/XdsAbstractTest.java
index fbf42d124..4d3d2ac67 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/util/XdsAbstractTest.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/util/XdsAbstractTest.java
@@ -124,7 +124,7 @@ public abstract class XdsAbstractTest {
         Mockito.when(xdsCoreService.getLoadBalanceService()).thenReturn(xdsLoadBalanceService);
         requestCircuitBreakers.setMaxRequests(1000);
         instanceCircuitBreakers.setInterval(1000);
-        retryPolicy.setRetryConditions(Collections.singletonList("503"));
+        retryPolicy.setRetryOn("503");
         rateLimit.setResponseHeaderOption(Collections.singletonList(new XdsHeaderOption()));
         XdsDelay delay = new XdsDelay();
         delay.setFixedDelay(1000);
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/xds/handler/XdsHandlerTest.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/xds/handler/XdsHandlerTest.java
index f977a2f68..756ae137e 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/xds/handler/XdsHandlerTest.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-common/src/test/java/io/sermant/flowcontrol/common/xds/handler/XdsHandlerTest.java
@@ -63,7 +63,7 @@ public class XdsHandlerTest extends XdsAbstractTest {
         final Optional<XdsRetryPolicy> result = XdsHandler.INSTANCE.getRetryPolicy(
                 SERVICE_NAME, ROUTE_NAME);
         Assert.assertTrue(result.isPresent());
-        Assert.assertEquals(retryPolicy.getRetryConditions().get(0), result.get().getRetryConditions().get(0));
+        Assert.assertEquals(retryPolicy.getRetryOn(), result.get().getRetryOn());
     }
 
     @Test
diff --git a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/test/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreatorTest.java b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/test/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreatorTest.java
index fd7263801..d11e04a48 100644
--- a/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/test/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreatorTest.java
+++ b/sermant-plugins/sermant-flowcontrol/flowcontrol-plugin/src/test/java/io/sermant/flowcontrol/retry/handler/DefaultRetryPredicateCreatorTest.java
@@ -18,7 +18,6 @@
 package io.sermant.flowcontrol.retry.handler;
 
 import feign.Response;
-import io.sermant.core.service.xds.entity.XdsRetryPolicy;
 import io.sermant.core.utils.ReflectUtils;
 import io.sermant.flowcontrol.common.core.rule.RetryRule;
 import io.sermant.flowcontrol.common.exception.InvokerWrapperException;
@@ -59,7 +58,7 @@ public class DefaultRetryPredicateCreatorTest {
     static class TestRetry implements Retry {
 
         @Override
-        public boolean isNeedRetry(Set<String> statusList, Object result) {
+        public boolean needRetry(Set<String> statusList, Object result) {
             final Optional<Object> status = ReflectUtils.invokeMethod(result, "status", null, null);
             if (status.isPresent()) {
                 final Object code = status.get();
@@ -68,16 +67,6 @@ public class DefaultRetryPredicateCreatorTest {
             return false;
         }
 
-        @Override
-        public boolean isNeedRetry(Object result, XdsRetryPolicy retryPolicy) {
-            return false;
-        }
-
-        @Override
-        public boolean isNeedRetry(Throwable throwable, XdsRetryPolicy retryPolicy) {
-            return false;
-        }
-
         @Override
         public Class<? extends Throwable>[] retryExceptions() {
             return new Class[0];
diff --git a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/CallableWrapperTest.java b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/CallableWrapperTest.java
index 0adda006b..445b4de8d 100644
--- a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/CallableWrapperTest.java
+++ b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/CallableWrapperTest.java
@@ -36,24 +36,18 @@ public class CallableWrapperTest extends BaseTest {
     public void testCanTransmit() throws Exception {
         Object obj = new Object();
         CallableWrapper<Object> wrapper = new CallableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
             return obj;
         }, new RequestTag(null), new RequestData(null, null, null), false);
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
 
         Assert.assertEquals(obj, wrapper.call());
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 
     @Test
@@ -67,10 +61,8 @@ public class CallableWrapperTest extends BaseTest {
 
         Object obj = new Object();
         CallableWrapper<Object> wrapper = new CallableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNull(ThreadLocalUtils.getRequestTag());
             return obj;
         }, new RequestTag(null), new RequestData(null, null, null), true);
 
@@ -79,9 +71,7 @@ public class CallableWrapperTest extends BaseTest {
 
         Assert.assertEquals(obj, wrapper.call());
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 }
diff --git a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableAndCallableWrapperTest.java b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableAndCallableWrapperTest.java
index 7b2b9d0cf..64785497c 100644
--- a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableAndCallableWrapperTest.java
+++ b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableAndCallableWrapperTest.java
@@ -35,23 +35,17 @@ public class RunnableAndCallableWrapperTest extends BaseTest {
     @Test
     public void testRunCanTransmit() {
         RunnableAndCallableWrapper<Object> wrapper = new RunnableAndCallableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
         }, null, new RequestTag(null), new RequestData(null, null, null), false);
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
 
         wrapper.run();
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 
     @Test
@@ -64,10 +58,8 @@ public class RunnableAndCallableWrapperTest extends BaseTest {
         Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
 
         RunnableWrapper<Object> wrapper = new RunnableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNull(ThreadLocalUtils.getRequestTag());
         }, new RequestTag(null), new RequestData(null, null, null), true);
 
         Assert.assertNull(ReflectUtils.getFieldValue(wrapper, "requestData").orElse(null));
@@ -75,34 +67,26 @@ public class RunnableAndCallableWrapperTest extends BaseTest {
 
         wrapper.run();
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 
     @Test
     public void testCallCanTransmit() throws Exception {
         Object obj = new Object();
         RunnableAndCallableWrapper<Object> wrapper = new RunnableAndCallableWrapper<>(null, () -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
             return obj;
         }, new RequestTag(null), new RequestData(null, null, null), false);
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
 
         Assert.assertEquals(obj, wrapper.call());
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 
     @Test
@@ -116,10 +100,8 @@ public class RunnableAndCallableWrapperTest extends BaseTest {
 
         Object obj = new Object();
         CallableWrapper<Object> wrapper = new CallableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNull(ThreadLocalUtils.getRequestTag());
             return obj;
         }, new RequestTag(null), new RequestData(null, null, null), true);
 
@@ -128,9 +110,7 @@ public class RunnableAndCallableWrapperTest extends BaseTest {
 
         Assert.assertEquals(obj, wrapper.call());
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 }
diff --git a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableWrapperTest.java b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableWrapperTest.java
index 2463bf4c7..ef7b8da4a 100644
--- a/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableWrapperTest.java
+++ b/sermant-plugins/sermant-router/router-transmit-plugin/src/test/java/io/sermant/router/transmit/wrapper/RunnableWrapperTest.java
@@ -35,23 +35,17 @@ public class RunnableWrapperTest extends BaseTest {
     @Test
     public void testCanTransmit() {
         RunnableWrapper<?> wrapper = new RunnableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
         }, new RequestTag(null), new RequestData(null, null, null), false);
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
 
         wrapper.run();
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 
     @Test
@@ -64,10 +58,8 @@ public class RunnableWrapperTest extends BaseTest {
         Assert.assertNotNull(ThreadLocalUtils.getRequestTag());
 
         RunnableWrapper<Object> wrapper = new RunnableWrapper<>(() -> {
-            Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                    || ThreadLocalUtils.getRequestData().getPath() == null);
-            Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                    || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+            Assert.assertNull(ThreadLocalUtils.getRequestData());
+            Assert.assertNull(ThreadLocalUtils.getRequestTag());
         }, new RequestTag(null), new RequestData(null, null, null), true);
 
         Assert.assertNull(ReflectUtils.getFieldValue(wrapper, "requestData").orElse(null));
@@ -75,9 +67,7 @@ public class RunnableWrapperTest extends BaseTest {
 
         wrapper.run();
 
-        Assert.assertTrue(ThreadLocalUtils.getRequestData() == null
-                || ThreadLocalUtils.getRequestData().getPath() == null);
-        Assert.assertTrue(ThreadLocalUtils.getRequestTag() == null
-                || ThreadLocalUtils.getRequestTag().getTag().isEmpty());
+        Assert.assertNull(ThreadLocalUtils.getRequestData());
+        Assert.assertNull(ThreadLocalUtils.getRequestTag());
     }
 }
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClientTest.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClientTest.java
deleted file mode 100644
index 023247cf5..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosDiscoveryClientTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package io.sermant.discovery.service.lb.discovery.nacos;
-
-import com.alibaba.nacos.api.exception.NacosException;
-import com.alibaba.nacos.api.naming.NamingMaintainService;
-import com.alibaba.nacos.api.naming.NamingService;
-import com.alibaba.nacos.api.naming.pojo.Instance;
-import com.alibaba.nacos.api.naming.pojo.ListView;
-import com.google.common.collect.Maps;
-import io.sermant.core.plugin.config.PluginConfigManager;
-import io.sermant.core.utils.ReflectUtils;
-import io.sermant.discovery.config.NacosRegisterConfig;
-import io.sermant.discovery.config.RegisterType;
-import io.sermant.discovery.entity.DefaultServiceInstance;
-import io.sermant.discovery.entity.RegisterContext;
-import io.sermant.discovery.entity.ServiceInstance;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockedStatic;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@RunWith(MockitoJUnitRunner.class)
-public class NacosDiscoveryClientTest {
-
-    private NacosRegisterConfig registerConfig = new NacosRegisterConfig();
-
-    private NacosDiscoveryClient nacosDiscoveryClient;
-
-    private NacosServiceManager nacosServiceManager;
-
-    private MockedStatic<PluginConfigManager> pluginConfigManagerMockedStatic;
-
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.openMocks(this);
-        pluginConfigManagerMockedStatic = Mockito
-                .mockStatic(PluginConfigManager.class);
-        pluginConfigManagerMockedStatic.when(() -> PluginConfigManager.getPluginConfig(NacosRegisterConfig.class))
-                .thenReturn(registerConfig);
-        RegisterContext.INSTANCE.getClientInfo().setServiceId("test");
-        nacosDiscoveryClient = new NacosDiscoveryClient();
-        nacosServiceManager = new NacosServiceManager();
-        Map<String, String> map = new HashMap<>();
-        map.put("foo", "123");
-        RegisterContext.INSTANCE.getClientInfo().setMeta(map);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        pluginConfigManagerMockedStatic.close();
-    }
-
-    @Test
-    public void testRegister() throws NacosException {
-        mockNamingService();
-        Instance instance = new Instance();
-        instance.setIp("1.1.1.1");
-        instance.setPort(8080);
-        instance.setServiceName("test");
-
-        Map<String, String> metadata = new HashMap<>();
-        metadata.put("version", "1.0.0");
-        metadata.put("environment", "dev");
-        ServiceInstance serviceInstance = new DefaultServiceInstance("127.0.0.1:8080", "127.0.0.1", 8080, metadata
-                , "test");
-        nacosDiscoveryClient.registry(serviceInstance);
-    }
-
-    @Test
-    public void testDeregister() throws NacosException {
-        mockNamingService();
-        Instance instance = new Instance();
-        instance.setIp("1.1.1.1");
-        instance.setPort(8080);
-        instance.setServiceName("test");
-
-        Map<String, Instance> instanceMap = new HashMap<>();
-        instanceMap.put("1.1.1.1:8080", instance);
-
-        nacosDiscoveryClient.unRegistry();
-    }
-
-    @Test
-    public void testGetInstances() throws NacosException {
-        mockNamingService();
-        nacosDiscoveryClient.getInstances("test");
-    }
-
-    @Test
-    public void testRegisterType(){
-        Assert.assertEquals(nacosDiscoveryClient.registerType(), RegisterType.NACOS);
-    }
-
-    @Test
-    public void testConvertServiceInstanceList(){
-        Instance instance = new Instance();
-        instance.setInstanceId("1.1.1.1:8080");
-        instance.setWeight(1.0D);
-        instance.setHealthy(true);
-        instance.setClusterName("DEFAULT");
-        instance.setMetadata(Maps.newHashMap());
-        instance.setEnabled(true);
-        instance.setEphemeral(false);
-
-        instance.setIp("1.1.1.1");
-        instance.setPort(8080);
-        instance.setServiceName("test");
-        List<ServiceInstance> serviceInstances = nacosDiscoveryClient.convertServiceInstanceList(Collections.singletonList(instance), "test");
-        Assert.assertEquals(serviceInstances.size(), 1);
-    }
-
-    private void mockNamingService() throws NacosException {
-        final NamingService namingService = Mockito.mock(NamingService.class);
-        List<String> list = new ArrayList<>();
-        list.add("test");
-        ListView<String> services = new ListView<>();
-        services.setData(list);
-        final NamingMaintainService namingMaintainService = Mockito.mock(NamingMaintainService.class);
-        ReflectUtils.setFieldValue(nacosServiceManager, "namingService", namingService);
-        ReflectUtils.setFieldValue(nacosServiceManager, "namingMaintainService", namingMaintainService);
-        setNacosServiceManager();
-    }
-
-    private void setNacosServiceManager() {
-        ReflectUtils.setFieldValue(nacosDiscoveryClient, "nacosServiceManager", nacosServiceManager);
-        ReflectUtils.setFieldValue(nacosDiscoveryClient, "nacosServiceDiscovery",
-                new NacosDiscoveryClient());
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManagerTest.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManagerTest.java
deleted file mode 100644
index 2e99f8dd6..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/discovery/nacos/NacosServiceManagerTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package io.sermant.discovery.service.lb.discovery.nacos;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-public class NacosServiceManagerTest {
-
-    private NacosServiceManager nacosServiceManagerInstance;
-
-    @Before
-    public void setUp() {
-        // Obtain an instance of NacosServiceManager before running tests
-        nacosServiceManagerInstance = NacosServiceManager.getInstance();
-    }
-
-    @Test
-    public void testGetInstance_ShouldReturnSameInstance() {
-        // Act
-        NacosServiceManager anotherInstance = NacosServiceManager.getInstance();
-
-        // Assert
-        assertNotNull("Instance should not be null", nacosServiceManagerInstance);
-        assertNotNull("Another instance should not be null", anotherInstance);
-        assertEquals("Both instances should be the same", nacosServiceManagerInstance, anotherInstance);
-    }
-
-    @Test
-    public void testGetInstance_ShouldBeThreadSafe() {
-        // Arrange
-        final int threadCount = 100;
-        Thread[] threads = new Thread[threadCount];
-        NacosServiceManager[] instances = new NacosServiceManager[threadCount];
-
-        for (int i = 0; i < threadCount; i++) {
-            int finalI = i;
-            threads[i] = new Thread(() -> {
-                instances[finalI] = NacosServiceManager.getInstance();
-            });
-        }
-
-        // Act
-        for (Thread thread : threads) {
-            thread.start();
-        }
-
-        try {
-            for (Thread thread : threads) {
-                thread.join();
-            }
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-        }
-
-        // Assert
-        for (int i = 0; i < threadCount; i++) {
-            assertNotNull("Instance should not be null", instances[i]);
-            assertEquals("All instances should be the same", instances[0], instances[i]);
-        }
-    }
-}
diff --git a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRuleTest.java b/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRuleTest.java
deleted file mode 100644
index 2cb326521..000000000
--- a/sermant-plugins/sermant-springboot-registry/springboot-registry-service/src/test/java/io/sermant/discovery/service/lb/rule/NacosWeightRandomRuleTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package io.sermant.discovery.service.lb.rule;
-
-import io.sermant.discovery.entity.ServiceInstance;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.junit.Assert;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-import java.util.Map;
-import java.util.List;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class NacosWeightRandomRuleTest {
-
-    @InjectMocks
-    private NacosWeightRandomRule nacosWeightRandomRule;
-
-    private List<ServiceInstance> instances;
-
-    @Before
-    public void setUp() {
-        instances = new ArrayList<>();
-        for (int i = 0; i < 5; i++) {
-            ServiceInstance instance = mock(ServiceInstance.class);
-            Map<String, String> metadata = new ConcurrentHashMap<>();
-            metadata.put("nacos.weight", String.valueOf(i));
-            when(instance.getMetadata()).thenReturn(metadata);
-            instances.add(instance);
-        }
-    }
-
-    @Test
-    public void testDoChoose() {
-
-        // Test the selection method of load balancing
-        ServiceInstance chosenInstance = nacosWeightRandomRule.doChoose("test-service", instances);
-
-        // Here is the basic test, without doing specific probabilistic verification, assuming that every execution is valid.
-        assertNotNull(chosenInstance);
-
-    }
-
-    @Test
-    public void lbType() {
-        Assert.assertEquals(new NacosWeightRandomRule().lbType(), "NacosWeight");
-    }
-
-    @Test
-    public void testWeightRandom() {
-        List<NacosWeightRandomRule.InstanceWithWeight> withWeights = instances.stream()
-                .map(instance -> {
-                    int weight = Integer.parseInt(instance.getMetadata().get("nacos.weight"));
-                    return new NacosWeightRandomRule.InstanceWithWeight(instance, weight);
-                }).collect(Collectors.toList());
-
-        ServiceInstance chosenInstance = nacosWeightRandomRule.weightRandom(withWeights);
-        assertNotNull(chosenInstance);
-    }
-}