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/06/15 09:41:15 UTC

[dubbo] branch master updated: [Dubbo-6720] fix bug same interface unexport and export fail. also support hotload service (#6720)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 222bf15  [Dubbo-6720] fix bug same interface unexport and export fail. also support hotload service (#6720)
222bf15 is described below

commit 222bf15bd55050ba9f6490ef5ea066f8f6c2bcf8
Author: Owen.Cai <89...@qq.com>
AuthorDate: Tue Jun 15 17:41:01 2021 +0800

    [Dubbo-6720] fix bug same interface unexport and export fail. also support hotload service (#6720)
    
    * delegate exportmap & support hotload
    
    * add header
    
    * delete unused import & change test code
    
    * delete unused import
    
    * check codestyle
    
    * endline format
    
    * add code
    
    * add res code exporters func
    
    * DelegateExporterMap referencing a map
    
    * fix test  DelegateExporterMap interface change
---
 .../dubbo/rpc/protocol/AbstractProtocol.java       | 232 ++++-----
 .../dubbo/rpc/protocol/AbstractProxyProtocol.java  |   7 +-
 .../dubbo/rpc/protocol/DelegateExporterMap.java    |  86 ++++
 .../dubbo/rpc/protocol/dubbo/DubboExporter.java    |  13 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |  11 +-
 .../dubbo/rpc/protocol/injvm/InjvmExporter.java    |  15 +-
 .../dubbo/rpc/protocol/injvm/InjvmInvoker.java     |  12 +-
 .../dubbo/rpc/protocol/injvm/InjvmProtocol.java    |  17 +-
 .../rpc/protocol/injvm/InjvmProtocolTest.java      |  29 +-
 .../dubbo/rpc/protocol/thrift/ThriftProtocol.java  | 544 ++++++++++-----------
 10 files changed, 534 insertions(+), 432 deletions(-)

diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
index d27fcbb..d10d383 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProtocol.java
@@ -1,116 +1,116 @@
-/*
- * 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.rpc.protocol;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.ConcurrentHashSet;
-import org.apache.dubbo.remoting.Constants;
-import org.apache.dubbo.rpc.Exporter;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Protocol;
-import org.apache.dubbo.rpc.ProtocolServer;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.support.ProtocolUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-
-/**
- * abstract ProtocolSupport.
- */
-public abstract class AbstractProtocol implements Protocol {
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
-
-    /**
-     * <host:port, ProtocolServer>
-     */
-    protected final Map<String, ProtocolServer> serverMap = new ConcurrentHashMap<>();
-
-    //TODO SoftReference
-    protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
-
-    protected static String serviceKey(URL url) {
-        int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
-        return serviceKey(port, url.getPath(), url.getParameter(VERSION_KEY), url.getParameter(GROUP_KEY));
-    }
-
-    protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
-        return ProtocolUtils.serviceKey(port, serviceName, serviceVersion, serviceGroup);
-    }
-
-    public List<ProtocolServer> getServers() {
-        return Collections.unmodifiableList(new ArrayList<>(serverMap.values()));
-    }
-
-    @Override
-    public void destroy() {
-        for (Invoker<?> invoker : invokers) {
-            if (invoker != null) {
-                invokers.remove(invoker);
-                try {
-                    if (logger.isInfoEnabled()) {
-                        logger.info("Destroy reference: " + invoker.getUrl());
-                    }
-                    invoker.destroy();
-                } catch (Throwable t) {
-                    logger.warn(t.getMessage(), t);
-                }
-            }
-        }
-        for (String key : new ArrayList<String>(exporterMap.keySet())) {
-            Exporter<?> exporter = exporterMap.remove(key);
-            if (exporter != null) {
-                try {
-                    if (logger.isInfoEnabled()) {
-                        logger.info("Unexport service: " + exporter.getInvoker().getUrl());
-                    }
-                    exporter.unexport();
-                } catch (Throwable t) {
-                    logger.warn(t.getMessage(), t);
-                }
-            }
-        }
-    }
-
-    @Override
-    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
-        return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
-    }
-
-    protected abstract <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException;
-
-    public Map<String, Exporter<?>> getExporterMap() {
-        return exporterMap;
-    }
-
-    public Collection<Exporter<?>> getExporters() {
-        return Collections.unmodifiableCollection(exporterMap.values());
-    }
-}
+/*
+ * 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.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.support.ProtocolUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+
+/**
+ * abstract ProtocolSupport.
+ */
+public abstract class AbstractProtocol implements Protocol {
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    protected final DelegateExporterMap exporterMap = new DelegateExporterMap();
+
+    /**
+     * <host:port, ProtocolServer>
+     */
+    protected final Map<String, ProtocolServer> serverMap = new ConcurrentHashMap<>();
+
+    //TODO SoftReference
+    protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
+
+    protected static String serviceKey(URL url) {
+        int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
+        return serviceKey(port, url.getPath(), url.getParameter(VERSION_KEY), url.getParameter(GROUP_KEY));
+    }
+
+    protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
+        return ProtocolUtils.serviceKey(port, serviceName, serviceVersion, serviceGroup);
+    }
+
+    @Override
+    public List<ProtocolServer> getServers() {
+        return Collections.unmodifiableList(new ArrayList<>(serverMap.values()));
+    }
+
+    @Override
+    public void destroy() {
+        for (Invoker<?> invoker : invokers) {
+            if (invoker != null) {
+                invokers.remove(invoker);
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Destroy reference: " + invoker.getUrl());
+                    }
+                    invoker.destroy();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+        }
+        for (Map.Entry<String, Exporter<?>> item : exporterMap.getExporterMap().entrySet()) {
+            if (exporterMap.removeExportMap(item.getKey(), item.getValue())) {
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Unexport service: " + item.getValue().getInvoker().getUrl());
+                    }
+                    item.getValue().unexport();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            }
+        }
+    }
+
+    @Override
+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+        return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
+    }
+
+    protected abstract <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException;
+
+    public Map<String, Exporter<?>> getExporterMap() {
+        return exporterMap.getExporterMap();
+    }
+
+    public Collection<Exporter<?>> getExporters() {
+        return exporterMap.getExporters();
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
index c9965a2..c2b6f3d 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java
@@ -77,7 +77,7 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
     @SuppressWarnings("unchecked")
     public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {
         final String uri = serviceKey(invoker.getUrl());
-        Exporter<T> exporter = (Exporter<T>) exporterMap.get(uri);
+        Exporter<T> exporter = (Exporter<T>) exporterMap.getExport(uri);
         if (exporter != null) {
             // When modifying the configuration through override, you need to re-expose the newly modified service.
             if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) {
@@ -88,7 +88,7 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
         exporter = new AbstractExporter<T>(invoker) {
             @Override
             public void afterUnExport() {
-                exporterMap.remove(uri);
+                exporterMap.removeExportMap(uri, this);
                 if (runnable != null) {
                     try {
                         runnable.run();
@@ -98,7 +98,7 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
                 }
             }
         };
-        exporterMap.put(uri, exporter);
+        exporterMap.addExportMap(uri, exporter);
         return exporter;
     }
 
@@ -277,5 +277,4 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
         }
     }
 
-
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/DelegateExporterMap.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/DelegateExporterMap.java
new file mode 100644
index 0000000..e596f7f
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/DelegateExporterMap.java
@@ -0,0 +1,86 @@
+/*
+ * 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.rpc.protocol;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.rpc.Exporter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * delegate exportermap oper
+ */
+public class DelegateExporterMap {
+    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
+
+    /**
+     * check is empty map
+     * @return
+     */
+    public boolean isEmpty() {
+        return CollectionUtils.isEmptyMap(exporterMap);
+    }
+
+    /**
+     * get export
+     * @param key
+     * @return
+     */
+    public Exporter<?> getExport(String key) {
+        return exporterMap.get(key);
+    }
+
+    /**
+     * add
+     * @param key
+     * @param exporter
+     */
+    public void addExportMap(String key, Exporter<?> exporter) {
+        exporterMap.put(key, exporter);
+    }
+
+    /**
+     * delete
+     * @param key
+     */
+    public boolean removeExportMap(String key, Exporter<?> exporter) {
+        Exporter<?> findExporter = exporterMap.get(key);
+        if(findExporter == exporter){
+            exporterMap.remove(key);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * get the exports
+     * @return
+     */
+    public Map<String, Exporter<?>> getExporterMap() {
+        return exporterMap;
+    }
+
+    /**
+     * get all exports
+     * @return
+     */
+    public Collection<Exporter<?>> getExporters() {
+        return Collections.unmodifiableCollection(exporterMap.values());
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
index 57c1954..3d64dd2 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java
@@ -16,11 +16,9 @@
  */
 package org.apache.dubbo.rpc.protocol.dubbo;
 
-import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.protocol.AbstractExporter;
-
-import java.util.Map;
+import org.apache.dubbo.rpc.protocol.DelegateExporterMap;
 
 /**
  * DubboExporter
@@ -29,17 +27,16 @@ public class DubboExporter<T> extends AbstractExporter<T> {
 
     private final String key;
 
-    private final Map<String, Exporter<?>> exporterMap;
+    private final DelegateExporterMap delegateExporterMap;
 
-    public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
+    public DubboExporter(Invoker<T> invoker, String key, DelegateExporterMap delegateExporterMap) {
         super(invoker);
         this.key = key;
-        this.exporterMap = exporterMap;
+        this.delegateExporterMap = delegateExporterMap;
     }
 
     @Override
     public void afterUnExport() {
-        exporterMap.remove(key);
+        delegateExporterMap.removeExportMap(key, this);
     }
-
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index 9b6067e..220f8bf 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -225,11 +225,6 @@ public class DubboProtocol extends AbstractProtocol {
         return INSTANCE;
     }
 
-    @Override
-    public Collection<Exporter<?>> getExporters() {
-        return Collections.unmodifiableCollection(exporterMap.values());
-    }
-
     private boolean isClientSide(Channel channel) {
         InetSocketAddress address = channel.getRemoteAddress();
         URL url = channel.getUrl();
@@ -263,11 +258,11 @@ public class DubboProtocol extends AbstractProtocol {
                 (String) inv.getObjectAttachments().get(VERSION_KEY),
                 (String) inv.getObjectAttachments().get(GROUP_KEY)
         );
-        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
+        DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.getExport(serviceKey);
 
         if (exporter == null) {
             throw new RemotingException(channel,
-                    "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " +
+                    "Not found exported service: " + serviceKey + " in " + exporterMap.getExporterMap().keySet() + ", may be version or group mismatch " +
                             ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() +
                             ", message:" + getInvocationWithoutData(inv));
         }
@@ -291,7 +286,7 @@ public class DubboProtocol extends AbstractProtocol {
         // export service.
         String key = serviceKey(url);
         DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
-        exporterMap.put(key, exporter);
+        exporterMap.addExportMap(key, exporter);
 
         //export an stub service for dispatching event
         Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
index cd899b4..307744c 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java
@@ -16,11 +16,9 @@
  */
 package org.apache.dubbo.rpc.protocol.injvm;
 
-import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.protocol.AbstractExporter;
-
-import java.util.Map;
+import org.apache.dubbo.rpc.protocol.DelegateExporterMap;
 
 /**
  * InjvmExporter
@@ -29,18 +27,17 @@ class InjvmExporter<T> extends AbstractExporter<T> {
 
     private final String key;
 
-    private final Map<String, Exporter<?>> exporterMap;
+    private final DelegateExporterMap delegateExporterMap;
 
-    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
+    InjvmExporter(Invoker<T> invoker, String key, DelegateExporterMap delegateExporterMap) {
         super(invoker);
         this.key = key;
-        this.exporterMap = exporterMap;
-        exporterMap.put(key, this);
+        this.delegateExporterMap = delegateExporterMap;
     }
 
     @Override
     public void afterUnExport() {
-        exporterMap.remove(key);
+        delegateExporterMap.removeExportMap(key, this);
     }
-
 }
+
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
index 8e16d2c..cd3c31c 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -31,8 +31,8 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
+import org.apache.dubbo.rpc.protocol.DelegateExporterMap;
 
-import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 
@@ -46,19 +46,19 @@ class InjvmInvoker<T> extends AbstractInvoker<T> {
 
     private final String key;
 
-    private final Map<String, Exporter<?>> exporterMap;
+    private final DelegateExporterMap delegateExporterMap;
 
     private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
 
-    InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
+    InjvmInvoker(Class<T> type, URL url, String key, DelegateExporterMap delegateExporterMap) {
         super(type, url);
         this.key = key;
-        this.exporterMap = exporterMap;
+        this.delegateExporterMap = delegateExporterMap;
     }
 
     @Override
     public boolean isAvailable() {
-        InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
+        InjvmExporter<?> exporter = (InjvmExporter<?>) delegateExporterMap.getExport(key);
         if (exporter == null) {
             return false;
         } else {
@@ -68,7 +68,7 @@ class InjvmInvoker<T> extends AbstractInvoker<T> {
 
     @Override
     public Result doInvoke(Invocation invocation) throws Throwable {
-        Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
+        Exporter<?> exporter = InjvmProtocol.getExporter(delegateExporterMap, getUrl());
         if (exporter == null) {
             throw new RpcException("Service [" + key + "] not found.");
         }
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
index 10df8ee..d9cef56 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java
@@ -18,17 +18,15 @@ package org.apache.dubbo.rpc.protocol.injvm;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.protocol.AbstractProtocol;
+import org.apache.dubbo.rpc.protocol.DelegateExporterMap;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
 
-import java.util.Map;
-
 import static org.apache.dubbo.common.constants.CommonConstants.BROADCAST_CLUSTER;
 import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
 import static org.apache.dubbo.rpc.Constants.SCOPE_KEY;
@@ -58,14 +56,14 @@ public class InjvmProtocol extends AbstractProtocol implements Protocol{
         return INSTANCE;
     }
 
-    static Exporter<?> getExporter(Map<String, Exporter<?>> map, URL key) {
+    static Exporter<?> getExporter(DelegateExporterMap delegateExporterMap, URL key) {
         Exporter<?> result = null;
 
         if (!key.getServiceKey().contains("*")) {
-            result = map.get(key.getServiceKey());
+            result = delegateExporterMap.getExport(key.getServiceKey());
         } else {
-            if (CollectionUtils.isNotEmptyMap(map)) {
-                for (Exporter<?> exporter : map.values()) {
+            if (!delegateExporterMap.isEmpty()) {
+                for (Exporter<?> exporter : delegateExporterMap.getExporters()) {
                     if (UrlUtils.isServiceKeyMatch(key, exporter.getInvoker().getUrl())) {
                         result = exporter;
                         break;
@@ -91,7 +89,10 @@ public class InjvmProtocol extends AbstractProtocol implements Protocol{
 
     @Override
     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
-        return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
+        String serviceKey = invoker.getUrl().getServiceKey();
+        InjvmExporter<T> tInjvmExporter = new InjvmExporter<>(invoker, serviceKey, exporterMap);
+        exporterMap.addExportMap(serviceKey, tInjvmExporter);
+        return tInjvmExporter;
     }
 
     @Override
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
index 0370c6b..3cc461f 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java
@@ -24,10 +24,12 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
 
+import org.apache.dubbo.rpc.protocol.DelegateExporterMap;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 
@@ -74,7 +76,32 @@ public class InjvmProtocolTest {
         assertEquals(service.getSize(new String[]{"", "", ""}), 3);
         service.invoke("injvm://127.0.0.1/TestService", "invoke");
 
-        InjvmInvoker injvmInvoker = new InjvmInvoker(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService"), null, new HashMap<String, Exporter<?>>());
+        InjvmInvoker injvmInvoker = new InjvmInvoker(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService"), null, new DelegateExporterMap() {
+            @Override
+            public boolean isEmpty() {
+                return true;
+            }
+
+            @Override
+            public Exporter<?> getExport(String key) {
+                return null;
+            }
+
+            @Override
+            public void addExportMap(String key, Exporter<?> exporter) {
+
+            }
+
+            @Override
+            public boolean removeExportMap(String key, Exporter<?> exporter) {
+                return true;
+            }
+
+            @Override
+            public Collection<Exporter<?>> getExporters() {
+                return null;
+            }
+        });
         assertFalse(injvmInvoker.isAvailable());
 
     }
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
index 2758c5e..eb68b58 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
@@ -1,272 +1,272 @@
-/*
- * 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.rpc.protocol.thrift;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.remoting.Channel;
-import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.RemotingServer;
-import org.apache.dubbo.remoting.Transporter;
-import org.apache.dubbo.remoting.exchange.ExchangeChannel;
-import org.apache.dubbo.remoting.exchange.ExchangeClient;
-import org.apache.dubbo.remoting.exchange.ExchangeHandler;
-import org.apache.dubbo.remoting.exchange.ExchangeServer;
-import org.apache.dubbo.remoting.exchange.Exchangers;
-import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
-import org.apache.dubbo.rpc.Exporter;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.ProtocolServer;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.protocol.AbstractProtocol;
-import org.apache.dubbo.rpc.protocol.dubbo.DubboExporter;
-
-import java.util.ArrayList;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Function;
-
-import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
-import static org.apache.dubbo.remoting.Constants.CHANNEL_READONLYEVENT_SENT_KEY;
-import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
-import static org.apache.dubbo.remoting.Constants.CODEC_KEY;
-import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY;
-import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
-import static org.apache.dubbo.rpc.Constants.IS_SERVER_KEY;
-
-/**
- * @since 2.7.0, use https://github.com/dubbo/dubbo-rpc-native-thrift instead
- */
-@Deprecated
-public class ThriftProtocol extends AbstractProtocol {
-
-    public static final int DEFAULT_PORT = 40880;
-
-    public static final String NAME = "thrift";
-
-    private ExchangeHandler handler = new ExchangeHandlerAdapter() {
-
-        @Override
-        public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException {
-
-            if (msg instanceof Invocation) {
-                Invocation inv = (Invocation) msg;
-                String path = (String) inv.getObjectAttachments().get(PATH_KEY);
-                String serviceKey = serviceKey(channel.getLocalAddress().getPort(),
-                        path, null, null);
-                DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
-                if (exporter == null) {
-                    throw new RemotingException(channel,
-                            "Not found exported service: "
-                                    + serviceKey
-                                    + " in "
-                                    + exporterMap.keySet()
-                                    + ", may be version or group mismatch "
-                                    + ", channel: consumer: "
-                                    + channel.getRemoteAddress()
-                                    + " --> provider: "
-                                    + channel.getLocalAddress()
-                                    + ", message:" + msg);
-                }
-
-                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
-
-                Result result = exporter.getInvoker().invoke(inv);
-                return result.thenApply(Function.identity());
-            }
-
-            throw new RemotingException(channel,
-                    "Unsupported request: "
-                            + (msg.getClass().getName() + ": " + msg)
-                            + ", channel: consumer: "
-                            + channel.getRemoteAddress()
-                            + " --> provider: "
-                            + channel.getLocalAddress());
-        }
-
-        @Override
-        public void received(Channel channel, Object message) throws RemotingException {
-            if (message instanceof Invocation) {
-                reply((ExchangeChannel) channel, message);
-            } else {
-                super.received(channel, message);
-            }
-        }
-
-    };
-
-    @Override
-    public int getDefaultPort() {
-        return DEFAULT_PORT;
-    }
-
-    @Override
-    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
-
-        // can use thrift codec only
-        URL url = invoker.getUrl().addParameter(CODEC_KEY, ThriftCodec.NAME);
-        // find server.
-        String key = url.getAddress();
-        // client can expose a service for server to invoke only.
-        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
-        if (isServer && !serverMap.containsKey(key)) {
-            serverMap.put(key, getServer(url));
-        }
-        // export service.
-        key = serviceKey(url);
-        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
-        exporterMap.put(key, exporter);
-
-        return exporter;
-    }
-
-    @Override
-    public void destroy() {
-
-        super.destroy();
-
-        for (String key : new ArrayList<String>(serverMap.keySet())) {
-
-            ProtocolServer protocolServer = serverMap.remove(key);
-
-            if (protocolServer != null) {
-                RemotingServer server = protocolServer.getRemotingServer();
-                try {
-                    if (logger.isInfoEnabled()) {
-                        logger.info("Close dubbo server: " + server.getLocalAddress());
-                    }
-                    server.close(ConfigurationUtils.getServerShutdownTimeout());
-                } catch (Throwable t) {
-                    logger.warn(t.getMessage(), t);
-                }
-            } // ~ end of if ( server != null )
-
-        } // ~ end of loop serverMap
-
-    } // ~ end of method destroy
-
-    @Override
-    protected <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException {
-
-        ThriftInvoker<T> invoker = new ThriftInvoker<T>(type, url, getClients(url), invokers);
-
-        invokers.add(invoker);
-
-        return invoker;
-
-    }
-
-    private ExchangeClient[] getClients(URL url) {
-
-        int connections = url.getParameter(CONNECTIONS_KEY, 1);
-
-        ExchangeClient[] clients = new ExchangeClient[connections];
-
-        for (int i = 0; i < clients.length; i++) {
-            clients[i] = initClient(url);
-        }
-        return clients;
-    }
-
-    private ExchangeClient initClient(URL url) {
-
-        ExchangeClient client;
-
-//        url = url.addParameter(CODEC_KEY, ThriftCodec.NAME);
-
-        try {
-            client = Exchangers.connect(url);
-        } catch (RemotingException e) {
-            throw new RpcException("Fail to create remoting client for service(" + url
-                    + "): " + e.getMessage(), e);
-        }
-
-        return client;
-
-    }
-
-    private ProtocolServer getServer(URL url) {
-        // enable sending readonly event when server closes by default
-        url = url.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
-        String str = url.getParameter(SERVER_KEY, org.apache.dubbo.rpc.Constants.DEFAULT_REMOTING_SERVER);
-
-        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
-            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
-        }
-
-        ExchangeServer server;
-        try {
-            server = Exchangers.bind(url, handler);
-        } catch (RemotingException e) {
-            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
-        }
-        str = url.getParameter(CLIENT_KEY);
-        if (str != null && str.length() > 0) {
-            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
-            if (!supportedTypes.contains(str)) {
-                throw new RpcException("Unsupported client type: " + str);
-            }
-        }
-        return new ThriftProtocolServer(server);
-    }
-
-    private class ThriftProtocolServer implements ProtocolServer {
-
-        private ExchangeServer server;
-        private String address;
-
-        public ThriftProtocolServer(ExchangeServer server) {
-            this.server = server;
-        }
-
-        @Override
-        public RemotingServer getRemotingServer() {
-            return server;
-        }
-
-        @Override
-        public String getAddress() {
-            return StringUtils.isNotEmpty(address) ? address : server.getUrl().getAddress();
-        }
-
-        @Override
-        public void setAddress(String address) {
-            this.address = address;
-        }
-
-        @Override
-        public URL getUrl() {
-            return server.getUrl();
-        }
-
-        @Override
-        public void reset(URL url) {
-            server.reset(url);
-        }
-
-        @Override
-        public void close() {
-            server.close();
-        }
-    }
-
-}
+/*
+ * 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.rpc.protocol.thrift;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.RemotingServer;
+import org.apache.dubbo.remoting.Transporter;
+import org.apache.dubbo.remoting.exchange.ExchangeChannel;
+import org.apache.dubbo.remoting.exchange.ExchangeClient;
+import org.apache.dubbo.remoting.exchange.ExchangeHandler;
+import org.apache.dubbo.remoting.exchange.ExchangeServer;
+import org.apache.dubbo.remoting.exchange.Exchangers;
+import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.AbstractProtocol;
+import org.apache.dubbo.rpc.protocol.dubbo.DubboExporter;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.remoting.Constants.CHANNEL_READONLYEVENT_SENT_KEY;
+import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
+import static org.apache.dubbo.remoting.Constants.CODEC_KEY;
+import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY;
+import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
+import static org.apache.dubbo.rpc.Constants.IS_SERVER_KEY;
+
+/**
+ * @since 2.7.0, use https://github.com/dubbo/dubbo-rpc-native-thrift instead
+ */
+@Deprecated
+public class ThriftProtocol extends AbstractProtocol {
+
+    public static final int DEFAULT_PORT = 40880;
+
+    public static final String NAME = "thrift";
+
+    private ExchangeHandler handler = new ExchangeHandlerAdapter() {
+
+        @Override
+        public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException {
+
+            if (msg instanceof Invocation) {
+                Invocation inv = (Invocation) msg;
+                String path = (String) inv.getObjectAttachments().get(PATH_KEY);
+                String serviceKey = serviceKey(channel.getLocalAddress().getPort(),
+                        path, null, null);
+                DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.getExport(serviceKey);
+                if (exporter == null) {
+                    throw new RemotingException(channel,
+                            "Not found exported service: "
+                                    + serviceKey
+                                    + " in "
+                                    + exporterMap.getExporterMap().keySet()
+                                    + ", may be version or group mismatch "
+                                    + ", channel: consumer: "
+                                    + channel.getRemoteAddress()
+                                    + " --> provider: "
+                                    + channel.getLocalAddress()
+                                    + ", message:" + msg);
+                }
+
+                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
+
+                Result result = exporter.getInvoker().invoke(inv);
+                return result.thenApply(Function.identity());
+            }
+
+            throw new RemotingException(channel,
+                    "Unsupported request: "
+                            + (msg.getClass().getName() + ": " + msg)
+                            + ", channel: consumer: "
+                            + channel.getRemoteAddress()
+                            + " --> provider: "
+                            + channel.getLocalAddress());
+        }
+
+        @Override
+        public void received(Channel channel, Object message) throws RemotingException {
+            if (message instanceof Invocation) {
+                reply((ExchangeChannel) channel, message);
+            } else {
+                super.received(channel, message);
+            }
+        }
+
+    };
+
+    @Override
+    public int getDefaultPort() {
+        return DEFAULT_PORT;
+    }
+
+    @Override
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+
+        // can use thrift codec only
+        URL url = invoker.getUrl().addParameter(CODEC_KEY, ThriftCodec.NAME);
+        // find server.
+        String key = url.getAddress();
+        // client can expose a service for server to invoke only.
+        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
+        if (isServer && !serverMap.containsKey(key)) {
+            serverMap.put(key, getServer(url));
+        }
+        // export service.
+        key = serviceKey(url);
+        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
+        exporterMap.addExportMap(key, exporter);
+
+        return exporter;
+    }
+
+    @Override
+    public void destroy() {
+
+        super.destroy();
+
+        for (String key : new ArrayList<String>(serverMap.keySet())) {
+
+            ProtocolServer protocolServer = serverMap.remove(key);
+
+            if (protocolServer != null) {
+                RemotingServer server = protocolServer.getRemotingServer();
+                try {
+                    if (logger.isInfoEnabled()) {
+                        logger.info("Close dubbo server: " + server.getLocalAddress());
+                    }
+                    server.close(ConfigurationUtils.getServerShutdownTimeout());
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            } // ~ end of if ( server != null )
+
+        } // ~ end of loop serverMap
+
+    } // ~ end of method destroy
+
+    @Override
+    protected <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException {
+
+        ThriftInvoker<T> invoker = new ThriftInvoker<T>(type, url, getClients(url), invokers);
+
+        invokers.add(invoker);
+
+        return invoker;
+
+    }
+
+    private ExchangeClient[] getClients(URL url) {
+
+        int connections = url.getParameter(CONNECTIONS_KEY, 1);
+
+        ExchangeClient[] clients = new ExchangeClient[connections];
+
+        for (int i = 0; i < clients.length; i++) {
+            clients[i] = initClient(url);
+        }
+        return clients;
+    }
+
+    private ExchangeClient initClient(URL url) {
+
+        ExchangeClient client;
+
+//        url = url.addParameter(CODEC_KEY, ThriftCodec.NAME);
+
+        try {
+            client = Exchangers.connect(url);
+        } catch (RemotingException e) {
+            throw new RpcException("Fail to create remoting client for service(" + url
+                    + "): " + e.getMessage(), e);
+        }
+
+        return client;
+
+    }
+
+    private ProtocolServer getServer(URL url) {
+        // enable sending readonly event when server closes by default
+        url = url.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
+        String str = url.getParameter(SERVER_KEY, org.apache.dubbo.rpc.Constants.DEFAULT_REMOTING_SERVER);
+
+        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
+            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
+        }
+
+        ExchangeServer server;
+        try {
+            server = Exchangers.bind(url, handler);
+        } catch (RemotingException e) {
+            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
+        }
+        str = url.getParameter(CLIENT_KEY);
+        if (str != null && str.length() > 0) {
+            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
+            if (!supportedTypes.contains(str)) {
+                throw new RpcException("Unsupported client type: " + str);
+            }
+        }
+        return new ThriftProtocolServer(server);
+    }
+
+    private class ThriftProtocolServer implements ProtocolServer {
+
+        private ExchangeServer server;
+        private String address;
+
+        public ThriftProtocolServer(ExchangeServer server) {
+            this.server = server;
+        }
+
+        @Override
+        public RemotingServer getRemotingServer() {
+            return server;
+        }
+
+        @Override
+        public String getAddress() {
+            return StringUtils.isNotEmpty(address) ? address : server.getUrl().getAddress();
+        }
+
+        @Override
+        public void setAddress(String address) {
+            this.address = address;
+        }
+
+        @Override
+        public URL getUrl() {
+            return server.getUrl();
+        }
+
+        @Override
+        public void reset(URL url) {
+            server.reset(url);
+        }
+
+        @Override
+        public void close() {
+            server.close();
+        }
+    }
+
+}