You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/11/18 13:41:24 UTC

[dubbo] branch 3.0 updated: Optimize the code for a qos module and add unit test (#9275)

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

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new e6249b3  Optimize the code for a qos module and add unit test (#9275)
e6249b3 is described below

commit e6249b30b76ffe88d3ce9de21d6ab9043f2d74f3
Author: 灼华 <43...@users.noreply.github.com>
AuthorDate: Thu Nov 18 21:41:14 2021 +0800

    Optimize the code for a qos module and add unit test (#9275)
    
    * Optimize the code for a qos module and add unit test
    
    * Remove unused imports
---
 .../dubbo/qos/command/CommandContextFactory.java   |   6 +-
 .../qos/command/decoder/HttpCommandDecoder.java    |   6 +-
 .../apache/dubbo/qos/command/impl/BaseOffline.java |   5 +-
 .../dubbo/qos/command/impl/ChangeTelnet.java       |   3 +-
 .../apache/dubbo/qos/command/impl/CountTelnet.java |   4 +-
 .../org/apache/dubbo/qos/command/impl/Help.java    |  14 +-
 .../org/apache/dubbo/qos/command/impl/Live.java    |  23 ++-
 .../apache/dubbo/qos/command/impl/OfflineApp.java  |   3 -
 .../org/apache/dubbo/qos/command/impl/Online.java  |  12 +-
 .../apache/dubbo/qos/command/impl/OnlineApp.java   |   2 +-
 .../apache/dubbo/qos/command/impl/PortTelnet.java  |   2 +-
 .../apache/dubbo/qos/command/impl/PwdTelnet.java   |   3 +-
 .../dubbo/qos/command/impl/SelectTelnet.java       |   3 +-
 .../dubbo/qos/command/util/CommandHelper.java      |   2 +-
 .../org/apache/dubbo/qos/probe/LivenessProbe.java  |   2 +-
 .../org/apache/dubbo/qos/probe/ReadinessProbe.java |   2 +-
 .../org/apache/dubbo/qos/probe/StartupProbe.java   |   2 +-
 .../qos/server/handler/HttpProcessHandler.java     |   4 +-
 .../command/decoder/HttpCommandDecoderTest.java    |  10 +-
 .../apache/dubbo/qos/command/impl/LiveTest.java    |  52 ++++++
 .../org/apache/dubbo/qos/command/impl/LsTest.java  | 177 ++++++++++++---------
 .../dubbo/qos/command/impl/MockLivenessProbe.java} |  19 ++-
 .../apache/dubbo/qos/command/impl/OfflineTest.java | 170 ++++++++++++--------
 .../apache/dubbo/qos/command/impl/OnlineTest.java  | 162 ++++++++++++-------
 .../qos/command/impl/PublishMetadataTest.java      |  78 +++++++++
 .../apache/dubbo/qos/command/impl/ReadyTest.java   |  78 +++++++++
 .../apache/dubbo/qos/command/impl/StartupTest.java |  75 +++++++++
 .../dubbo/qos/legacy/service/DemoServiceImpl.java  |   4 +-
 .../qos/server/handler/HttpProcessHandlerTest.java |  16 +-
 .../org.apache.dubbo.qos.probe.LivenessProbe       |   1 +
 .../dubbo/registry/ListenerRegistryWrapper.java    |   4 +-
 .../telnet/support/command/HelpTelnetHandler.java  |  66 +++++---
 32 files changed, 709 insertions(+), 301 deletions(-)

diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java
index 7f3ce9a..5cc3b5d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java
@@ -17,11 +17,11 @@
 package org.apache.dubbo.qos.command;
 
 public class CommandContextFactory {
-    public static CommandContext newInstance(String commandName){
+    public static CommandContext newInstance(String commandName) {
         return new CommandContext(commandName);
     }
 
-    public static CommandContext newInstance(String commandName, String[] args,boolean isHttp){
-        return new CommandContext(commandName,args,isHttp);
+    public static CommandContext newInstance(String commandName, String[] args, boolean isHttp) {
+        return new CommandContext(commandName, args, isHttp);
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoder.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoder.java
index 5a16a13..b941bfe 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoder.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoder.java
@@ -34,14 +34,14 @@ public class HttpCommandDecoder {
     public static CommandContext decode(HttpRequest request) {
         CommandContext commandContext = null;
         if (request != null) {
-            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
+            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
             String path = queryStringDecoder.path();
             String[] array = path.split("/");
             if (array.length == 2) {
                 String name = array[1];
 
                 // process GET request and POST request separately. Check url for GET, and check body for POST
-                if (request.getMethod() == HttpMethod.GET) {
+                if (request.method() == HttpMethod.GET) {
                     if (queryStringDecoder.parameters().isEmpty()) {
                         commandContext = CommandContextFactory.newInstance(name);
                         commandContext.setHttp(true);
@@ -52,7 +52,7 @@ public class HttpCommandDecoder {
                         }
                         commandContext = CommandContextFactory.newInstance(name, valueList.toArray(new String[]{}),true);
                     }
-                } else if (request.getMethod() == HttpMethod.POST) {
+                } else if (request.method() == HttpMethod.POST) {
                     HttpPostRequestDecoder httpPostRequestDecoder = new HttpPostRequestDecoder(request);
                     List<String> valueList = new ArrayList<String>();
                     for (InterfaceHttpData interfaceHttpData : httpPostRequestDecoder.getBodyHttpDatas()) {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
index 4a98e23..425d37f 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/BaseOffline.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.registry.Registry;
@@ -35,14 +36,14 @@ public class BaseOffline implements BaseCommand {
     public FrameworkServiceRepository serviceRepository;
 
     public BaseOffline(FrameworkModel frameworkModel) {
-        this.serviceRepository = FrameworkModel.defaultModel().getServiceRepository();
+        this.serviceRepository = frameworkModel.getServiceRepository();
     }
 
     @Override
     public String execute(CommandContext commandContext, String[] args) {
         logger.info("receive offline command");
         String servicePattern = ".*";
-        if (args != null && args.length > 0) {
+        if (ArrayUtils.isNotEmpty(args)) {
             servicePattern = args[0];
         }
 
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
index 2fc00e7..708a18b 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.qos.command.impl;
 
 import io.netty.channel.Channel;
 import io.netty.util.AttributeKey;
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
@@ -42,7 +43,7 @@ public class ChangeTelnet implements BaseCommand {
     public String execute(CommandContext commandContext, String[] args) {
         Channel channel = commandContext.getRemote();
 
-        if (args == null || args.length < 1) {
+        if (ArrayUtils.isEmpty(args)) {
             return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService";
         }
         String message = args[0];
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
index bd6a6f6..8fad057 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
@@ -38,6 +38,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.qos.server.handler.QosProcessHandler.PROMPT;
 
 @Cmd(name = "count", summary = "Count the service.", example = {
     "count [service] [method] [times]"
@@ -92,7 +93,6 @@ public class CountTelnet implements BaseCommand {
             if (t > 0) {
                 final String mtd = method;
                 final Invoker<?> inv = invoker;
-                final String prompt = "telnet";
                 Thread thread = new Thread(() -> {
                     for (int i = 0; i < t; i++) {
                         String result = count(inv, mtd);
@@ -109,7 +109,7 @@ public class CountTelnet implements BaseCommand {
                         }
                     }
                     try {
-                        send(channel, "\r\n" + prompt + "> ");
+                        send(channel, "\r\n" + PROMPT);
                     } catch (RemotingException ignored) {
                     }
                 }, "TelnetCount");
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Help.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Help.java
index 534c3fa..2e07458 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Help.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Help.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
@@ -26,6 +27,8 @@ import org.apache.dubbo.rpc.model.FrameworkModel;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 @Cmd(name = "help", summary = "help command", example = {
         "help",
@@ -35,18 +38,21 @@ public class Help implements BaseCommand {
 
     private CommandHelper commandHelper;
 
+    private static final String MAIN_HELP = "mainHelp";
+
+    private static Map<String, String> processedTable = new WeakHashMap<>();
+
     public Help(FrameworkModel frameworkModel) {
         this.commandHelper = new CommandHelper(frameworkModel);
     }
 
     @Override
     public String execute(CommandContext commandContext, String[] args) {
-        if (args != null && args.length > 0) {
-            return commandHelp(args[0]);
+        if (ArrayUtils.isNotEmpty(args)) {
+            return processedTable.computeIfAbsent(args[0], commandName -> commandHelp(commandName));
         } else {
-            return mainHelp();
+            return processedTable.computeIfAbsent(MAIN_HELP, commandName -> mainHelp());
         }
-
     }
 
 
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java
index c9e00c6..947ad8e 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
@@ -48,18 +47,16 @@ public class Live implements BaseCommand {
             .map(ApplicationConfig::getLivenessProbe)
             .filter(Objects::nonNull)
             .collect(Collectors.joining(","));
-        if(StringUtils.isNotEmpty(config)) {
-            URL url = URL.valueOf("application://")
-                .addParameter(CommonConstants.QOS_LIVE_PROBE_EXTENSION, config);
-            List<LivenessProbe> livenessProbes = frameworkModel.getExtensionLoader(LivenessProbe.class)
-                .getActivateExtension(url, CommonConstants.QOS_LIVE_PROBE_EXTENSION);
-            if (!livenessProbes.isEmpty()) {
-                for (LivenessProbe livenessProbe : livenessProbes) {
-                    if (!livenessProbe.check()) {
-                        // 503 Service Unavailable
-                        commandContext.setHttpCode(503);
-                        return "false";
-                    }
+        URL url = URL.valueOf("application://")
+            .addParameter(CommonConstants.QOS_LIVE_PROBE_EXTENSION, config);
+        List<LivenessProbe> livenessProbes = frameworkModel.getExtensionLoader(LivenessProbe.class)
+            .getActivateExtension(url, CommonConstants.QOS_LIVE_PROBE_EXTENSION);
+        if (!livenessProbes.isEmpty()) {
+            for (LivenessProbe livenessProbe : livenessProbes) {
+                if (!livenessProbe.check()) {
+                    // 503 Service Unavailable
+                    commandContext.setHttpCode(503);
+                    return "false";
                 }
             }
         }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineApp.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineApp.java
index 29aeb98..01776be 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineApp.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OfflineApp.java
@@ -16,8 +16,6 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -28,7 +26,6 @@ import org.apache.dubbo.rpc.model.ProviderModel;
         "offlineApp xx.xx.xxx.service"
 })
 public class OfflineApp extends BaseOffline {
-    private static final Logger logger = LoggerFactory.getLogger(OfflineApp.class);
 
     public OfflineApp(FrameworkModel frameworkModel) {
         super(frameworkModel);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
index 9370a3b..76e94be 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
@@ -18,19 +18,13 @@ package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-import org.apache.dubbo.rpc.model.ProviderModel;
 
-@Cmd(name = "onlineAPp", summary = "offline app addresses", example = {
-        "offlineApp dubbo",
-        "offlineApp xx.xx.xxx.service"
+@Cmd(name = "onlineApp", summary = "online app addresses", example = {
+        "onlineApp dubbo",
+        "onlineApp xx.xx.xxx.service"
 })
 public class Online extends BaseOnline {
     public Online(FrameworkModel frameworkModel) {
         super(frameworkModel);
     }
-
-    @Override
-    protected void doExport(ProviderModel.RegisterStatedURL statedURL) {
-        super.doExport(statedURL);
-    }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineApp.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineApp.java
index d25ad21..ed9340c 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineApp.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/OnlineApp.java
@@ -34,7 +34,7 @@ public class OnlineApp extends BaseOnline {
     @Override
     protected void doExport(ProviderModel.RegisterStatedURL statedURL) {
         if (UrlUtils.isServiceDiscoveryURL(statedURL.getRegistryUrl())) {
-           super.doExport(statedURL);
+            super.doExport(statedURL);
         }
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
index e41e47a..db7635c 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
@@ -55,7 +55,7 @@ public class PortTelnet implements BaseCommand {
                 }
             }
         }
-        if (port == null || port.length() == 0) {
+        if (StringUtils.isEmpty(port)) {
             for (ProtocolServer server : dubboProtocol.getServers()) {
                 if (buf.length() > 0) {
                     buf.append("\r\n");
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
index 366077b..17c38fe 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PwdTelnet.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
@@ -33,7 +34,7 @@ public class PwdTelnet implements BaseCommand {
         }
         String service = commandContext.getRemote().attr(ChangeTelnet.SERVICE_KEY).get();
         StringBuilder buf = new StringBuilder();
-        if (service == null || service.length() == 0) {
+        if (StringUtils.isEmpty(service)) {
             buf.append('/');
         } else {
             buf.append(service);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
index c405eb8..7fd01a1 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.qos.command.impl;
 
 import io.netty.channel.Channel;
 import io.netty.util.AttributeKey;
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
@@ -43,7 +44,7 @@ public class SelectTelnet implements BaseCommand {
 
     @Override
     public String execute(CommandContext commandContext, String[] args) {
-        if (args == null || args.length == 0) {
+        if (ArrayUtils.isEmpty(args)) {
             return "Please input the index of the method you want to invoke, eg: \r\n select 1";
         }
         Channel channel = commandContext.getRemote();
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/CommandHelper.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/CommandHelper.java
index fa61ab9..7c554f4 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/CommandHelper.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/util/CommandHelper.java
@@ -33,7 +33,7 @@ public class CommandHelper {
 
     public boolean hasCommand(String commandName) {
 
-        BaseCommand command = null;
+        BaseCommand command;
         try {
             command = frameworkModel.getExtensionLoader(BaseCommand.class).getExtension(commandName);
         } catch (Throwable throwable) {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
index 0f81a9f..be65fed 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
@@ -23,7 +23,7 @@ import org.apache.dubbo.common.extension.SPI;
  * A probe to indicate whether program is alive
  * </p>
  * If one or more spi return false, 'live' command in dubbo-qos
- * will return false. This can be extend with custom program and developers
+ * will return false. This can be extended with custom program and developers
  * can implement this to customize life cycle.
  *
  * @since 3.0
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
index 98a51dd..95b419f 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
@@ -23,7 +23,7 @@ import org.apache.dubbo.common.extension.SPI;
  * A probe to indicate whether program is ready
  * </p>
  * If one or more spi return false, 'ready' command in dubbo-qos
- * will return false. This can be extend with custom program and developers
+ * will return false. This can be extended with custom program and developers
  * can implement this to customize life cycle.
  *
  * @since 3.0
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
index 018f8e8..7bbd64d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
@@ -23,7 +23,7 @@ import org.apache.dubbo.common.extension.SPI;
  * A probe to indicate whether program is startup
  * </p>
  * If one or more spi return false, 'startup' command in dubbo-qos
- * will return false. This can be extend with custom program and developers
+ * will return false. This can be extended with custom program and developers
  * can implement this to customize life cycle.
  *
  * @since 3.0
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
index e8fc6e1..efb125f 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
@@ -78,7 +78,7 @@ public class HttpProcessHandler extends SimpleChannelInboundHandler<HttpRequest>
         CommandContext commandContext = HttpCommandDecoder.decode(msg);
         // return 404 when fail to construct command context
         if (commandContext == null) {
-            log.warn("can not found commandContext url: " + msg.uri());
+            log.warn("can not found commandContext, url: " + msg.uri());
             FullHttpResponse response = http404();
             ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
         } else {
@@ -89,7 +89,7 @@ public class HttpProcessHandler extends SimpleChannelInboundHandler<HttpRequest>
                 FullHttpResponse response = http(httpCode, result);
                 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
             } catch (NoSuchCommandException ex) {
-                log.error("can not find commandContext: " + commandContext, ex);
+                log.error("can not find command: " + commandContext, ex);
                 FullHttpResponse response = http404();
                 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
             } catch (Exception qosEx) {
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoderTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoderTest.java
index b66fbba..b030927 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoderTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/decoder/HttpCommandDecoderTest.java
@@ -38,12 +38,12 @@ public class HttpCommandDecoderTest {
     @Test
     public void decodeGet() throws Exception {
         HttpRequest request = mock(HttpRequest.class);
-        when(request.getUri()).thenReturn("localhost:80/test");
-        when(request.getMethod()).thenReturn(HttpMethod.GET);
+        when(request.uri()).thenReturn("localhost:80/test");
+        when(request.method()).thenReturn(HttpMethod.GET);
         CommandContext context = HttpCommandDecoder.decode(request);
         assertThat(context.getCommandName(), equalTo("test"));
         assertThat(context.isHttp(), is(true));
-        when(request.getUri()).thenReturn("localhost:80/test?a=b&c=d");
+        when(request.uri()).thenReturn("localhost:80/test?a=b&c=d");
         context = HttpCommandDecoder.decode(request);
         assertThat(context.getArgs(), arrayContaining("b", "d"));
     }
@@ -51,8 +51,8 @@ public class HttpCommandDecoderTest {
     @Test
     public void decodePost() throws Exception {
         FullHttpRequest request = mock(FullHttpRequest.class);
-        when(request.getUri()).thenReturn("localhost:80/test");
-        when(request.getMethod()).thenReturn(HttpMethod.POST);
+        when(request.uri()).thenReturn("localhost:80/test");
+        when(request.method()).thenReturn(HttpMethod.POST);
         when(request.headers()).thenReturn(HttpHeaders.EMPTY_HEADERS);
         ByteBuf buf = Unpooled.copiedBuffer("a=b&c=d", StandardCharsets.UTF_8);
         when(request.content()).thenReturn(buf);
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LiveTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LiveTest.java
new file mode 100644
index 0000000..cdacfc5
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LiveTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class LiveTest {
+    private FrameworkModel frameworkModel;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = new FrameworkModel();
+    }
+
+    @AfterEach
+    public void reset() {
+        frameworkModel.destroy();
+    }
+
+    @Test
+    public void testExecute() {
+        Live live = new Live(frameworkModel);
+        CommandContext commandContext = new CommandContext("live");
+        String result = live.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "false");
+        Assertions.assertEquals(commandContext.getHttpCode(), 503);
+
+        MockLivenessProbe.setCheckReturnValue(true);
+        result = live.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "true");
+        Assertions.assertEquals(commandContext.getHttpCode(), 200);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LsTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LsTest.java
index 2bae3d3..f95a228 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LsTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/LsTest.java
@@ -1,75 +1,102 @@
-///*
-// * Licensed to the Apache Software Foundation (ASF) under one or more
-// * contributor license agreements.  See the NOTICE file distributed with
-// * this work for additional information regarding copyright ownership.
-// * The ASF licenses this file to You under the Apache License, Version 2.0
-// * (the "License"); you may not use this file except in compliance with
-// * the License.  You may obtain a copy of the License at
-// *
-// *     http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//package org.apache.dubbo.qos.command.impl;
-//
-//import org.apache.dubbo.common.URL;
-//import org.apache.dubbo.qos.command.CommandContext;
-//import org.apache.dubbo.registry.integration.RegistryDirectory;
-//import org.apache.dubbo.rpc.Invoker;
-//import org.apache.dubbo.rpc.model.ApplicationModel;
-//import org.apache.dubbo.rpc.model.ConsumerModel;
-//import org.apache.dubbo.rpc.model.ProviderModel;
-//
-//import org.junit.jupiter.api.Test;
-//import org.mockito.Mockito;
-//
-//import java.util.Map;
-//
-//import static org.hamcrest.MatcherAssert.assertThat;
-//import static org.hamcrest.Matchers.containsString;
-//import static org.mockito.Mockito.mock;
-//import static org.mockito.Mockito.when;
-//
-//public class LsTest {
-//    @Test
-//    public void testExecute() throws Exception {
-//        ConsumerModel consumerModel = mock(ConsumerModel.class);
-//        when(consumerModel.getServiceKey()).thenReturn("org.apache.dubbo.FooService");
-//        ProviderModel providerModel = mock(ProviderModel.class);
-//        when(providerModel.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        ApplicationModel.initConsumerModel("org.apache.dubbo.FooService", consumerModel);
-//        ApplicationModel.initProviderModel("org.apache.dubbo.BarService", providerModel);
-//
-//        Invoker providerInvoker = mock(Invoker.class);
-//        URL registryUrl = mock(URL.class);
-//        when(registryUrl.toFullString()).thenReturn("test://localhost:8080");
-//        URL providerUrl = mock(URL.class);
-//        when(providerUrl.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        when(providerUrl.toFullString()).thenReturn("dubbo://localhost:8888/org.apache.dubbo.BarService");
-//        when(providerInvoker.getUrl()).thenReturn(providerUrl);
-//        ApplicationModel.registerProviderInvoker(providerInvoker, registryUrl, providerUrl);
-//        for (ProviderInvokerWrapper wrapper : ApplicationModel.defaultModel().getProviderInvokers("org.apache.dubbo.BarService")) {
-//            wrapper.setReg(true);
-//        }
-//
-//        Invoker consumerInvoker = mock(Invoker.class);
-//        URL consumerUrl = mock(URL.class);
-//        when(consumerUrl.getServiceKey()).thenReturn("org.apache.dubbo.FooService");
-//        when(consumerUrl.toFullString()).thenReturn("dubbo://localhost:8888/org.apache.dubbo.FooService");
-//        when(consumerInvoker.getUrl()).thenReturn(consumerUrl);
-//        RegistryDirectory directory = mock(RegistryDirectory.class);
-//        Map invokers = Mockito.mock(Map.class);
-//        when(invokers.size()).thenReturn(100);
-//        when(directory.getUrlInvokerMap()).thenReturn(invokers);
-//        ApplicationModel.registerConsumerInvoker(consumerInvoker, consumerUrl.getServiceKey());
-//
-//        Ls ls = new Ls();
-//        String output = ls.execute(mock(CommandContext.class), null);
-//        assertThat(output, containsString("org.apache.dubbo.FooService|100"));
-//        assertThat(output, containsString("org.apache.dubbo.BarService| Y"));
-//    }
-//}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.qos.DemoService;
+import org.apache.dubbo.qos.DemoServiceImpl;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.rpc.model.AsyncMethodInfo;
+import org.apache.dubbo.rpc.model.ConsumerModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceMetadata;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LsTest {
+    private FrameworkModel frameworkModel;
+    private ModuleServiceRepository repository;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = new FrameworkModel();
+        repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository();
+        registerProvider();
+        registerConsumer();
+    }
+
+    @AfterEach
+    public void reset() {
+        frameworkModel.destroy();
+    }
+
+    @Test
+    public void testExecute() {
+        Ls ls = new Ls(frameworkModel);
+        String result = ls.execute(Mockito.mock(CommandContext.class), new String[0]);
+        System.out.println(result);
+        /**
+         As Provider side:
+         +--------------------------------+---+
+         |      Provider Service Name     |PUB|
+         +--------------------------------+---+
+         |org.apache.dubbo.qos.DemoService| N |
+         +--------------------------------+---+
+         As Consumer side:
+         +--------------------------------+---+
+         |      Consumer Service Name     |NUM|
+         +--------------------------------+---+
+         |org.apache.dubbo.qos.DemoService| 0 |
+         +--------------------------------+---+
+         */
+    }
+
+    private void registerProvider() {
+        ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class);
+        ServiceMetadata serviceMetadata = new ServiceMetadata();
+        serviceMetadata.setServiceKey(DemoService.class.getName());
+        ProviderModel providerModel = new ProviderModel(
+            DemoService.class.getName(),
+            new DemoServiceImpl(),
+            serviceDescriptor,
+            null,
+            serviceMetadata);
+        repository.registerProvider(providerModel);
+    }
+
+    private void registerConsumer() {
+        ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class);
+        ReferenceConfig referenceConfig = new ReferenceConfig();
+        referenceConfig.setInterface(DemoService.class);
+        ServiceMetadata serviceMetadata = new ServiceMetadata();
+        serviceMetadata.setServiceKey(DemoService.class.getName());
+        Map<String, AsyncMethodInfo> methodConfigs = new HashMap<>();
+        ConsumerModel consumerModel = new ConsumerModel(
+            serviceMetadata.getServiceKey(), null, serviceDescriptor,
+            referenceConfig, serviceMetadata, methodConfigs
+        );
+        repository.registerConsumer(consumerModel);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/MockLivenessProbe.java
similarity index 63%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java
copy to dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/MockLivenessProbe.java
index 7f3ce9a..17434b1 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContextFactory.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/MockLivenessProbe.java
@@ -14,14 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.command;
+package org.apache.dubbo.qos.command.impl;
 
-public class CommandContextFactory {
-    public static CommandContext newInstance(String commandName){
-        return new CommandContext(commandName);
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.qos.probe.LivenessProbe;
+
+@Activate
+public class MockLivenessProbe implements LivenessProbe {
+    private static boolean checkReturnValue = false;
+
+    @Override
+    public boolean check() {
+        return checkReturnValue;
     }
 
-    public static CommandContext newInstance(String commandName, String[] args,boolean isHttp){
-        return new CommandContext(commandName,args,isHttp);
+    public static void setCheckReturnValue(boolean checkReturnValue) {
+        MockLivenessProbe.checkReturnValue = checkReturnValue;
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OfflineTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OfflineTest.java
index cdda677..05a4449 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OfflineTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OfflineTest.java
@@ -1,68 +1,102 @@
-///*
-// * Licensed to the Apache Software Foundation (ASF) under one or more
-// * contributor license agreements.  See the NOTICE file distributed with
-// * this work for additional information regarding copyright ownership.
-// * The ASF licenses this file to You under the Apache License, Version 2.0
-// * (the "License"); you may not use this file except in compliance with
-// * the License.  You may obtain a copy of the License at
-// *
-// *     http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//package org.apache.dubbo.qos.command.impl;
-//
-//import org.apache.dubbo.common.URL;
-//import org.apache.dubbo.qos.command.CommandContext;
-//import org.apache.dubbo.registry.Registry;
-//import org.apache.dubbo.rpc.Invoker;
-//import org.apache.dubbo.rpc.model.ApplicationModel;
-//import org.apache.dubbo.rpc.model.ProviderModel;
-//
-//import org.junit.jupiter.api.Test;
-//import org.mockito.Mockito;
-//
-//import static org.hamcrest.MatcherAssert.assertThat;
-//import static org.hamcrest.Matchers.containsString;
-//import static org.hamcrest.core.Is.is;
-//import static org.mockito.Mockito.mock;
-//import static org.mockito.Mockito.when;
-//
-//public class OfflineTest {
-//    @Test
-//    public void testExecute() throws Exception {
-//        ProviderModel providerModel = mock(ProviderModel.class);
-//        when(providerModel.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        ApplicationModel.initProviderModel("org.apache.dubbo.BarService", providerModel);
-//
-//        Invoker providerInvoker = mock(Invoker.class);
-//        URL registryUrl = mock(URL.class);
-//        when(registryUrl.toFullString()).thenReturn("test://localhost:8080");
-//        URL providerUrl = mock(URL.class);
-//        when(providerUrl.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        when(providerUrl.toFullString()).thenReturn("dubbo://localhost:8888/org.apache.dubbo.BarService");
-//        when(providerInvoker.getUrl()).thenReturn(providerUrl);
-//        ApplicationModel.registerProviderInvoker(providerInvoker, registryUrl, providerUrl);
-//        for (ProviderInvokerWrapper wrapper : ApplicationModel.defaultModel().getProviderInvokers("org.apache.dubbo.BarService")) {
-//            wrapper.setReg(true);
-//        }
-//
-//        Registry registry = mock(Registry.class);
-//        TestRegistryFactory.registry = registry;
-//
-//        Offline offline = new Offline();
-//        String output = offline.execute(mock(CommandContext.class), new String[]{"org.apache.dubbo.BarService"});
-//        assertThat(output, containsString("OK"));
-//        Mockito.verify(registry).unregister(providerUrl);
-//        for (ProviderInvokerWrapper wrapper : ApplicationModel.defaultModel().getProviderInvokers("org.apache.dubbo.BarService")) {
-//            assertThat(wrapper.isReg(), is(false));
-//        }
-//
-//        output = offline.execute(mock(CommandContext.class), new String[]{"org.apache.dubbo.FooService"});
-//        assertThat(output, containsString("service not found"));
-//    }
-//}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.qos.DemoService;
+import org.apache.dubbo.qos.DemoServiceImpl;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.registry.RegistryService;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceMetadata;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
+import static org.mockito.Mockito.mock;
+
+/**
+ * {@link BaseOffline}
+ * {@link Offline}
+ * {@link OfflineApp}
+ * {@link OfflineInterface}
+ */
+public class OfflineTest {
+    private FrameworkModel frameworkModel;
+    private ModuleServiceRepository repository;
+    private ProviderModel.RegisterStatedURL registerStatedURL;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = new FrameworkModel();
+        repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository();
+        registerProvider();
+    }
+
+    @AfterEach
+    public void reset() {
+        frameworkModel.destroy();
+    }
+
+    @Test
+    public void testExecute() {
+        Offline offline = new Offline(frameworkModel);
+        String result = offline.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertFalse(registerStatedURL.isRegistered());
+
+        OfflineInterface offlineInterface = new OfflineInterface(frameworkModel);
+        registerStatedURL.setRegistered(true);
+        result = offlineInterface.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertFalse(registerStatedURL.isRegistered());
+
+        registerStatedURL.setRegistered(true);
+        registerStatedURL.setRegistryUrl(URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName())
+            .addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE));
+        OfflineApp offlineApp = new OfflineApp(frameworkModel);
+        result = offlineApp.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertFalse(registerStatedURL.isRegistered());
+
+    }
+
+    private void registerProvider() {
+        ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class);
+        ServiceMetadata serviceMetadata = new ServiceMetadata();
+        serviceMetadata.setServiceKey(DemoService.class.getName());
+        ProviderModel providerModel = new ProviderModel(
+            DemoService.class.getName(),
+            new DemoServiceImpl(),
+            serviceDescriptor,
+            null,
+            serviceMetadata);
+        registerStatedURL = new ProviderModel.RegisterStatedURL(
+            URL.valueOf("dubbo://127.0.0.1:20880/" + DemoService.class.getName()),
+            URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()),
+            true);
+        providerModel.addStatedUrl(registerStatedURL
+        );
+        repository.registerProvider(providerModel);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OnlineTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OnlineTest.java
index ef8a037..d0906e9 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OnlineTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/OnlineTest.java
@@ -1,60 +1,102 @@
-///*
-// * Licensed to the Apache Software Foundation (ASF) under one or more
-// * contributor license agreements.  See the NOTICE file distributed with
-// * this work for additional information regarding copyright ownership.
-// * The ASF licenses this file to You under the Apache License, Version 2.0
-// * (the "License"); you may not use this file except in compliance with
-// * the License.  You may obtain a copy of the License at
-// *
-// *     http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//package org.apache.dubbo.qos.command.impl;
-//
-//import org.apache.dubbo.common.URL;
-//import org.apache.dubbo.qos.command.CommandContext;
-//import org.apache.dubbo.registry.Registry;
-//import org.apache.dubbo.rpc.Invoker;
-//import org.apache.dubbo.rpc.model.ApplicationModel;
-//import org.apache.dubbo.rpc.model.ProviderModel;
-//
-//import org.junit.jupiter.api.Test;
-//
-//import static org.hamcrest.MatcherAssert.assertThat;
-//import static org.hamcrest.Matchers.equalTo;
-//import static org.junit.jupiter.api.Assertions.assertTrue;
-//import static org.mockito.Mockito.mock;
-//import static org.mockito.Mockito.when;
-//
-//public class OnlineTest {
-//    @Test
-//    public void testExecute() throws Exception {
-//        ProviderModel providerModel = mock(ProviderModel.class);
-//        when(providerModel.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        ApplicationModel.initProviderModel("org.apache.dubbo.BarService", providerModel);
-//
-//        Invoker providerInvoker = mock(Invoker.class);
-//        URL registryUrl = mock(URL.class);
-//        when(registryUrl.toFullString()).thenReturn("test://localhost:8080");
-//        URL providerUrl = mock(URL.class);
-//        when(providerUrl.getServiceKey()).thenReturn("org.apache.dubbo.BarService");
-//        when(providerUrl.toFullString()).thenReturn("dubbo://localhost:8888/org.apache.dubbo.BarService");
-//        when(providerInvoker.getUrl()).thenReturn(providerUrl);
-//        ApplicationModel.registerProviderInvoker(providerInvoker, registryUrl, providerUrl);
-//
-//        Registry registry = mock(Registry.class);
-//        TestRegistryFactory.registry = registry;
-//
-//        Online online = new Online();
-//        String output = online.execute(mock(CommandContext.class), new String[]{"org.apache.dubbo.BarService"});
-//        assertThat(output, equalTo("OK"));
-//        for (ProviderInvokerWrapper wrapper : ApplicationModel.defaultModel().getProviderInvokers("org.apache.dubbo.BarService")) {
-//            assertTrue(wrapper.isReg());
-//        }
-//    }
-//}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.qos.DemoService;
+import org.apache.dubbo.qos.DemoServiceImpl;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.registry.RegistryService;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceMetadata;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Assertions;
+
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
+import static org.mockito.Mockito.mock;
+
+/**
+ * {@link BaseOnline}
+ * {@link Online}
+ * {@link OnlineApp}
+ * {@link OnlineInterface}
+ */
+public class OnlineTest {
+    private FrameworkModel frameworkModel;
+    private ModuleServiceRepository repository;
+    private ProviderModel.RegisterStatedURL registerStatedURL;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = new FrameworkModel();
+        repository = frameworkModel.newApplication().getDefaultModule().getServiceRepository();
+        registerProvider();
+    }
+
+    @AfterEach
+    public void reset() {
+        frameworkModel.destroy();
+    }
+
+    @Test
+    public void testExecute() {
+        Online online = new Online(frameworkModel);
+        String result = online.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertTrue(registerStatedURL.isRegistered());
+
+        OnlineInterface onlineInterface = new OnlineInterface(frameworkModel);
+        registerStatedURL.setRegistered(false);
+        result = onlineInterface.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertTrue(registerStatedURL.isRegistered());
+
+        registerStatedURL.setRegistered(false);
+        registerStatedURL.setRegistryUrl(URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName())
+            .addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE));
+        OnlineApp onlineApp = new OnlineApp(frameworkModel);
+        result = onlineApp.execute(mock(CommandContext.class), new String[]{DemoService.class.getName()});
+        Assertions.assertEquals(result, "OK");
+        Assertions.assertTrue(registerStatedURL.isRegistered());
+
+    }
+
+    private void registerProvider() {
+        ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class);
+        ServiceMetadata serviceMetadata = new ServiceMetadata();
+        serviceMetadata.setServiceKey(DemoService.class.getName());
+        ProviderModel providerModel = new ProviderModel(
+            DemoService.class.getName(),
+            new DemoServiceImpl(),
+            serviceDescriptor,
+            null,
+            serviceMetadata);
+        registerStatedURL = new ProviderModel.RegisterStatedURL(
+            URL.valueOf("dubbo://127.0.0.1:20880/" + DemoService.class.getName()),
+            URL.valueOf("test://127.0.0.1:2181/" + RegistryService.class.getName()),
+            false);
+        providerModel.addStatedUrl(registerStatedURL
+        );
+        repository.registerProvider(providerModel);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PublishMetadataTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PublishMetadataTest.java
new file mode 100644
index 0000000..d6d2e68
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PublishMetadataTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.config.deploy.DefaultApplicationDeployer;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+
+public class PublishMetadataTest {
+    private FrameworkModel frameworkModel;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        frameworkModel = new FrameworkModel();
+        for (int i = 0; i < 3; i++) {
+            ApplicationModel applicationModel = frameworkModel.newApplication();
+            DefaultApplicationDeployer deployer = applicationModel.getBeanFactory().getBean(DefaultApplicationDeployer.class);
+            ServiceInstance serviceInstance = new DefaultServiceInstance("APP_" + i, applicationModel);
+            Field serviceInstanceField = deployer.getClass().getDeclaredField("serviceInstance");
+            serviceInstanceField.setAccessible(true);
+            serviceInstanceField.set(deployer, serviceInstance);
+        }
+
+    }
+
+    @AfterEach
+    public void reset() {
+        frameworkModel.destroy();
+    }
+
+    @Test
+    public void testExecute() {
+        PublishMetadata publishMetadata = new PublishMetadata(frameworkModel);
+
+        String result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[0]);
+        String expectResult = "publish metadata succeeded. App:APP_0\n" +
+            "publish metadata succeeded. App:APP_1\n" +
+            "publish metadata succeeded. App:APP_2\n";
+        Assertions.assertEquals(result, expectResult);
+
+        // delay 5s
+        result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[]{"5"});
+        expectResult = "publish task submitted, will publish in 5 seconds. App:APP_0\n" +
+            "publish task submitted, will publish in 5 seconds. App:APP_1\n" +
+            "publish task submitted, will publish in 5 seconds. App:APP_2\n";
+        Assertions.assertEquals(result, expectResult);
+
+        // wrong delay param
+        result = publishMetadata.execute(Mockito.mock(CommandContext.class), new String[]{"A"});
+        expectResult = "publishMetadata failed! Wrong delay param!";
+        Assertions.assertEquals(result, expectResult);
+
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ReadyTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ReadyTest.java
new file mode 100644
index 0000000..a2a88cd
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ReadyTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.deploy.ModuleDeployer;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.probe.ReadinessProbe;
+import org.apache.dubbo.qos.probe.impl.DeployerReadinessProbe;
+import org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ReadyTest {
+
+    private FrameworkModel frameworkModel;
+    private ModuleDeployer moduleDeployer;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = Mockito.mock(FrameworkModel.class);
+        ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class);
+        ModuleModel moduleModel = Mockito.mock(ModuleModel.class);
+        moduleDeployer = Mockito.mock(ModuleDeployer.class);
+        Mockito.when(frameworkModel.newApplication()).thenReturn(applicationModel);
+        Mockito.when(applicationModel.getModuleModels()).thenReturn(Arrays.asList(moduleModel));
+        Mockito.when(moduleModel.getDeployer()).thenReturn(moduleDeployer);
+        Mockito.when(moduleDeployer.isStarted()).thenReturn(true);
+
+        ExtensionLoader loader = Mockito.mock(ExtensionLoader.class);
+        Mockito.when(frameworkModel.getExtensionLoader(ReadinessProbe.class)).thenReturn(loader);
+        URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_READY_PROBE_EXTENSION, "");
+        List<ReadinessProbe> readinessProbes = Arrays.asList(
+            new DeployerReadinessProbe(frameworkModel.newApplication()),
+            new ProviderReadinessProbe()
+        );
+        Mockito.when(loader.getActivateExtension(url, CommonConstants.QOS_READY_PROBE_EXTENSION)).thenReturn(readinessProbes);
+    }
+
+    @Test
+    public void testExecute() {
+        Ready ready = new Ready(frameworkModel);
+        CommandContext commandContext = new CommandContext("ready");
+
+        String result = ready.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "true");
+        Assertions.assertEquals(commandContext.getHttpCode(), 200);
+
+        Mockito.when(moduleDeployer.isStarted()).thenReturn(false);
+        result = ready.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "false");
+        Assertions.assertEquals(commandContext.getHttpCode(), 503);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/StartupTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/StartupTest.java
new file mode 100644
index 0000000..ef25437
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/StartupTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.deploy.ModuleDeployer;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.probe.StartupProbe;
+import org.apache.dubbo.qos.probe.impl.DeployerStartupProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class StartupTest {
+    private FrameworkModel frameworkModel;
+    private ModuleDeployer moduleDeployer;
+
+    @BeforeEach
+    public void setUp() {
+        frameworkModel = Mockito.mock(FrameworkModel.class);
+        ApplicationModel applicationModel = Mockito.mock(ApplicationModel.class);
+        ModuleModel moduleModel = Mockito.mock(ModuleModel.class);
+        moduleDeployer = Mockito.mock(ModuleDeployer.class);
+        Mockito.when(frameworkModel.newApplication()).thenReturn(applicationModel);
+        Mockito.when(applicationModel.getModuleModels()).thenReturn(Arrays.asList(moduleModel));
+        Mockito.when(moduleModel.getDeployer()).thenReturn(moduleDeployer);
+        Mockito.when(moduleDeployer.isRunning()).thenReturn(true);
+
+        ExtensionLoader loader = Mockito.mock(ExtensionLoader.class);
+        Mockito.when(frameworkModel.getExtensionLoader(StartupProbe.class)).thenReturn(loader);
+        URL url = URL.valueOf("application://").addParameter(CommonConstants.QOS_STARTUP_PROBE_EXTENSION, "");
+        List<StartupProbe> readinessProbes = Arrays.asList(
+            new DeployerStartupProbe(frameworkModel.newApplication())
+        );
+        Mockito.when(loader.getActivateExtension(url, CommonConstants.QOS_STARTUP_PROBE_EXTENSION)).thenReturn(readinessProbes);
+    }
+
+    @Test
+    public void testExecute() {
+        Startup startup = new Startup(frameworkModel);
+        CommandContext commandContext = new CommandContext("startup");
+
+        String result = startup.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "true");
+        Assertions.assertEquals(commandContext.getHttpCode(), 200);
+
+        Mockito.when(moduleDeployer.isRunning()).thenReturn(false);
+        result = startup.execute(commandContext, new String[0]);
+        Assertions.assertEquals(result, "false");
+        Assertions.assertEquals(commandContext.getHttpCode(), 503);
+    }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoServiceImpl.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoServiceImpl.java
index a42cc12..466e193 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoServiceImpl.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/legacy/service/DemoServiceImpl.java
@@ -113,8 +113,8 @@ public class DemoServiceImpl implements DemoService {
     }
 
     @Override
-    public int getPerson(Person person1, Person perso2) {
-        return person1.getAge() + perso2.getAge();
+    public int getPerson(Person person1, Person person2) {
+        return person1.getAge() + person2.getAge();
     }
 
     @Override
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
index 6cecfae..573b016 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
@@ -41,14 +41,14 @@ public class HttpProcessHandlerTest {
         ChannelFuture future = mock(ChannelFuture.class);
         when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future);
         HttpRequest message = Mockito.mock(HttpRequest.class);
-        when(message.getUri()).thenReturn("test");
+        when(message.uri()).thenReturn("test");
         HttpProcessHandler handler = new HttpProcessHandler(FrameworkModel.defaultModel());
         handler.channelRead0(context, message);
         verify(future).addListener(ChannelFutureListener.CLOSE);
         ArgumentCaptor<FullHttpResponse> captor = ArgumentCaptor.forClass(FullHttpResponse.class);
         verify(context).writeAndFlush(captor.capture());
         FullHttpResponse response = captor.getValue();
-        assertThat(response.getStatus().code(), equalTo(404));
+        assertThat(response.status().code(), equalTo(404));
     }
 
     @Test
@@ -57,15 +57,15 @@ public class HttpProcessHandlerTest {
         ChannelFuture future = mock(ChannelFuture.class);
         when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future);
         HttpRequest message = Mockito.mock(HttpRequest.class);
-        when(message.getUri()).thenReturn("localhost:80/greeting");
-        when(message.getMethod()).thenReturn(HttpMethod.GET);
+        when(message.uri()).thenReturn("localhost:80/greeting");
+        when(message.method()).thenReturn(HttpMethod.GET);
         HttpProcessHandler handler = new HttpProcessHandler(FrameworkModel.defaultModel());
         handler.channelRead0(context, message);
         verify(future).addListener(ChannelFutureListener.CLOSE);
         ArgumentCaptor<FullHttpResponse> captor = ArgumentCaptor.forClass(FullHttpResponse.class);
         verify(context).writeAndFlush(captor.capture());
         FullHttpResponse response = captor.getValue();
-        assertThat(response.getStatus().code(), equalTo(200));
+        assertThat(response.status().code(), equalTo(200));
     }
 
     @Test
@@ -74,14 +74,14 @@ public class HttpProcessHandlerTest {
         ChannelFuture future = mock(ChannelFuture.class);
         when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future);
         HttpRequest message = Mockito.mock(HttpRequest.class);
-        when(message.getUri()).thenReturn("localhost:80/test");
-        when(message.getMethod()).thenReturn(HttpMethod.GET);
+        when(message.uri()).thenReturn("localhost:80/test");
+        when(message.method()).thenReturn(HttpMethod.GET);
         HttpProcessHandler handler = new HttpProcessHandler(FrameworkModel.defaultModel());
         handler.channelRead0(context, message);
         verify(future).addListener(ChannelFutureListener.CLOSE);
         ArgumentCaptor<FullHttpResponse> captor = ArgumentCaptor.forClass(FullHttpResponse.class);
         verify(context).writeAndFlush(captor.capture());
         FullHttpResponse response = captor.getValue();
-        assertThat(response.getStatus().code(), equalTo(404));
+        assertThat(response.status().code(), equalTo(404));
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.probe.LivenessProbe b/dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.probe.LivenessProbe
new file mode 100644
index 0000000..8c08e9a
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/test/resources/META-INF/services/org.apache.dubbo.qos.probe.LivenessProbe
@@ -0,0 +1 @@
+mock=org.apache.dubbo.qos.command.impl.MockLivenessProbe
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
index fc686a4..3762387 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
@@ -81,7 +81,9 @@ public class ListenerRegistryWrapper implements Registry {
     @Override
     public void unregister(URL url) {
         try {
-            registry.unregister(url);
+            if (registry != null) {
+                registry.unregister(url);
+            }
         } finally {
             if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) {
                 RuntimeException exception = null;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
index 7da05de..720b0ef 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
@@ -26,6 +26,8 @@ import org.apache.dubbo.remoting.telnet.support.TelnetUtils;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * HelpTelnetHandler
@@ -36,38 +38,50 @@ public class HelpTelnetHandler implements TelnetHandler {
 
     private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);
 
+    private static final String MAIN_HELP = "mainHelp";
+
+    private static Map<String, String> processedTable = new WeakHashMap<>();
+
     @Override
     public String telnet(Channel channel, String message) {
         if (message.length() > 0) {
-            if (!extensionLoader.hasExtension(message)) {
-                return "No such command " + message;
-            }
-            TelnetHandler handler = extensionLoader.getExtension(message);
-            Help help = handler.getClass().getAnnotation(Help.class);
-            StringBuilder buf = new StringBuilder();
-            buf.append("Command:\r\n    ");
-            buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));
-            buf.append("\r\nSummary:\r\n    ");
-            buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));
-            buf.append("\r\nDetail:\r\n    ");
-            buf.append(help.detail().replace("\r\n", "    \r\n").replace("\n", "    \n"));
-            return buf.toString();
+            return processedTable.computeIfAbsent(message, commandName ->  generateForOneCommand(commandName));
         } else {
-            List<List<String>> table = new ArrayList<List<String>>();
-            List<TelnetHandler> handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet");
-            if (CollectionUtils.isNotEmpty(handlers)) {
-                for (TelnetHandler handler : handlers) {
-                    Help help = handler.getClass().getAnnotation(Help.class);
-                    List<String> row = new ArrayList<String>();
-                    String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");
-                    row.add(parameter.length() > 55 ? parameter.substring(0, 55) + "..." : parameter);
-                    String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";
-                    row.add(summary.length() > 55 ? summary.substring(0, 55) + "..." : summary);
-                    table.add(row);
-                }
+            return processedTable.computeIfAbsent(MAIN_HELP, commandName ->  generateForAllCommand(channel));
+        }
+    }
+
+    private String generateForOneCommand(String message) {
+        if (!extensionLoader.hasExtension(message)) {
+            return "No such command " + message;
+        }
+        TelnetHandler handler = extensionLoader.getExtension(message);
+        Help help = handler.getClass().getAnnotation(Help.class);
+        StringBuilder buf = new StringBuilder();
+        buf.append("Command:\r\n    ");
+        buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));
+        buf.append("\r\nSummary:\r\n    ");
+        buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));
+        buf.append("\r\nDetail:\r\n    ");
+        buf.append(help.detail().replace("\r\n", "    \r\n").replace("\n", "    \n"));
+        return buf.toString();
+    }
+
+    private String generateForAllCommand(Channel channel) {
+        List<List<String>> table = new ArrayList<List<String>>();
+        List<TelnetHandler> handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet");
+        if (CollectionUtils.isNotEmpty(handlers)) {
+            for (TelnetHandler handler : handlers) {
+                Help help = handler.getClass().getAnnotation(Help.class);
+                List<String> row = new ArrayList<String>();
+                String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");
+                row.add(parameter.length() > 55 ? parameter.substring(0, 55) + "..." : parameter);
+                String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";
+                row.add(summary.length() > 55 ? summary.substring(0, 55) + "..." : summary);
+                table.add(row);
             }
-            return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);
         }
+        return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);
     }
 
 }