From 1f526682a3b428bf642d17c7bfbff0447a75ee92 Mon Sep 17 00:00:00 2001 From: "ken.lj" Date: Fri, 12 Apr 2019 10:29:30 +0800 Subject: [PATCH] Async optimization (#3738) * Result implement CF * Result implement CF * Result implement CF * Add AsyncRpcResult * Fix bugs and refactor Filter * Try to add onSend onError for Filter * invoke different filter method according to result status. * make generic work with async call, including add $invokeAsync * refactor legacy Filter implementation to work with onResponse. * demo changes * Fixes #3620, provider attachment lose on consumer side, fix this by reverting RpcContext copy * AsyncRpcResult should always holds an Invocation instance * refactor filter signature * reimplement embedded Filters * use ProviderModel modification in 3.x * Fix address notification processing workflow after merging 3.x branch * Fix UT * Fix UT * Unit test of JValidator; Clean code of JValidator (#3723) * Fixes #3625 (#3730) use constant to replace magic number * Fix conflict when merging master and 3.x * Fix conflict when merging master and 3.x * Result interface itself has Future status. * Fix DefaultFuture UT * Wrap all protocol Invoker with AsyncToSyncInvoker & Fix UT * Add license * fix UT * Fix ut in MonitorFilterTest * avoid duplicate async to sync wrapper * return async result in CacheFilter. * fix UT in CacheFilterTest * Add generic condition check to GenericFilter callback. * Fix UT * Get generic from RpcContext if the value in Invocation is empty. * Fix RSocketProtocol to meet AbstractProtocol adjustment * rename RpcResult to AppResponse to help avoid confusion with AsyncRpcResult. * RSocket module switch to AsyncRpcResult --- .../support/FailbackClusterInvoker.java | 4 +- .../support/FailsafeClusterInvoker.java | 4 +- .../support/ForkingClusterInvoker.java | 3 +- .../support/MergeableClusterInvoker.java | 32 ++- .../support/wrapper/MockClusterInvoker.java | 4 +- .../apache/dubbo/rpc/cluster/StickyTest.java | 4 +- .../cluster/directory/MockDirInvocation.java | 10 + .../router/file/FileRouterEngineTest.java | 4 +- .../support/FailSafeClusterInvokerTest.java | 4 +- .../support/FailbackClusterInvokerTest.java | 4 +- .../support/FailfastClusterInvokerTest.java | 4 +- .../support/FailoverClusterInvokerTest.java | 4 +- .../support/ForkingClusterInvokerTest.java | 6 +- .../support/MergeableClusterInvokerTest.java | 11 +- .../org/apache/dubbo/common/Constants.java | 7 + .../apache/dubbo/common/logger/Logger.java | 2 +- .../dubbo/common/utils/ReflectUtils.java | 23 ++ .../config/PropertiesConfigurationTest.java | 31 +++ .../com/alibaba/dubbo/rpc/Invocation.java | 9 + .../java/com/alibaba/dubbo/rpc/Result.java | 36 +++- .../apache/dubbo/filter/LegacyInvoker.java | 4 +- .../apache/dubbo/service/MockInvocation.java | 10 + .../consumer/comp/DemoServiceComponent.java | 7 + .../dubbo/demo/provider/DemoServiceImpl.java | 7 + .../dubbo/demo/provider/DemoServiceImpl.java | 7 + .../org/apache/dubbo/demo/DemoService.java | 3 + .../dubbo/demo/consumer/Application.java | 11 +- .../main/resources/spring/dubbo-consumer.xml | 2 +- .../dubbo/demo/provider/DemoServiceImpl.java | 12 ++ .../dubbo/cache/filter/CacheFilter.java | 6 +- .../dubbo/cache/filter/CacheFilterTest.java | 31 +-- .../validation/filter/ValidationFilter.java | 4 +- .../filter/ValidationFilterTest.java | 12 +- .../apache/dubbo/monitor/MonitorService.java | 1 + .../dubbo/monitor/support/MonitorFilter.java | 197 +++++++++-------- .../monitor/support/MonitorFilterTest.java | 16 +- .../dubbo/registry/dubbo/MockChannel.java | 8 +- .../dubbo/registry/dubbo/MockedClient.java | 18 +- .../dubbo/remoting/RemotingException.java | 3 +- .../dubbo/remoting/TimeoutException.java | 3 +- .../remoting/exchange/ExchangeChannel.java | 6 +- .../dubbo/remoting/exchange/Response.java | 61 ++++++ .../remoting/exchange/ResponseFuture.java | 58 ----- .../exchange/support/DefaultFuture.java | 196 ++++------------- .../exchange/support/SimpleFuture.java | 54 ----- .../support/header/HeaderExchangeChannel.java | 6 +- .../support/header/HeaderExchangeClient.java | 6 +- .../support/header/HeaderExchangeHandler.java | 15 +- .../java/org/apache/dubbo/remoting/Main.java | 4 +- .../dubbo/remoting/PerformanceServerTest.java | 1 - .../exchange/support/DefaultFutureTest.java | 4 +- .../handler/HeaderExchangeHandlerTest.java | 2 +- .../transport/mina/ClientToServerTest.java | 5 +- .../support/header/HeartbeatHandlerTest.java | 1 + .../transport/netty/ClientToServerTest.java | 6 +- .../transport/netty4/ClientToServerTest.java | 5 +- .../org/apache/dubbo/rpc/AbstractResult.java | 74 ------- .../org/apache/dubbo/rpc/AppResponse.java | 160 ++++++++++++++ .../org/apache/dubbo/rpc/AsyncRpcResult.java | 201 +++++++++--------- .../java/org/apache/dubbo/rpc/Filter.java | 31 +-- .../org/apache/dubbo/rpc/FutureContext.java | 52 +++++ .../java/org/apache/dubbo/rpc/Invocation.java | 4 + .../java/org/apache/dubbo/rpc/InvokeMode.java | 23 ++ .../apache/dubbo/rpc/ListenableFilter.java | 67 +++--- .../java/org/apache/dubbo/rpc/Result.java | 23 +- .../java/org/apache/dubbo/rpc/RpcContext.java | 35 +-- .../org/apache/dubbo/rpc/RpcInvocation.java | 21 ++ .../java/org/apache/dubbo/rpc/RpcResult.java | 128 ----------- .../dubbo/rpc/SimpleAsyncRpcResult.java | 50 ----- .../dubbo/rpc/filter/ActiveLimitFilter.java | 72 +++++-- .../dubbo/rpc/filter/CompatibleFilter.java | 70 +++--- .../rpc/filter/ConsumerContextFilter.java | 26 ++- .../dubbo/rpc/filter/ContextFilter.java | 24 ++- .../apache/dubbo/rpc/filter/EchoFilter.java | 4 +- .../dubbo/rpc/filter/ExceptionFilter.java | 126 ++++++----- .../dubbo/rpc/filter/ExecuteLimitFilter.java | 34 ++- .../dubbo/rpc/filter/GenericFilter.java | 62 ++++-- .../dubbo/rpc/filter/GenericImplFilter.java | 176 ++++++++------- .../dubbo/rpc/filter/TimeoutFilter.java | 49 ++--- .../dubbo/rpc/protocol/AbstractInvoker.java | 16 +- .../dubbo/rpc/protocol/AbstractProtocol.java | 8 + .../rpc/protocol/AbstractProxyProtocol.java | 7 +- .../rpc/protocol/AsyncToSyncInvoker.java | 89 ++++++++ .../rpc/protocol/ProtocolFilterWrapper.java | 31 ++- .../dubbo/rpc/proxy/AbstractProxyInvoker.java | 46 ++-- .../rpc/proxy/InvokerInvocationHandler.java | 14 +- .../dubbo/rpc/service/GenericService.java | 10 + .../apache/dubbo/rpc/support/MockInvoker.java | 6 +- .../dubbo/rpc/support/MockProtocol.java | 2 +- .../apache/dubbo/rpc/support/RpcUtils.java | 40 ++-- ...pcResultTest.java => AppResponseTest.java} | 0 .../rpc/filter/ActiveLimitFilterTest.java | 24 ++- .../filter/CompatibleFilterFilterTest.java | 39 ++-- .../rpc/filter/ConsumerContextFilterTest.java | 15 +- .../dubbo/rpc/filter/ContextFilterTest.java | 4 +- .../dubbo/rpc/filter/EchoFilterTest.java | 6 +- .../dubbo/rpc/filter/ExceptionFilterTest.java | 37 ++-- .../rpc/filter/ExecuteLimitFilterTest.java | 7 +- .../dubbo/rpc/filter/GenericFilterTest.java | 24 ++- .../rpc/filter/GenericImplFilterTest.java | 21 +- .../dubbo/rpc/filter/TimeoutFilterTest.java | 6 +- .../dubbo/rpc/filter/TokenFilterTest.java | 8 +- .../dubbo/rpc/support/BlockMyInvoker.java | 10 +- .../dubbo/rpc/support/MockInvocation.java | 10 + .../apache/dubbo/rpc/support/MyInvoker.java | 10 +- .../protocol/dubbo/CallbackServiceCodec.java | 3 +- .../protocol/dubbo/ChannelWrappedInvoker.java | 17 +- .../protocol/dubbo/DecodeableRpcResult.java | 4 +- .../rpc/protocol/dubbo/DubboCountCodec.java | 4 +- .../rpc/protocol/dubbo/DubboInvoker.java | 28 +-- .../rpc/protocol/dubbo/DubboProtocol.java | 15 +- .../rpc/protocol/dubbo/FutureAdapter.java | 56 ++--- .../dubbo/LazyConnectExchangeClient.java | 6 +- .../dubbo/ReferenceCountExchangeClient.java | 6 +- .../protocol/dubbo/filter/FutureFilter.java | 56 ++--- .../dubbo/telnet/InvokeTelnetHandler.java | 7 +- .../dubbo/DubboInvokerAvilableTest.java | 20 +- .../rpc/protocol/dubbo/FutureFilterTest.java | 6 +- .../protocol/dubbo/ImplicitCallBackTest.java | 2 +- .../ReferenceCountExchangeClientTest.java | 5 +- .../rpc/protocol/hessian/HessianProtocol.java | 2 +- .../dubbo/rpc/protocol/http/HttpProtocol.java | 4 +- .../rpc/protocol/injvm/InjvmProtocol.java | 2 +- .../protocol/memcached/MemcachedProtocol.java | 10 +- .../rpc/protocol/redis/RedisProtocol.java | 12 +- .../dubbo/rpc/protocol/rest/RestProtocol.java | 2 +- .../dubbo/rpc/protocol/rmi/RmiProtocol.java | 3 +- .../rpc/protocol/thrift/ThriftCodec.java | 10 +- .../rpc/protocol/thrift/ThriftInvoker.java | 11 +- .../rpc/protocol/thrift/ThriftProtocol.java | 9 +- .../rpc/protocol/thrift/ThriftCodecTest.java | 20 +- .../webservice/WebServiceProtocol.java | 2 +- 132 files changed, 1751 insertions(+), 1571 deletions(-) create mode 100644 dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java delete mode 100644 dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseFuture.java delete mode 100644 dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/SimpleFuture.java delete mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AbstractResult.java create mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java create mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.java create mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java rename dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java => dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java (71%) delete mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcResult.java delete mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/SimpleAsyncRpcResult.java create mode 100644 dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AsyncToSyncInvoker.java rename dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/{RpcResultTest.java => AppResponseTest.java} (100%) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java index dd68d7ef3dd..e9d090c4133 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java @@ -24,11 +24,11 @@ import org.apache.dubbo.common.timer.Timer; import org.apache.dubbo.common.timer.TimerTask; import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; @@ -99,7 +99,7 @@ protected Result doInvoke(Invocation invocation, List> invokers, Load logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: " + e.getMessage() + ", ", e); addFailed(loadbalance, invocation, invokers, invoker); - return new RpcResult(); // ignore + return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java index 0f5378a19a0..061068a71b0 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java @@ -18,11 +18,11 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; @@ -50,7 +50,7 @@ public Result doInvoke(Invocation invocation, List> invokers, LoadBal return invoker.invoke(invocation); } catch (Throwable e) { logger.error("Failsafe ignore exception: " + e.getMessage(), e); - return new RpcResult(); // ignore + return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore } } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java index 184a7b63df6..6b02731304a 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java @@ -36,6 +36,8 @@ import java.util.concurrent.atomic.AtomicInteger; /** + * NOTICE! This implementation does not work well with async call. + * * Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources. * * Fork @@ -66,7 +68,6 @@ public Result doInvoke(final Invocation invocation, List> invokers, L } else { selected = new ArrayList<>(); for (int i = 0; i < forks; i++) { - // TODO. Add some comment here, refer chinese version for more details. Invoker invoker = select(loadbalance, invocation, invokers, selected); if (!selected.contains(invoker)) { //Avoid add the same invoker several times. diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java index 82a0e8a2cb7..400a2c487a6 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java @@ -23,12 +23,12 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.Merger; @@ -41,12 +41,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +/** + * NOTICE! Does not work with async call. + * @param + */ @SuppressWarnings("unchecked") public class MergeableClusterInvoker extends AbstractClusterInvoker { @@ -86,26 +87,19 @@ protected Result doInvoke(Invocation invocation, List> invokers, Load returnType = null; } - Map> results = new HashMap>(); + Map results = new HashMap<>(); for (final Invoker invoker : invokers) { - Future future = executor.submit(new Callable() { - @Override - public Result call() throws Exception { - return invoker.invoke(new RpcInvocation(invocation, invoker)); - } - }); - results.put(invoker.getUrl().getServiceKey(), future); + results.put(invoker.getUrl().getServiceKey(), invoker.invoke(new RpcInvocation(invocation, invoker))); } Object result = null; List resultList = new ArrayList(results.size()); - int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); - for (Map.Entry> entry : results.entrySet()) { - Future future = entry.getValue(); + for (Map.Entry entry : results.entrySet()) { + Result asyncResult = entry.getValue(); try { - Result r = future.get(timeout, TimeUnit.MILLISECONDS); + Result r = asyncResult.get(); if (r.hasException()) { log.error("Invoke " + getGroupDescFromServiceKey(entry.getKey()) + " failed: " + r.getException().getMessage(), @@ -119,13 +113,13 @@ public Result call() throws Exception { } if (resultList.isEmpty()) { - return new RpcResult((Object) null); + return AsyncRpcResult.newDefaultAsyncResult(invocation); } else if (resultList.size() == 1) { return resultList.iterator().next(); } if (returnType == void.class) { - return new RpcResult((Object) null); + return AsyncRpcResult.newDefaultAsyncResult(invocation); } if (merger.startsWith(".")) { @@ -173,7 +167,7 @@ public Result call() throws Exception { throw new RpcException("There is no merger to merge result."); } } - return new RpcResult(result); + return AsyncRpcResult.newDefaultAsyncResult(result, invocation); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java index 59303142d85..1628600a91f 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java @@ -22,12 +22,12 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.support.MockInvoker; @@ -113,7 +113,7 @@ private Result doMockInvoke(Invocation invocation, RpcException e) { result = minvoker.invoke(invocation); } catch (RpcException me) { if (me.isBiz()) { - result = new RpcResult(me.getCause()); + result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation); } else { throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause()); } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/StickyTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/StickyTest.java index b10fa01c802..0fce016fbf1 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/StickyTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/StickyTest.java @@ -20,12 +20,12 @@ import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; import org.junit.jupiter.api.Assertions; @@ -48,7 +48,7 @@ public class StickyTest { private Invoker invoker2 = mock(Invoker.class); private RpcInvocation invocation; private Directory dic; - private Result result = new RpcResult(); + private Result result = new AppResponse(); private StickyClusterInvoker clusterinvoker = null; private URL url = URL.valueOf("test://test:11/test?" + "&loadbalance=roundrobin" diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java index 279160e7167..07e115d803b 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java @@ -51,6 +51,16 @@ public Map getAttachments() { return attachments; } + @Override + public void setAttachment(String key, String value) { + + } + + @Override + public void setAttachmentIfAbsent(String key, String value) { + + } + public Invoker getInvoker() { return null; } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java index c4cb85daf8b..74056f38a5b 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java @@ -19,12 +19,12 @@ import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.RouterFactory; @@ -52,7 +52,7 @@ public class FileRouterEngineTest { Invoker invoker2 = mock(Invoker.class); Invocation invocation; StaticDirectory dic; - Result result = new RpcResult(); + Result result = new AppResponse(); private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension(); @BeforeAll diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java index 413d9b9dc8a..8ab596bec1e 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java @@ -18,11 +18,11 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.LogUtil; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.filter.DemoService; @@ -48,7 +48,7 @@ public class FailSafeClusterInvokerTest { Invoker invoker = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; - Result result = new RpcResult(); + Result result = new AppResponse(); /** * @throws java.lang.Exception diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java index ad96a66830d..b745b0e1b26 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java @@ -20,11 +20,11 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.LogUtil; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.log4j.Level; @@ -59,7 +59,7 @@ public class FailbackClusterInvokerTest { Invoker invoker = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; - Result result = new RpcResult(); + Result result = new AppResponse(); /** * @throws java.lang.Exception diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java index 9b6d2e88b73..6a706868bb9 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java @@ -17,12 +17,12 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.junit.jupiter.api.Assertions; @@ -47,7 +47,7 @@ public class FailfastClusterInvokerTest { Invoker invoker1 = mock(Invoker.class); RpcInvocation invocation = new RpcInvocation(); Directory dic; - Result result = new RpcResult(); + Result result = new AppResponse(); /** * @throws java.lang.Exception diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java index ff29a6183a9..6a2e76e0474 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java @@ -17,12 +17,12 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.protocol.AbstractInvoker; @@ -55,7 +55,7 @@ public class FailoverClusterInvokerTest { private Invoker invoker2 = mock(Invoker.class); private RpcInvocation invocation = new RpcInvocation(); private Directory dic; - private Result result = new RpcResult(); + private Result result = new AppResponse(); /** * @throws java.lang.Exception diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java index b3d343a3bbe..aa27fabe458 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java @@ -17,12 +17,12 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; -import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.cluster.Directory; import org.junit.jupiter.api.Assertions; @@ -50,7 +50,7 @@ public class ForkingClusterInvokerTest { private Invoker invoker3 = mock(Invoker.class); private RpcInvocation invocation = new RpcInvocation(); private Directory dic; - private Result result = new RpcResult(); + private Result result = new AppResponse(); @BeforeEach public void setUp() throws Exception { diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java index 8a4089c9049..a79a9facc09 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java @@ -18,10 +18,11 @@ import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.junit.jupiter.api.Assertions; @@ -119,7 +120,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return MenuService.class; } if ("invoke".equals(method.getName())) { - return new RpcResult(firstMenu); + return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation); } return null; } @@ -135,7 +136,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return MenuService.class; } if ("invoke".equals(method.getName())) { - return new RpcResult(secondMenu); + return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation); } return null; } @@ -195,14 +196,14 @@ public void testAddMenu() throws Exception { given(firstInvoker.getUrl()).willReturn( url.addParameter(Constants.GROUP_KEY, "first")); given(firstInvoker.getInterface()).willReturn(MenuService.class); - given(firstInvoker.invoke(invocation)).willReturn(new RpcResult()) + given(firstInvoker.invoke(invocation)).willReturn(new AppResponse()) ; given(firstInvoker.isAvailable()).willReturn(true); given(secondInvoker.getUrl()).willReturn( url.addParameter(Constants.GROUP_KEY, "second")); given(secondInvoker.getInterface()).willReturn(MenuService.class); - given(secondInvoker.invoke(invocation)).willReturn(new RpcResult()) + given(secondInvoker.invoke(invocation)).willReturn(new AppResponse()) ; given(secondInvoker.isAvailable()).willReturn(true); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java index c89a31421fa..3dc4a7c2aa9 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java @@ -99,6 +99,8 @@ public class Constants { public static final String $INVOKE = "$invoke"; + public static final String $INVOKE_ASYNC = "$invokeAsync"; + public static final String $ECHO = "$echo"; public static final int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32); @@ -847,6 +849,11 @@ public class Constants { */ public static final String DEVELOPMENT_ENVIRONMENT = "develop"; + /** + * Consumer side 's proxy class + */ + public static final String PROXY_CLASS_REF = "refClass"; + /** * Production environment key. */ diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/Logger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/Logger.java index c505326d0c3..874ab564a2e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/Logger.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/Logger.java @@ -142,7 +142,7 @@ public interface Logger { /** * Is debug logging currently enabled? - * + *  * @return true if debug is enabled */ boolean isDebugEnabled(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java index ccec6875ca8..0c1a13dfe25 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; @@ -39,6 +40,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -1096,4 +1098,25 @@ public static Map getBeanPropertyReadMethods(Class cl) { return properties; } + + public static Type[] getReturnTypes(Method method) { + Class returnType = method.getReturnType(); + Type genericReturnType = method.getGenericReturnType(); + if (Future.class.isAssignableFrom(returnType)) { + if (genericReturnType instanceof ParameterizedType) { + Type actualArgType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0]; + if (actualArgType instanceof ParameterizedType) { + returnType = (Class) ((ParameterizedType) actualArgType).getRawType(); + genericReturnType = actualArgType; + } else { + returnType = (Class) actualArgType; + genericReturnType = returnType; + } + } else { + returnType = null; + genericReturnType = null; + } + } + return new Type[]{returnType, genericReturnType}; + } } \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java new file mode 100644 index 00000000000..30b81f0a67f --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java @@ -0,0 +1,31 @@ +/* + * 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.common.config; + + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PropertiesConfigurationTest { + + @Test + public void testOrderPropertiesProviders() { + PropertiesConfiguration configuration = new PropertiesConfiguration("test", null); + Assertions.assertTrue(configuration.getInternalProperty("testKey").equals("999")); + } + +} diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java index b96d14d1ee4..591af2c6203 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java @@ -29,6 +29,15 @@ default org.apache.dubbo.rpc.Invocation getOriginal() { return null; } + @Override + default void setAttachmentIfAbsent(String key, String value) { + } + + @Override + default void setAttachment(String key, String value) { + + } + class CompatibleInvocation implements Invocation { private org.apache.dubbo.rpc.Invocation delegate; diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java index a661b2b6b5b..22531301be1 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java @@ -17,11 +17,42 @@ package com.alibaba.dubbo.rpc; +import org.apache.dubbo.rpc.AppResponse; + import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; @Deprecated public interface Result extends org.apache.dubbo.rpc.Result { + @Override + default void setValue(Object value) { + + } + + @Override + default void setException(Throwable t) { + + } + + @Override + default org.apache.dubbo.rpc.Result thenApplyWithContext(Function fn) { + return this; + } + + @Override + default CompletableFuture thenApply(Function fn) { + return null; + } + + @Override + default org.apache.dubbo.rpc.Result get() throws InterruptedException, ExecutionException { + return this; + } + + class CompatibleResult implements Result { private org.apache.dubbo.rpc.Result delegate; @@ -53,11 +84,6 @@ public Object recreate() throws Throwable { return delegate.recreate(); } - @Override - public Object getResult() { - return delegate.getResult(); - } - @Override public Map getAttachments() { return delegate.getAttachments(); diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvoker.java b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvoker.java index ee1288affff..f77e0a523d0 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvoker.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvoker.java @@ -17,7 +17,7 @@ package org.apache.dubbo.filter; -import org.apache.dubbo.rpc.RpcResult; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.service.DemoService; import com.alibaba.dubbo.common.URL; @@ -58,7 +58,7 @@ public boolean isAvailable() { } public Result invoke(Invocation invocation) throws RpcException { - RpcResult result = new RpcResult(); + AppResponse result = new AppResponse(); if (hasException == false) { result.setValue("alibaba"); } else { diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java index 148b5bce5f5..73706a087ba 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java @@ -57,6 +57,16 @@ public Map getAttachments() { return attachments; } + @Override + public void setAttachment(String key, String value) { + + } + + @Override + public void setAttachmentIfAbsent(String key, String value) { + + } + public Invoker getInvoker() { return null; } diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java index 4305ed6c05f..db6b7559a76 100644 --- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java +++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java @@ -24,6 +24,8 @@ import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component("demoServiceComponent") public class DemoServiceComponent implements DemoService { @Reference @@ -33,4 +35,9 @@ public class DemoServiceComponent implements DemoService { public String sayHello(String name) { return demoService.sayHello(name); } + + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } } diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index cb06537ae01..137fa23ead6 100644 --- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -25,6 +25,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + @Service public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -35,4 +37,9 @@ public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } + } diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index 5e2ef2350d2..2f13a5c6714 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -22,6 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -31,4 +33,9 @@ public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } + } diff --git a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java index 1172c9be0fc..bd9c9287de7 100644 --- a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java +++ b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java @@ -16,8 +16,11 @@ */ package org.apache.dubbo.demo; +import java.util.concurrent.CompletableFuture; + public interface DemoService { String sayHello(String name); + CompletableFuture sayHelloAsync(String name); } \ No newline at end of file diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java index 90ed7c0b6a6..ad34442a436 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java @@ -20,16 +20,21 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; +import java.util.concurrent.CompletableFuture; + public class Application { /** * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before * launch the application */ - public static void main(String[] args) { + public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml"); context.start(); DemoService demoService = context.getBean("demoService", DemoService.class); - String hello = demoService.sayHello("world"); - System.out.println("result: " + hello); +// String hello = demoService.sayHello("world"); + CompletableFuture helloFuture = demoService.sayHelloAsync("world"); +// System.out.println("result: " + hello); + System.out.println("result: " + helloFuture.get()); + System.in.read(); } } diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml index 6b5efc32f00..286b24b3f15 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml @@ -27,6 +27,6 @@ - + diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index d0d315c9b4e..e95caa60444 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -22,6 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -31,4 +33,14 @@ public String sayHello(String name) { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return CompletableFuture.completedFuture("future return value!"); + } + } diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java index 4de9f5a170c..2f207b83b08 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java @@ -22,12 +22,12 @@ import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcResult; import java.io.Serializable; @@ -95,9 +95,9 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept Object value = cache.get(key); if (value != null) { if (value instanceof ValueWrapper) { - return new RpcResult(((ValueWrapper)value).get()); + return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation); } else { - return new RpcResult(value); + return AsyncRpcResult.newDefaultAsyncResult(value, invocation); } } Result result = invoker.invoke(invocation); diff --git a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java index c64647309f0..217919f1d6f 100644 --- a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java +++ b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java @@ -22,9 +22,10 @@ import org.apache.dubbo.cache.support.lru.LruCacheFactory; import org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; @@ -60,19 +61,19 @@ public void setUp(String cacheType, CacheFactory cacheFactory) { URL url = URL.valueOf("test://test:11/test?cache=" + cacheType); - given(invoker.invoke(invocation)).willReturn(new RpcResult("value")); + given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value", invocation)); given(invoker.getUrl()).willReturn(url); - given(invoker1.invoke(invocation)).willReturn(new RpcResult("value1")); + given(invoker1.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value1", invocation)); given(invoker1.getUrl()).willReturn(url); - given(invoker2.invoke(invocation)).willReturn(new RpcResult("value2")); + given(invoker2.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value2", invocation)); given(invoker2.getUrl()).willReturn(url); - given(invoker3.invoke(invocation)).willReturn(new RpcResult(new RuntimeException())); + given(invoker3.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(new RuntimeException(), invocation)); given(invoker3.getUrl()).willReturn(url); - given(invoker4.invoke(invocation)).willReturn(new RpcResult()); + given(invoker4.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(invocation)); given(invoker4.getUrl()).willReturn(url); } @@ -85,8 +86,8 @@ public void testNonArgsMethod(String cacheType, CacheFactory cacheFactory) { invocation.setArguments(new Object[]{}); cacheFilter.invoke(invoker, invocation); - RpcResult rpcResult1 = (RpcResult) cacheFilter.invoke(invoker1, invocation); - RpcResult rpcResult2 = (RpcResult) cacheFilter.invoke(invoker2, invocation); + Result rpcResult1 = cacheFilter.invoke(invoker1, invocation); + Result rpcResult2 = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue()); Assertions.assertEquals(rpcResult1.getValue(), "value"); } @@ -100,8 +101,8 @@ public void testMethodWithArgs(String cacheType, CacheFactory cacheFactory) { invocation.setArguments(new Object[]{"arg1"}); cacheFilter.invoke(invoker, invocation); - RpcResult rpcResult1 = (RpcResult) cacheFilter.invoke(invoker1, invocation); - RpcResult rpcResult2 = (RpcResult) cacheFilter.invoke(invoker2, invocation); + Result rpcResult1 = cacheFilter.invoke(invoker1, invocation); + Result rpcResult2 = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue()); Assertions.assertEquals(rpcResult1.getValue(), "value"); } @@ -115,7 +116,7 @@ public void testException(String cacheType, CacheFactory cacheFactory) { invocation.setArguments(new Object[]{"arg2"}); cacheFilter.invoke(invoker3, invocation); - RpcResult rpcResult = (RpcResult) cacheFilter.invoke(invoker2, invocation); + Result rpcResult = cacheFilter.invoke(invoker2, invocation); Assertions.assertEquals(rpcResult.getValue(), "value2"); } @@ -128,9 +129,9 @@ public void testNull(String cacheType, CacheFactory cacheFactory) { invocation.setArguments(new Object[]{"arg3"}); cacheFilter.invoke(invoker4, invocation); - RpcResult rpcResult1 = (RpcResult) cacheFilter.invoke(invoker1, invocation); - RpcResult rpcResult2 = (RpcResult) cacheFilter.invoke(invoker2, invocation); - Assertions.assertEquals(rpcResult1.getValue(), null); - Assertions.assertEquals(rpcResult2.getValue(), null); + Result result1 = cacheFilter.invoke(invoker1, invocation); + Result result2 = cacheFilter.invoke(invoker2, invocation); + Assertions.assertEquals(result1.getValue(), null); + Assertions.assertEquals(result2.getValue(), null); } } diff --git a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java index 2af761a0958..84950c395e1 100644 --- a/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java +++ b/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java @@ -19,12 +19,12 @@ import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; @@ -87,7 +87,7 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept } catch (RpcException e) { throw e; } catch (Throwable t) { - return new RpcResult(t); + return AsyncRpcResult.newDefaultAsyncResult(t, invocation); } } return invoker.invoke(invocation); diff --git a/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java b/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java index cade5936402..b757536e9da 100644 --- a/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java +++ b/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java @@ -17,11 +17,11 @@ package org.apache.dubbo.validation.filter; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.RpcResult; import org.apache.dubbo.validation.Validation; import org.apache.dubbo.validation.Validator; @@ -52,7 +52,7 @@ public void testItWithNotExistClass() throws Exception { URL url = URL.valueOf("test://test:11/test?default.validation=true"); given(validation.getValidator(url)).willThrow(new IllegalStateException("Not found class test, cause: test")); - given(invoker.invoke(invocation)).willReturn(new RpcResult("success")); + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[]{String.class}); @@ -70,7 +70,7 @@ public void testItWithExistClass() throws Exception { URL url = URL.valueOf("test://test:11/test?default.validation=true"); given(validation.getValidator(url)).willReturn(validator); - given(invoker.invoke(invocation)).willReturn(new RpcResult("success")); + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[]{String.class}); @@ -87,7 +87,7 @@ public void testItWithoutUrlParameters() throws Exception { URL url = URL.valueOf("test://test:11/test"); given(validation.getValidator(url)).willReturn(validator); - given(invoker.invoke(invocation)).willReturn(new RpcResult("success")); + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[]{String.class}); @@ -104,7 +104,7 @@ public void testItWhileMethodNameStartWithDollar() throws Exception { URL url = URL.valueOf("test://test:11/test"); given(validation.getValidator(url)).willReturn(validator); - given(invoker.invoke(invocation)).willReturn(new RpcResult("success")); + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("$echo1"); given(invocation.getParameterTypes()).willReturn(new Class[]{String.class}); @@ -124,7 +124,7 @@ public void testItWhileThrowoutRpcException() throws Exception { URL url = URL.valueOf("test://test:11/test?default.validation=true"); given(validation.getValidator(url)).willThrow(new RpcException("rpc exception")); - given(invoker.invoke(invocation)).willReturn(new RpcResult("success")); + given(invoker.invoke(invocation)).willReturn(new AppResponse("success")); given(invoker.getUrl()).willReturn(url); given(invocation.getMethodName()).willReturn("echo1"); given(invocation.getParameterTypes()).willReturn(new Class[]{String.class}); diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java index e6b90501264..06280ceaf25 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java +++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java @@ -86,4 +86,5 @@ public interface MonitorService { */ List lookup(URL query); + } \ No newline at end of file diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java index 10eb31e42fc..5358719e75e 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java +++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java @@ -25,9 +25,9 @@ import org.apache.dubbo.monitor.Monitor; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; -import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.ListenableFilter; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; @@ -41,10 +41,14 @@ * MonitorFilter. (SPI, Singleton, ThreadSafe) */ @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}) -public class MonitorFilter implements Filter { +public class MonitorFilter extends ListenableFilter { private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class); + private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time"; + public MonitorFilter() { + super.listener = new MonitorListener(); + } /** * The Concurrent counter */ @@ -59,6 +63,7 @@ public void setMonitorFactory(MonitorFactory monitorFactory) { this.monitorFactory = monitorFactory; } + /** * The invocation interceptor,it will collect the invoke data about this invocation and send it to monitor center * @@ -70,105 +75,10 @@ public void setMonitorFactory(MonitorFactory monitorFactory) { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) { - RpcContext context = RpcContext.getContext(); // provider must fetch context before invoke() gets called - String remoteHost = context.getRemoteHost(); - long start = System.currentTimeMillis(); // record start timestamp + invocation.setAttachment(MONITOR_FILTER_START_TIME, String.valueOf(System.currentTimeMillis())); getConcurrent(invoker, invocation).incrementAndGet(); // count up - try { - Result result = invoker.invoke(invocation); // proceed invocation chain - collect(invoker, invocation, result, remoteHost, start, false); - return result; - } catch (RpcException e) { - collect(invoker, invocation, null, remoteHost, start, true); - throw e; - } finally { - getConcurrent(invoker, invocation).decrementAndGet(); // count down - } - } else { - return invoker.invoke(invocation); - } - } - - /** - * The collector logic, it will be handled by the default monitor - * - * @param invoker - * @param invocation - * @param result the invoke result - * @param remoteHost the remote host address - * @param start the timestamp the invoke begin - * @param error if there is an error on the invoke - */ - private void collect(Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { - try { - URL monitorUrl = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY); - Monitor monitor = monitorFactory.getMonitor(monitorUrl); - if (monitor == null) { - return; - } - URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error); - monitor.collect(statisticsURL); - } catch (Throwable t) { - logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t); } - } - - /** - * Create statistics url - * - * @param invoker - * @param invocation - * @param result - * @param remoteHost - * @param start - * @param error - * @return - */ - private URL createStatisticsUrl(Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { - // ---- service statistics ---- - long elapsed = System.currentTimeMillis() - start; // invocation cost - int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count - String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY); - String service = invoker.getInterface().getName(); // service name - String method = RpcUtils.getMethodName(invocation); // method name - String group = invoker.getUrl().getParameter(Constants.GROUP_KEY); - String version = invoker.getUrl().getParameter(Constants.VERSION_KEY); - - int localPort; - String remoteKey, remoteValue; - if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) { - // ---- for service consumer ---- - localPort = 0; - remoteKey = MonitorService.PROVIDER; - remoteValue = invoker.getUrl().getAddress(); - } else { - // ---- for service provider ---- - localPort = invoker.getUrl().getPort(); - remoteKey = MonitorService.CONSUMER; - remoteValue = remoteHost; - } - String input = "", output = ""; - if (invocation.getAttachment(Constants.INPUT_KEY) != null) { - input = invocation.getAttachment(Constants.INPUT_KEY); - } - if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) { - output = result.getAttachment(Constants.OUTPUT_KEY); - } - - return new URL(Constants.COUNT_PROTOCOL, - NetUtils.getLocalHost(), localPort, - service + Constants.PATH_SEPARATOR + method, - MonitorService.APPLICATION, application, - MonitorService.INTERFACE, service, - MonitorService.METHOD, method, - remoteKey, remoteValue, - error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1", - MonitorService.ELAPSED, String.valueOf(elapsed), - MonitorService.CONCURRENT, String.valueOf(concurrent), - Constants.INPUT_KEY, input, - Constants.OUTPUT_KEY, output, - Constants.GROUP_KEY, group, - Constants.VERSION_KEY, version); + return invoker.invoke(invocation); // proceed invocation chain } // concurrent counter @@ -182,4 +92,93 @@ private AtomicInteger getConcurrent(Invoker invoker, Invocation invocation) { return concurrent; } + class MonitorListener implements Listener { + + @Override + public void onResponse(Result result, Invoker invoker, Invocation invocation) { + if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) { + collect(invoker, invocation, result, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), false); + getConcurrent(invoker, invocation).decrementAndGet(); // count down + } + } + + @Override + public void onError(Throwable t, Invoker invoker, Invocation invocation) { + if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) { + collect(invoker, invocation, null, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), true); + getConcurrent(invoker, invocation).decrementAndGet(); // count down + } + } + + /** + * The collector logic, it will be handled by the default monitor + * + * @param invoker + * @param invocation + * @param result the invoke result + * @param remoteHost the remote host address + * @param start the timestamp the invoke begin + * @param error if there is an error on the invoke + */ + private void collect(Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { + try { + URL monitorUrl = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY); + Monitor monitor = monitorFactory.getMonitor(monitorUrl); + if (monitor == null) { + return; + } + URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error); + monitor.collect(statisticsURL); + } catch (Throwable t) { + logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t); + } + } + + /** + * Create statistics url + * + * @param invoker + * @param invocation + * @param result + * @param remoteHost + * @param start + * @param error + * @return + */ + private URL createStatisticsUrl(Invoker invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { + // ---- service statistics ---- + long elapsed = System.currentTimeMillis() - start; // invocation cost + int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count + String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY); + String service = invoker.getInterface().getName(); // service name + String method = RpcUtils.getMethodName(invocation); // method name + String group = invoker.getUrl().getParameter(Constants.GROUP_KEY); + String version = invoker.getUrl().getParameter(Constants.VERSION_KEY); + + int localPort; + String remoteKey, remoteValue; + if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) { + // ---- for service consumer ---- + localPort = 0; + remoteKey = MonitorService.PROVIDER; + remoteValue = invoker.getUrl().getAddress(); + } else { + // ---- for service provider ---- + localPort = invoker.getUrl().getPort(); + remoteKey = MonitorService.CONSUMER; + remoteValue = remoteHost; + } + String input = "", output = ""; + if (invocation.getAttachment(Constants.INPUT_KEY) != null) { + input = invocation.getAttachment(Constants.INPUT_KEY); + } + if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) { + output = result.getAttachment(Constants.OUTPUT_KEY); + } + + return new URL(Constants.COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + Constants.PATH_SEPARATOR + method, MonitorService.APPLICATION, application, MonitorService.INTERFACE, service, MonitorService.METHOD, method, remoteKey, remoteValue, error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1", MonitorService.ELAPSED, String.valueOf(elapsed), MonitorService.CONCURRENT, String.valueOf(concurrent), Constants.INPUT_KEY, input, Constants.OUTPUT_KEY, output, Constants.GROUP_KEY, group, Constants.VERSION_KEY, version); + } + + } + } diff --git a/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java b/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java index 01ba57dd7b8..730ab28e483 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java +++ b/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java @@ -22,12 +22,14 @@ import org.apache.dubbo.monitor.Monitor; import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -73,7 +75,7 @@ public boolean isAvailable() { public Result invoke(Invocation invocation) throws RpcException { lastInvocation = invocation; - return null; + return AsyncRpcResult.newDefaultAsyncResult(invocation); } @Override @@ -115,7 +117,11 @@ public void testFilter() throws Exception { monitorFilter.setMonitorFactory(monitorFactory); Invocation invocation = new RpcInvocation("aaa", new Class[0], new Object[0]); RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); - monitorFilter.invoke(serviceInvoker, invocation); + Result result = monitorFilter.invoke(serviceInvoker, invocation); + result.thenApplyWithContext((r) -> { + monitorFilter.listener().onResponse(r, serviceInvoker, invocation); + return r; + }); while (lastStatistics == null) { Thread.sleep(10); } @@ -151,7 +157,11 @@ public void testGenericFilter() throws Exception { monitorFilter.setMonitorFactory(monitorFactory); Invocation invocation = new RpcInvocation("$invoke", new Class[]{String.class, String[].class, Object[].class}, new Object[]{"xxx", new String[]{}, new Object[]{}}); RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); - monitorFilter.invoke(serviceInvoker, invocation); + Result result = monitorFilter.invoke(serviceInvoker, invocation); + result.thenApplyWithContext((r) -> { + monitorFilter.listener().onResponse(r, serviceInvoker, invocation); + return r; + }); while (lastStatistics == null) { Thread.sleep(10); } diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java index fa061b99696..7a4961bf954 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java @@ -21,9 +21,9 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeHandler; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; public class MockChannel implements ExchangeChannel { @@ -76,7 +76,7 @@ public URL getUrl() { return null; } - public ResponseFuture send(Object request, int timeout) throws RemotingException { + public CompletableFuture send(Object request, int timeout) throws RemotingException { return null; } @@ -85,11 +85,11 @@ public ChannelHandler getChannelHandler() { return null; } - public ResponseFuture request(Object request) throws RemotingException { + public CompletableFuture request(Object request) throws RemotingException { return null; } - public ResponseFuture request(Object request, int timeout) throws RemotingException { + public CompletableFuture request(Object request, int timeout) throws RemotingException { return null; } diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java index f1341ef71f8..1afe0d550c8 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java @@ -23,12 +23,13 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.remoting.exchange.ExchangeHandler; -import org.apache.dubbo.remoting.exchange.ResponseCallback; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.Replier; import java.net.InetSocketAddress; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; /** * MockedClient @@ -79,27 +80,24 @@ public void send(Object msg) throws RemotingException { this.sent = msg; } - public ResponseFuture request(Object msg) throws RemotingException { + public CompletableFuture request(Object msg) throws RemotingException { return request(msg, 0); } - public ResponseFuture request(Object msg, int timeout) throws RemotingException { + public CompletableFuture request(Object msg, int timeout) throws RemotingException { this.invoked = msg; - return new ResponseFuture() { - public Object get() throws RemotingException { + return new CompletableFuture() { + public Object get() throws InterruptedException, ExecutionException { return received; } - public Object get(int timeoutInMillis) throws RemotingException { + public Object get(int timeoutInMillis) throws InterruptedException, ExecutionException, TimeoutException { return received; } public boolean isDone() { return true; } - - public void setCallback(ResponseCallback callback) { - } }; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingException.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingException.java index fb288bb9643..1bbd4c25473 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingException.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/RemotingException.java @@ -22,8 +22,7 @@ * RemotingException. (API, Prototype, ThreadSafe) * * @export - * @see org.apache.dubbo.remoting.exchange.ResponseFuture#get() - * @see org.apache.dubbo.remoting.exchange.ResponseFuture#get(int) + * @see org.apache.dubbo.remoting.exchange.support.DefaultFuture#get() * @see org.apache.dubbo.remoting.Channel#send(Object, boolean) * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object) * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object, int) diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/TimeoutException.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/TimeoutException.java index 464452e1afe..a14371645fa 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/TimeoutException.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/TimeoutException.java @@ -22,8 +22,7 @@ * TimeoutException. (API, Prototype, ThreadSafe) * * @export - * @see org.apache.dubbo.remoting.exchange.ResponseFuture#get() - * @see org.apache.dubbo.remoting.exchange.ResponseFuture#get(int) + * @see org.apache.dubbo.remoting.exchange.support.DefaultFuture#get() */ public class TimeoutException extends RemotingException { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java index 3922626fdfb..0e4917d200f 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java @@ -19,6 +19,8 @@ import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; +import java.util.concurrent.CompletableFuture; + /** * ExchangeChannel. (API/SPI, Prototype, ThreadSafe) */ @@ -31,7 +33,7 @@ public interface ExchangeChannel extends Channel { * @return response future * @throws RemotingException */ - ResponseFuture request(Object request) throws RemotingException; + CompletableFuture request(Object request) throws RemotingException; /** * send request. @@ -41,7 +43,7 @@ public interface ExchangeChannel extends Channel { * @return response future * @throws RemotingException */ - ResponseFuture request(Object request, int timeout) throws RemotingException; + CompletableFuture request(Object request, int timeout) throws RemotingException; /** * get message handler. diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java index 568ecf1160b..b0453d61352 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java @@ -16,6 +16,16 @@ */ package org.apache.dubbo.remoting.exchange; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Decodeable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + /** * Response */ @@ -173,4 +183,55 @@ public String toString() { return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent + ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]"; } + + public static class AppResult { + + protected Map attachments = new HashMap(); + + protected Object result; + + protected Throwable exception; + + public Map getAttachments() { + return attachments; + } + + public void setAttachments(Map attachments) { + this.attachments = attachments; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + + public Throwable getException() { + return exception; + } + + public void setException(Throwable exception) { + this.exception = exception; + } + } + + public static class DecodeableAppResult implements Codec, Decodeable { + + @Override + public void decode() throws Exception { + + } + + @Override + public void encode(Channel channel, OutputStream output, Object message) throws IOException { + + } + + @Override + public Object decode(Channel channel, InputStream input) throws IOException { + return null; + } + } } \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseFuture.java deleted file mode 100644 index 9e641dd3579..00000000000 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseFuture.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.remoting.exchange; - -import org.apache.dubbo.remoting.RemotingException; - -/** - * Future. (API/SPI, Prototype, ThreadSafe) - * - * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object) - * @see org.apache.dubbo.remoting.exchange.ExchangeChannel#request(Object, int) - */ -public interface ResponseFuture { - - /** - * get result. - * - * @return result. - */ - Object get() throws RemotingException; - - /** - * get result with the specified timeout. - * - * @param timeoutInMillis timeout. - * @return result. - */ - Object get(int timeoutInMillis) throws RemotingException; - - /** - * set callback. - * - * @param callback - */ - void setCallback(ResponseCallback callback); - - /** - * check is done. - * - * @return done or not. - */ - boolean isDone(); - -} \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java index 290d668241a..3324ad994ac 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java @@ -29,22 +29,18 @@ import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; -import org.apache.dubbo.remoting.exchange.ResponseCallback; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * DefaultFuture. */ -public class DefaultFuture implements ResponseFuture { +public class DefaultFuture extends CompletableFuture { private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class); @@ -62,12 +58,8 @@ public class DefaultFuture implements ResponseFuture { private final Channel channel; private final Request request; private final int timeout; - private final Lock lock = new ReentrantLock(); - private final Condition done = lock.newCondition(); private final long start = System.currentTimeMillis(); private volatile long sent; - private volatile Response response; - private volatile ResponseCallback callback; private DefaultFuture(Channel channel, Request request, int timeout) { this.channel = channel; @@ -79,14 +71,6 @@ private DefaultFuture(Channel channel, Request request, int timeout) { CHANNELS.put(id, channel); } - /** - * check time out of the future - */ - private static void timeoutCheck(DefaultFuture future) { - TimeoutCheckTask task = new TimeoutCheckTask(future); - TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS); - } - /** * init a DefaultFuture * 1.init a DefaultFuture @@ -160,141 +144,32 @@ public static void received(Channel channel, Response response) { } @Override - public Object get() throws RemotingException { - return get(timeout); - } - - @Override - public Object get(int timeout) throws RemotingException { - if (timeout <= 0) { - timeout = Constants.DEFAULT_TIMEOUT; - } - if (!isDone()) { - long start = System.currentTimeMillis(); - lock.lock(); - try { - while (!isDone()) { - done.await(timeout, TimeUnit.MILLISECONDS); - if (isDone() || System.currentTimeMillis() - start > timeout) { - break; - } - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } finally { - lock.unlock(); - } - if (!isDone()) { - throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false)); - } - } - return returnFromResponse(); - } - - public void cancel() { + public boolean cancel(boolean mayInterruptIfRunning) { Response errorResult = new Response(id); + errorResult.setStatus(Response.CLIENT_ERROR); errorResult.setErrorMessage("request future has been canceled."); - response = errorResult; + this.doReceived(errorResult); FUTURES.remove(id); CHANNELS.remove(id); + return true; } - @Override - public boolean isDone() { - return response != null; - } - - @Override - public void setCallback(ResponseCallback callback) { - if (isDone()) { - invokeCallback(callback); - } else { - boolean isdone = false; - lock.lock(); - try { - if (!isDone()) { - this.callback = callback; - } else { - isdone = true; - } - } finally { - lock.unlock(); - } - if (isdone) { - invokeCallback(callback); - } - } + public void cancel() { + this.cancel(true); } - private static class TimeoutCheckTask implements TimerTask { - - private DefaultFuture future; - - TimeoutCheckTask(DefaultFuture future) { - this.future = future; - } - - @Override - public void run(Timeout timeout) { - if (future == null || future.isDone()) { - return; - } - // create exception response. - Response timeoutResponse = new Response(future.getId()); - // set timeout status. - timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT); - timeoutResponse.setErrorMessage(future.getTimeoutMessage(true)); - // handle response. - DefaultFuture.received(future.getChannel(), timeoutResponse); - - } - } - private void invokeCallback(ResponseCallback c) { - ResponseCallback callbackCopy = c; - if (callbackCopy == null) { - throw new NullPointerException("callback cannot be null."); - } - Response res = response; + private void doReceived(Response res) { if (res == null) { - throw new IllegalStateException("response cannot be null. url:" + channel.getUrl()); + throw new IllegalStateException("response cannot be null"); } - if (res.getStatus() == Response.OK) { - try { - callbackCopy.done(res.getResult()); - } catch (Exception e) { - logger.error("callback invoke error .result:" + res.getResult() + ",url:" + channel.getUrl(), e); - } + this.complete(res.getResult()); } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) { - try { - TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()); - callbackCopy.caught(te); - } catch (Exception e) { - logger.error("callback invoke error ,url:" + channel.getUrl(), e); - } + this.completeExceptionally(new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage())); } else { - try { - RuntimeException re = new RuntimeException(res.getErrorMessage()); - callbackCopy.caught(re); - } catch (Exception e) { - logger.error("callback invoke error ,url:" + channel.getUrl(), e); - } - } - } - - private Object returnFromResponse() throws RemotingException { - Response res = response; - if (res == null) { - throw new IllegalStateException("response cannot be null"); - } - if (res.getStatus() == Response.OK) { - return res.getResult(); - } - if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) { - throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()); + this.completeExceptionally(new RemotingException(channel, res.getErrorMessage())); } - throw new RemotingException(channel, res.getErrorMessage()); } private long getId() { @@ -317,25 +192,16 @@ private int getTimeout() { return timeout; } - private long getStartTimestamp() { - return start; - } - private void doSent() { sent = System.currentTimeMillis(); } - private void doReceived(Response res) { - lock.lock(); - try { - response = res; - done.signalAll(); - } finally { - lock.unlock(); - } - if (callback != null) { - invokeCallback(callback); - } + /** + * check time out of the future + */ + private static void timeoutCheck(DefaultFuture future) { + TimeoutCheckTask task = new TimeoutCheckTask(future); + TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS); } private String getTimeoutMessage(boolean scan) { @@ -350,4 +216,28 @@ private String getTimeoutMessage(boolean scan) { + timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress(); } + + private static class TimeoutCheckTask implements TimerTask { + + private DefaultFuture future; + + TimeoutCheckTask(DefaultFuture future) { + this.future = future; + } + + @Override + public void run(Timeout timeout) { + if (future == null || future.isDone()) { + return; + } + // create exception response. + Response timeoutResponse = new Response(future.getId()); + // set timeout status. + timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT); + timeoutResponse.setErrorMessage(future.getTimeoutMessage(true)); + // handle response. + DefaultFuture.received(future.getChannel(), timeoutResponse); + + } + } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/SimpleFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/SimpleFuture.java deleted file mode 100644 index 95b95f4ce2f..00000000000 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/SimpleFuture.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.remoting.exchange.support; - -import org.apache.dubbo.remoting.RemotingException; -import org.apache.dubbo.remoting.exchange.ResponseCallback; -import org.apache.dubbo.remoting.exchange.ResponseFuture; - -/** - * SimpleFuture - */ -public class SimpleFuture implements ResponseFuture { - - private final Object value; - - public SimpleFuture(Object value) { - this.value = value; - } - - @Override - public Object get() throws RemotingException { - return value; - } - - @Override - public Object get(int timeoutInMillis) throws RemotingException { - return value; - } - - @Override - public void setCallback(ResponseCallback callback) { - callback.done(value); - } - - @Override - public boolean isDone() { - return true; - } - -} diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java index 4aef993a2e2..1666bbf38e7 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java @@ -28,10 +28,10 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; /** * ExchangeReceiver @@ -97,12 +97,12 @@ public void send(Object message, boolean sent) throws RemotingException { } @Override - public ResponseFuture request(Object request) throws RemotingException { + public CompletableFuture request(Object request) throws RemotingException { return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT)); } @Override - public ResponseFuture request(Object request, int timeout) throws RemotingException { + public CompletableFuture request(Object request, int timeout) throws RemotingException { if (closed) { throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java index 3e57fba90c4..5b99079e0e1 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java @@ -27,10 +27,10 @@ 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.ResponseFuture; import java.net.InetSocketAddress; import java.util.Collections; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.common.utils.UrlUtils.getHeartbeat; @@ -62,7 +62,7 @@ public HeaderExchangeClient(Client client, boolean startTimer) { } @Override - public ResponseFuture request(Object request) throws RemotingException { + public CompletableFuture request(Object request) throws RemotingException { return channel.request(request); } @@ -77,7 +77,7 @@ public InetSocketAddress getRemoteAddress() { } @Override - public ResponseFuture request(Object request, int timeout) throws RemotingException { + public CompletableFuture request(Object request, int timeout) throws RemotingException { return channel.request(request, timeout); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java index 6f342f997c0..d0f62fe6cb0 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java @@ -34,7 +34,7 @@ import org.apache.dubbo.remoting.transport.ChannelHandlerDelegate; import java.net.InetSocketAddress; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; /** @@ -99,19 +99,12 @@ void handleRequest(final ExchangeChannel channel, Request req) throws RemotingEx // find handler by message class. Object msg = req.getData(); try { - // handle data. - CompletableFuture future = handler.reply(channel, msg); - if (future.isDone()) { - res.setStatus(Response.OK); - res.setResult(future.get()); - channel.send(res); - return; - } - future.whenComplete((result, t) -> { + CompletionStage future = handler.reply(channel, msg); + future.whenComplete((appResult, t) -> { try { if (t == null) { res.setStatus(Response.OK); - res.setResult(result); + res.setResult(appResult); } else { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(t)); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/Main.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/Main.java index 566bda10d8e..4478f86ecfd 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/Main.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/Main.java @@ -19,12 +19,12 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.Exchangers; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.Replier; import org.apache.dubbo.remoting.exchange.support.ReplierDispatcher; import java.io.Serializable; import java.util.Random; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -94,7 +94,7 @@ private static void test(int port) throws Exception { System.out.println("=====test invoke====="); for (int i = 0; i < 100; i++) { - ResponseFuture future = client.request(new Main.Data()); + CompletableFuture future = client.request(new Main.Data()); System.out.println("invoke and get"); System.out.println("invoke result:" + future.get()); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java index 413aa05b657..1989d3715c5 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java @@ -25,7 +25,6 @@ import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter; import org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java index 1ab41243dd4..2dd400545a4 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java @@ -74,7 +74,7 @@ public void timeoutNotSend() throws Exception { try { f.get(); } catch (Exception e) { - Assertions.assertTrue(e instanceof TimeoutException, "catch exception is not timeout exception!"); + Assertions.assertTrue(e.getCause() instanceof TimeoutException, "catch exception is not timeout exception!"); System.out.println(e.getMessage()); } } @@ -108,7 +108,7 @@ public void timeoutSend() throws Exception { try { f.get(); } catch (Exception e) { - Assertions.assertTrue(e instanceof TimeoutException, "catch exception is not timeout exception!"); + Assertions.assertTrue(e.getCause() instanceof TimeoutException, "catch exception is not timeout exception!"); System.out.println(e.getMessage()); } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java index 3c11eca1c45..d08c8e4285d 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java @@ -178,7 +178,7 @@ public void send(Object message) throws RemotingException { HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler() { @Override - public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { + public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException { Assertions.fail(); throw new RemotingException(channel, ""); } diff --git a/dubbo-remoting/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/ClientToServerTest.java b/dubbo-remoting/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/ClientToServerTest.java index 987139275d5..6414434f51d 100644 --- a/dubbo-remoting/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/ClientToServerTest.java +++ b/dubbo-remoting/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/ClientToServerTest.java @@ -19,7 +19,6 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.Replier; import org.junit.jupiter.api.AfterEach; @@ -27,6 +26,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.CompletableFuture; + /** * ClientToServer */ @@ -64,7 +65,7 @@ protected void tearDown() { @Test public void testFuture() throws Exception { - ResponseFuture future = client.request(new World("world")); + CompletableFuture future = client.request(new World("world")); Hello result = (Hello) future.get(); Assertions.assertEquals("hello,world", result.getName()); } diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java index 52c3e9d03e7..da93c18c646 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java @@ -29,6 +29,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeServer; import org.apache.dubbo.remoting.exchange.Exchangers; import org.apache.dubbo.remoting.transport.dispatcher.FakeChannelHandlers; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientToServerTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientToServerTest.java index 03466bed688..267f5694065 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientToServerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientToServerTest.java @@ -19,14 +19,14 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.Replier; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.CompletableFuture; + /** * ClientToServer */ @@ -64,7 +64,7 @@ protected void tearDown() throws Exception { @Test public void testFuture() throws Exception { - ResponseFuture future = client.request(new World("world")); + CompletableFuture future = client.request(new World("world")); Hello result = (Hello) future.get(); Assertions.assertEquals("hello,world", result.getName()); } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java index 9d2311fa5ff..9b8db0027c2 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java @@ -19,7 +19,6 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.ExchangeChannel; import org.apache.dubbo.remoting.exchange.ExchangeServer; -import org.apache.dubbo.remoting.exchange.ResponseFuture; import org.apache.dubbo.remoting.exchange.support.Replier; import org.junit.jupiter.api.AfterEach; @@ -27,6 +26,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.CompletableFuture; + /** * ClientToServer */ @@ -64,7 +65,7 @@ protected void tearDown() { @Test public void testFuture() throws Exception { - ResponseFuture future = client.request(new World("world")); + CompletableFuture future = client.request(new World("world")); Hello result = (Hello) future.get(); Assertions.assertEquals("hello,world", result.getName()); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AbstractResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AbstractResult.java deleted file mode 100644 index b898934c63c..00000000000 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AbstractResult.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc; - -import org.apache.dubbo.common.utils.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -/** - * - */ -public abstract class AbstractResult implements Result { - protected Map attachments = new HashMap(); - - protected Object result; - - protected Throwable exception; - - @Override - public Map getAttachments() { - return attachments; - } - - @Override - public void setAttachments(Map map) { - this.attachments = map == null ? new HashMap() : map; - } - - @Override - public void addAttachments(Map map) { - if (map == null) { - return; - } - if (this.attachments == null) { - this.attachments = new HashMap(); - } - this.attachments.putAll(map); - } - - @Override - public String getAttachment(String key) { - return attachments.get(key); - } - - @Override - public String getAttachment(String key, String defaultValue) { - String result = attachments.get(key); - if (StringUtils.isEmpty(result)) { - result = defaultValue; - } - return result; - } - - @Override - public void setAttachment(String key, String value) { - attachments.put(key, value); - } - -} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java new file mode 100644 index 00000000000..6ede9d4ee5b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java @@ -0,0 +1,160 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; + +/** + * {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}: + *
    + *
  • AsyncRpcResult is the object that is actually passed in the call chain
  • + *
  • AppResponse only simply represents the business result
  • + *
+ * + * The relationship between them can be reflected in the definition of AsyncRpcResult: + *
+ *  {@code
+ *   Public class AsyncRpcResult implements Result {
+ *      private CompletableFuture  resultFuture;
+ *       ......
+ *   }
+ *  }
+ * 
+ * + * In theory, AppResponse does not need to implement the {@link Result} interface, this is done mainly for compatibility purpose. + * + * @serial Do not change the class name and properties. + */ +public class AppResponse implements Result, Serializable { + + private static final long serialVersionUID = -6925924956850004727L; + + private Object result; + + private Throwable exception; + + private Map attachments = new HashMap(); + + public AppResponse() { + } + + public AppResponse(Object result) { + this.result = result; + } + + public AppResponse(Throwable exception) { + this.exception = exception; + } + + @Override + public Object recreate() throws Throwable { + if (exception != null) { + throw exception; + } + return result; + } + + @Override + public Object getValue() { + return result; + } + + public void setValue(Object value) { + this.result = value; + } + + @Override + public Throwable getException() { + return exception; + } + + public void setException(Throwable e) { + this.exception = e; + } + + @Override + public boolean hasException() { + return exception != null; + } + + @Override + public Map getAttachments() { + return attachments; + } + + /** + * Append all items from the map into the attachment, if map is empty then nothing happens + * + * @param map contains all key-value pairs to append + */ + public void setAttachments(Map map) { + this.attachments = map == null ? new HashMap() : map; + } + + public void addAttachments(Map map) { + if (map == null) { + return; + } + if (this.attachments == null) { + this.attachments = new HashMap(); + } + this.attachments.putAll(map); + } + + @Override + public String getAttachment(String key) { + return attachments.get(key); + } + + @Override + public String getAttachment(String key, String defaultValue) { + String result = attachments.get(key); + if (result == null || result.length() == 0) { + result = defaultValue; + } + return result; + } + + public void setAttachment(String key, String value) { + attachments.put(key, value); + } + + @Override + public Result thenApplyWithContext(Function fn) { + throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly."); + } + + @Override + public CompletableFuture thenApply(Function fn) { + throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly."); + } + + @Override + public Result get() throws InterruptedException, ExecutionException { + throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly."); + } + + @Override + public String toString() { + return "AppResponse [value=" + result + ", exception=" + exception + "]"; + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java index b631dffe180..e48b7130159 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java @@ -22,96 +22,27 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.function.Function; -/** - * NOTICE!! - * - *

- * You should never rely on this class directly when using or extending Dubbo, the implementation of {@link AsyncRpcResult} - * is only a workaround for compatibility purpose. It may be changed or even get removed from the next major version. - * Please only use {@link Result} or {@link RpcResult}. - * - * Extending the {@link Filter} is one typical use case: - *

- * {@code
- * public class YourFilter implements Filter {
- *     @Override
- *     public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
- *         System.out.println("Filter get the return value: " + result.getValue());
- *         // Don't do this
- *         // AsyncRpcResult asyncRpcResult = ((AsyncRpcResult)result;
- *         // System.out.println("Filter get the return value: " + asyncRpcResult.getValue());
- *         return result;
- *     }
- *
- *     @Override
- *     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
- *         return invoker.invoke(invocation);
- *     }
- * }
- * }
- * 
- *

- * TODO RpcResult can be an instance of {@link java.util.concurrent.CompletionStage} instead of composing CompletionStage inside. - */ -public class AsyncRpcResult extends AbstractResult { +public class AsyncRpcResult implements Result { private static final Logger logger = LoggerFactory.getLogger(AsyncRpcResult.class); /** - * RpcContext can be changed, because thread may have been used by other thread. It should be cloned before store. - * So we use Invocation instead, Invocation will create for every invoke, but invocation only support attachments of string type. + * RpcContext may already have been changed when callback happens, it happens when the same thread is used to execute another RPC call. + * So we should keep the reference of current RpcContext instance and restore it before callback being executed. */ private RpcContext storedContext; private RpcContext storedServerContext; - protected CompletableFuture valueFuture; + private CompletableFuture responseFuture; + private Invocation invocation; - protected CompletableFuture resultFuture; - - public AsyncRpcResult(CompletableFuture future) { - this(future, true); - } - - public AsyncRpcResult(CompletableFuture future, boolean registerCallback) { - this(future, new CompletableFuture<>(), registerCallback); - } - - /** - * @param future - * @param rFuture - * @param registerCallback - */ - public AsyncRpcResult(CompletableFuture future, final CompletableFuture rFuture, boolean registerCallback) { - if (rFuture == null) { - throw new IllegalArgumentException(); - } - resultFuture = rFuture; - if (registerCallback) { - /** - * We do not know whether future already completed or not, it's a future exposed or even created by end user. - * 1. future complete before whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread subscribing, in our case, it's Dubbo thread. - * 2. future complete after whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread calling complete, normally its User thread. - */ - future.whenComplete((v, t) -> { - RpcResult rpcResult; - if (t != null) { - if (t instanceof CompletionException) { - rpcResult = new RpcResult(t.getCause()); - } else { - rpcResult = new RpcResult(t); - } - } else { - rpcResult = new RpcResult(v); - } - // instead of resultFuture we must use rFuture here, resultFuture may being changed before complete when building filter chain, but rFuture was guaranteed never changed by closure. - rFuture.complete(rpcResult); - }); - } - this.valueFuture = future; - // employ copy of context avoid the other call may modify the context content - this.storedContext = RpcContext.getContext().copyOf(); - this.storedServerContext = RpcContext.getServerContext().copyOf(); + public AsyncRpcResult(CompletableFuture future, Invocation invocation) { + this.responseFuture = future; + this.invocation = invocation; + this.storedContext = RpcContext.getContext(); + this.storedServerContext = RpcContext.getServerContext(); } @Override @@ -120,51 +51,86 @@ public Object getValue() { } @Override - public Throwable getException() { - return getRpcResult().getException(); + public void setValue(Object value) { + } @Override - public boolean hasException() { - return getRpcResult().hasException(); + public Throwable getException() { + return getRpcResult().getException(); } @Override - public Object getResult() { - return getRpcResult().getResult(); + public void setException(Throwable t) { + } - public CompletableFuture getValueFuture() { - return valueFuture; + @Override + public boolean hasException() { + return getRpcResult().hasException(); } - public CompletableFuture getResultFuture() { - return resultFuture; + public CompletableFuture getResponseFuture() { + return responseFuture; } - public void setResultFuture(CompletableFuture resultFuture) { - this.resultFuture = resultFuture; + public void setResponseFuture(CompletableFuture responseFuture) { + this.responseFuture = responseFuture; } public Result getRpcResult() { try { - if (resultFuture.isDone()) { - return resultFuture.get(); + if (responseFuture.isDone()) { + return responseFuture.get(); } } catch (Exception e) { // This should never happen; logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e); } - return new RpcResult(); + return new AppResponse(); } @Override public Object recreate() throws Throwable { - return valueFuture; + RpcInvocation rpcInvocation = (RpcInvocation) invocation; + if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) { + AppResponse rpcResult = new AppResponse(); + CompletableFuture future = new CompletableFuture<>(); + rpcResult.setValue(future); + responseFuture.whenComplete((result, t) -> { + if (t != null) { + if (t instanceof CompletionException) { + t = t.getCause(); + } + future.completeExceptionally(t); + } else { + if (result.hasException()) { + future.completeExceptionally(result.getException()); + } else { + future.complete(result.getValue()); + } + } + }); + return rpcResult.recreate(); + } else if (responseFuture.isDone()) { + return responseFuture.get().recreate(); + } + return (new AppResponse()).recreate(); } - public void thenApplyWithContext(Function fn) { - this.resultFuture = resultFuture.thenApply(fn.compose(beforeContext).andThen(afterContext)); + public Result get() throws InterruptedException, ExecutionException { + return responseFuture.get(); + } + + @Override + public Result thenApplyWithContext(Function fn) { + this.responseFuture = responseFuture.thenApply(fn.compose(beforeContext).andThen(afterContext)); + return this; + } + + @Override + public CompletableFuture thenApply(Function fn) { + return this.responseFuture.thenApply(fn); } @Override @@ -203,18 +169,49 @@ public void setAttachment(String key, String value) { private RpcContext tmpContext; private RpcContext tmpServerContext; - private Function beforeContext = (result) -> { + private Function beforeContext = (appResponse) -> { tmpContext = RpcContext.getContext(); tmpServerContext = RpcContext.getServerContext(); RpcContext.restoreContext(storedContext); RpcContext.restoreServerContext(storedServerContext); - return result; + return appResponse; }; - private Function afterContext = (result) -> { + private Function afterContext = (appResponse) -> { RpcContext.restoreContext(tmpContext); RpcContext.restoreServerContext(tmpServerContext); - return result; + return appResponse; }; + + /** + * Some utility methods used to quickly generate default AsyncRpcResult instance. + */ + public static AsyncRpcResult newDefaultAsyncResult(AppResponse result, Invocation invocation) { + return new AsyncRpcResult(CompletableFuture.completedFuture(result), invocation); + } + + public static AsyncRpcResult newDefaultAsyncResult(Invocation invocation) { + return newDefaultAsyncResult(null, null, invocation); + } + + public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) { + return newDefaultAsyncResult(value, null, invocation); + } + + public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invocation) { + return newDefaultAsyncResult(null, t, invocation); + } + + public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) { + CompletableFuture future = new CompletableFuture<>(); + AppResponse result = new AppResponse(); + if (t != null) { + result.setException(t); + } else { + result.setValue(value); + } + future.complete(result); + return new AsyncRpcResult(future, invocation); + } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java index a17fd906d55..7c634481d7e 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Filter.java @@ -42,35 +42,16 @@ */ @SPI public interface Filter { - /** - * do invoke filter. - *

- * - * // before filter - * Result result = invoker.invoke(invocation); - * // after filter - * return result; - * - * - * @param invoker service - * @param invocation invocation. - * @return invoke result. - * @throws RpcException - * @see org.apache.dubbo.rpc.Invoker#invoke(Invocation) + * Does not need to override/implement this method. */ Result invoke(Invoker invoker, Invocation invocation) throws RpcException; - /** - * Return processing result - * - * @param result result - * @param invoker invoker - * @param invocation invocation - * @return Return {@link Result} - */ - default Result onResponse(Result result, Invoker invoker, Invocation invocation) { - return result; + interface Listener { + + void onResponse(Result result, Invoker invoker, Invocation invocation); + + void onError(Throwable t, Invoker invoker, Invocation invocation); } } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.java new file mode 100644 index 00000000000..2468837958c --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.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.rpc; + +import java.util.concurrent.CompletableFuture; + +/** + * Used for async call scenario. But if the method you are calling has a {@link CompletableFuture} signature + * you do not need to use this class since you will get a Future response directly. + *

+ * Remember to save the Future reference before making another call using the same thread, otherwise, + * the current Future will be override by the new one, which means you will lose the chance get the return value. + */ +public class FutureContext { + + public static ThreadLocal> futureTL = new ThreadLocal<>(); + + /** + * get future. + * + * @param + * @return future + */ + @SuppressWarnings("unchecked") + public static CompletableFuture getCompletableFuture() { + return (CompletableFuture) futureTL.get(); + } + + /** + * set future. + * + * @param future + */ + public static void setFuture(CompletableFuture future) { + futureTL.set(future); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java index 2b5f3dd9e95..058e0ad09c9 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java @@ -59,6 +59,10 @@ public interface Invocation { */ Map getAttachments(); + void setAttachment(String key, String value); + + void setAttachmentIfAbsent(String key, String value); + /** * get attachment by key. * diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java new file mode 100644 index 00000000000..a97a0be61f4 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java @@ -0,0 +1,23 @@ +/* + * 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; + +public enum InvokeMode { + + SYNC, ASYNC, FUTURE; + +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java similarity index 71% rename from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java rename to dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java index 37fe6cc6e34..622ef1f0309 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java @@ -1,38 +1,29 @@ -/* - * 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.remoting.exchange; - -/** - * Callback - */ -public interface ResponseCallback { - - /** - * done. - * - * @param response - */ - void done(Object response); - - /** - * caught exception. - * - * @param exception - */ - void caught(Throwable exception); - -} \ No newline at end of file +/* + * 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; + +/** + * + */ +public abstract class ListenableFilter implements Filter { + + protected Listener listener = null; + + public Listener listener() { + return listener; + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java index 58a258241ff..1ecd9dae771 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java @@ -18,6 +18,9 @@ import java.io.Serializable; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; /** @@ -25,7 +28,7 @@ * * @serial Don't change the class name and package name. * @see org.apache.dubbo.rpc.Invoker#invoke(Invocation) - * @see org.apache.dubbo.rpc.RpcResult + * @see AppResponse */ public interface Result extends Serializable { @@ -36,6 +39,8 @@ public interface Result extends Serializable { */ Object getValue(); + void setValue(Object value); + /** * Get exception. * @@ -43,6 +48,8 @@ public interface Result extends Serializable { */ Throwable getException(); + void setException(Throwable t); + /** * Has exception. * @@ -66,14 +73,6 @@ public interface Result extends Serializable { */ Object recreate() throws Throwable; - /** - * @see org.apache.dubbo.rpc.Result#getValue() - * @deprecated Replace to getValue() - */ - @Deprecated - Object getResult(); - - /** * get attachments. * @@ -111,4 +110,10 @@ public interface Result extends Serializable { void setAttachment(String key, String value); + Result thenApplyWithContext(Function fn); + + CompletableFuture thenApply(Function fn); + + Result get() throws InterruptedException, ExecutionException; + } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java index fa63b7e4c1a..c585c178ce9 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java @@ -70,7 +70,6 @@ protected RpcContext initialValue() { private final Map attachments = new HashMap(); private final Map values = new HashMap(); - private Future future; private List urls; @@ -139,32 +138,6 @@ public static void restoreContext(RpcContext oldContext) { LOCAL.set(oldContext); } - - public RpcContext copyOf() { - RpcContext copy = new RpcContext(); - copy.attachments.putAll(this.attachments); - copy.values.putAll(this.values); - copy.future = this.future; - copy.urls = this.urls; - copy.url = this.url; - copy.methodName = this.methodName; - copy.parameterTypes = this.parameterTypes; - copy.arguments = this.arguments; - copy.localAddress = this.localAddress; - copy.remoteAddress = this.remoteAddress; - copy.remoteApplicationName = this.remoteApplicationName; - copy.invokers = this.invokers; - copy.invoker = this.invoker; - copy.invocation = this.invocation; - - copy.request = this.request; - copy.response = this.response; - copy.asyncContext = this.asyncContext; - - return copy; - } - - /** * remove context. * @@ -246,7 +219,7 @@ public boolean isConsumerSide() { */ @SuppressWarnings("unchecked") public CompletableFuture getCompletableFuture() { - return (CompletableFuture) future; + return FutureContext.getCompletableFuture(); } /** @@ -257,7 +230,7 @@ public CompletableFuture getCompletableFuture() { */ @SuppressWarnings("unchecked") public Future getFuture() { - return (Future) future; + return FutureContext.getCompletableFuture(); } /** @@ -265,8 +238,8 @@ public Future getFuture() { * * @param future */ - public void setFuture(Future future) { - this.future = future; + public void setFuture(CompletableFuture future) { + FutureContext.setFuture(future); } public List getUrls() { diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java index 707bf3b77ad..207239d11ef 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java @@ -45,6 +45,10 @@ public class RpcInvocation implements Invocation, Serializable { private transient Invoker invoker; + private transient Class returnType; + + private transient InvokeMode invokeMode; + public RpcInvocation() { } @@ -87,6 +91,7 @@ public RpcInvocation(Method method, Object[] arguments) { public RpcInvocation(Method method, Object[] arguments, Map attachment) { this(method.getName(), method.getParameterTypes(), arguments, attachment, null); + this.returnType = method.getReturnType(); } public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments) { @@ -205,6 +210,22 @@ public String getAttachment(String key, String defaultValue) { return value; } + public Class getReturnType() { + return returnType; + } + + public void setReturnType(Class returnType) { + this.returnType = returnType; + } + + public InvokeMode getInvokeMode() { + return invokeMode; + } + + public void setInvokeMode(InvokeMode invokeMode) { + this.invokeMode = invokeMode; + } + @Override public String toString() { return "RpcInvocation [methodName=" + methodName + ", parameterTypes=" diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcResult.java deleted file mode 100644 index 8087210f29a..00000000000 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcResult.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc; - -import java.lang.reflect.Field; - -/** - * RPC Result. - * - * @serial Don't change the class name and properties. - */ -public class RpcResult extends AbstractResult { - - private static final long serialVersionUID = -6925924956850004727L; - - public RpcResult() { - } - - public RpcResult(Object result) { - this.result = result; - } - - public RpcResult(Throwable exception) { - this.exception = handleStackTraceNull(exception); - } - - @Override - public Object recreate() throws Throwable { - if (exception != null) { - throw exception; - } - return result; - } - - /** - * @see org.apache.dubbo.rpc.RpcResult#getValue() - * @deprecated Replace to getValue() - */ - @Override - @Deprecated - public Object getResult() { - return getValue(); - } - - /** - * @see org.apache.dubbo.rpc.RpcResult#setValue(Object) - * @deprecated Replace to setValue() - */ - @Deprecated - public void setResult(Object result) { - setValue(result); - } - - @Override - public Object getValue() { - return result; - } - - public void setValue(Object value) { - this.result = value; - } - - @Override - public Throwable getException() { - return exception; - } - - public void setException(Throwable e) { - this.exception = handleStackTraceNull(e); - } - - @Override - public boolean hasException() { - return exception != null; - } - - @Override - public String toString() { - return "RpcResult [result=" + result + ", exception=" + exception + "]"; - } - - /** - * we need to deal the exception whose stack trace is null. - *

- * see https://github.com/apache/incubator-dubbo/pull/2956 - * and https://github.com/apache/incubator-dubbo/pull/3634 - * and https://github.com/apache/incubator-dubbo/issues/619 - * - * @param e exception - * @return exception after deal with stack trace - */ - private Throwable handleStackTraceNull(Throwable e) { - if (e != null) { - try { - // get Throwable class - Class clazz = e.getClass(); - while (!clazz.getName().equals(Throwable.class.getName())) { - clazz = clazz.getSuperclass(); - } - // get stackTrace value - Field stackTraceField = clazz.getDeclaredField("stackTrace"); - stackTraceField.setAccessible(true); - Object stackTrace = stackTraceField.get(e); - if (stackTrace == null) { - e.setStackTrace(new StackTraceElement[0]); - } - } catch (Throwable t) { - // ignore - } - } - - return e; - } -} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/SimpleAsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/SimpleAsyncRpcResult.java deleted file mode 100644 index 98e42d94a38..00000000000 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/SimpleAsyncRpcResult.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc; - -import java.util.concurrent.CompletableFuture; - -/** - * A sub class used for normal async invoke. - * - * NOTICE!! - * - *

- * You should never rely on this class directly when using or extending Dubbo, the implementation of {@link SimpleAsyncRpcResult} - * is only a workaround for compatibility purpose. It may be changed or even get removed from the next major version. - * Please only use {@link Result} or {@link RpcResult}. - *

- * - * Check {@link AsyncRpcResult} for more details. - * - * TODO AsyncRpcResult, AsyncNormalRpcResult should not be a parent-child hierarchy. - */ -public class SimpleAsyncRpcResult extends AsyncRpcResult { - public SimpleAsyncRpcResult(CompletableFuture future, boolean registerCallback) { - super(future, registerCallback); - } - - public SimpleAsyncRpcResult(CompletableFuture future, CompletableFuture rFuture, boolean registerCallback) { - super(future, rFuture, registerCallback); - } - - @Override - public Object recreate() throws Throwable { - // TODO should we check the status of valueFuture here? - return null; - } -} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ActiveLimitFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ActiveLimitFilter.java index 1c2f69fecb9..fe692af565f 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ActiveLimitFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ActiveLimitFilter.java @@ -19,14 +19,17 @@ import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.ListenableFilter; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcStatus; /** + * * ActiveLimitFilter restrict the concurrent client invocation for a service or service's method from client side. * To use active limit filter, configured url with actives and provide valid >0 integer value. *
@@ -39,50 +42,75 @@
  * @see Filter
  */
 @Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)
-public class ActiveLimitFilter implements Filter {
+public class ActiveLimitFilter extends ListenableFilter {
+
+    private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
+
+    public ActiveLimitFilter() {
+        super.listener = new ActiveLimitListener();
+    }
 
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
         URL url = invoker.getUrl();
         String methodName = invocation.getMethodName();
         int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
-        RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
-        if (!count.beginCount(url, methodName, max)) {
+        RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
+        if (!RpcStatus.beginCount(url, methodName, max)) {
             long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
             long start = System.currentTimeMillis();
             long remain = timeout;
-            synchronized (count) {
-                while (!count.beginCount(url, methodName, max)) {
+            synchronized (rpcStatus) {
+                while (!RpcStatus.beginCount(url, methodName, max)) {
                     try {
-                        count.wait(remain);
+                        rpcStatus.wait(remain);
                     } catch (InterruptedException e) {
                         // ignore
                     }
                     long elapsed = System.currentTimeMillis() - start;
                     remain = timeout - elapsed;
                     if (remain <= 0) {
-                        throw new RpcException("Waiting concurrent invoke timeout in client-side for service:  "
-                                + invoker.getInterface().getName() + ", method: "
-                                + invocation.getMethodName() + ", elapsed: " + elapsed
-                                + ", timeout: " + timeout + ". concurrent invokes: " + count.getActive()
-                                + ". max concurrent invoke limit: " + max);
+                        throw new RpcException("Waiting concurrent invoke timeout in client-side for service:  " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", elapsed: " + elapsed + ", timeout: " + timeout + ". concurrent invokes: " + rpcStatus.getActive() + ". max concurrent invoke limit: " + max);
                     }
                 }
             }
         }
 
-        boolean isSuccess = true;
-        long begin = System.currentTimeMillis();
-        try {
-            return invoker.invoke(invocation);
-        } catch (RuntimeException t) {
-            isSuccess = false;
-            throw t;
-        } finally {
-            count.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
+        invocation.setAttachment(ACTIVELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
+
+        return invoker.invoke(invocation);
+    }
+
+    static class ActiveLimitListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            String methodName = invocation.getMethodName();
+            URL url = invoker.getUrl();
+            int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
+
+            RpcStatus.endCount(url, methodName, getElapsed(invocation), true);
+            notifyFinish(RpcStatus.getStatus(url, methodName), max);
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+            String methodName = invocation.getMethodName();
+            URL url = invoker.getUrl();
+            int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
+
+            RpcStatus.endCount(url, methodName, getElapsed(invocation), false);
+            notifyFinish(RpcStatus.getStatus(url, methodName), max);
+        }
+
+        private long getElapsed(Invocation invocation) {
+            String beginTime = invocation.getAttachment(ACTIVELIMIT_FILTER_START_TIME);
+            return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
+        }
+
+        private void notifyFinish(RpcStatus rpcStatus, int max) {
             if (max > 0) {
-                synchronized (count) {
-                    count.notifyAll();
+                synchronized (rpcStatus) {
+                    rpcStatus.notifyAll();
                 }
             }
         }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/CompatibleFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/CompatibleFilter.java
index ace832ca33c..ad1a8a2492b 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/CompatibleFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/CompatibleFilter.java
@@ -24,9 +24,9 @@
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
@@ -45,44 +45,54 @@
  * @see Filter
  *
  */
-public class CompatibleFilter implements Filter {
+public class CompatibleFilter extends ListenableFilter {
 
     private static Logger logger = LoggerFactory.getLogger(CompatibleFilter.class);
 
+    public CompatibleFilter() {
+        super.listener = new CompatibleListener();
+    }
+
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
-        Result result = invoker.invoke(invocation);
-        if (!invocation.getMethodName().startsWith("$") && !result.hasException()) {
-            Object value = result.getValue();
-            if (value != null) {
-                try {
-                    Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
-                    Class type = method.getReturnType();
-                    Object newValue;
-                    String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
-                    if ("json".equals(serialization)
-                            || "fastjson".equals(serialization)) {
-                        // If the serialization key is json or fastjson
-                        Type gtype = method.getGenericReturnType();
-                        newValue = PojoUtils.realize(value, type, gtype);
-                    } else if (!type.isInstance(value)) {
-                        //if local service interface's method's return type is not instance of return value
-                        newValue = PojoUtils.isPojo(type)
-                                ? PojoUtils.realize(value, type)
-                                : CompatibleTypeUtils.compatibleTypeConvert(value, type);
+        return invoker.invoke(invocation);
+    }
 
-                    } else {
-                        newValue = value;
-                    }
-                    if (newValue != value) {
-                        result = new RpcResult(newValue);
+    static class CompatibleListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            if (!invocation.getMethodName().startsWith("$") && !result.hasException()) {
+                Object value = result.getValue();
+                if (value != null) {
+                    try {
+                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+                        Class type = method.getReturnType();
+                        Object newValue;
+                        String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
+                        if ("json".equals(serialization) || "fastjson".equals(serialization)) {
+                            // If the serialization key is json or fastjson
+                            Type gtype = method.getGenericReturnType();
+                            newValue = PojoUtils.realize(value, type, gtype);
+                        } else if (!type.isInstance(value)) {
+                            //if local service interface's method's return type is not instance of return value
+                            newValue = PojoUtils.isPojo(type) ? PojoUtils.realize(value, type) : CompatibleTypeUtils.compatibleTypeConvert(value, type);
+
+                        } else {
+                            newValue = value;
+                        }
+                        if (newValue != value) {
+                            result.setValue(newValue);
+                        }
+                    } catch (Throwable t) {
+                        logger.warn(t.getMessage(), t);
                     }
-                } catch (Throwable t) {
-                    logger.warn(t.getMessage(), t);
                 }
             }
         }
-        return result;
-    }
 
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
index 5db6b889d35..a2005e21e1a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ConsumerContextFilter.java
@@ -19,9 +19,9 @@
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.NetUtils;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
@@ -35,7 +35,11 @@
  * @see RpcContext
  */
 @Activate(group = Constants.CONSUMER, order = -10000)
-public class ConsumerContextFilter implements Filter {
+public class ConsumerContextFilter extends ListenableFilter {
+
+    public ConsumerContextFilter() {
+        super.listener = new ConsumerContextListener();
+    }
 
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
@@ -49,18 +53,22 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept
             ((RpcInvocation) invocation).setInvoker(invoker);
         }
         try {
-            // TODO should we clear server context?
             RpcContext.removeServerContext();
             return invoker.invoke(invocation);
         } finally {
-            // TODO removeContext? but we need to save future for RpcContext.getFuture() API. If clear attachments here, attachments will not available when postProcessResult is invoked.
-            RpcContext.getContext().clearAttachments();
+            RpcContext.removeContext();
         }
     }
 
-    @Override
-    public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
-        RpcContext.getServerContext().setAttachments(result.getAttachments());
-        return result;
+    static class ConsumerContextListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            RpcContext.getServerContext().setAttachments(result.getAttachments());
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
     }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
index fe85da0386a..df135a64894 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
@@ -18,9 +18,9 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
@@ -38,7 +38,11 @@
  * @see RpcContext
  */
 @Activate(group = Constants.PROVIDER, order = -10000)
-public class ContextFilter implements Filter {
+public class ContextFilter extends ListenableFilter {
+
+    public ContextFilter() {
+        super.listener = new ContextListener();
+    }
 
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
@@ -86,10 +90,16 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept
         }
     }
 
-    @Override
-    public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
-        // pass attachments to result
-        result.addAttachments(RpcContext.getServerContext().getAttachments());
-        return result;
+    static class ContextListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            // pass attachments to result
+            result.addAttachments(RpcContext.getServerContext().getAttachments());
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
     }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/EchoFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/EchoFilter.java
index cba9e7f4f46..7e3e5b8dda0 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/EchoFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/EchoFilter.java
@@ -18,12 +18,12 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 
 /**
  * Dubbo provided default Echo echo service, which is available for all dubbo provider service interface.
@@ -34,7 +34,7 @@ public class EchoFilter implements Filter {
     @Override
     public Result invoke(Invoker invoker, Invocation inv) throws RpcException {
         if (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
-            return new RpcResult(inv.getArguments()[0]);
+            return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
         }
         return invoker.invoke(inv);
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
index 7715348f827..1cb9f87d488 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java
@@ -22,13 +22,12 @@
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.service.GenericService;
 
 import java.lang.reflect.Method;
@@ -45,85 +44,82 @@
  * 
  */
 @Activate(group = Constants.PROVIDER)
-public class ExceptionFilter implements Filter {
-
-    private final Logger logger;
+public class ExceptionFilter extends ListenableFilter {
 
     public ExceptionFilter() {
-        this(LoggerFactory.getLogger(ExceptionFilter.class));
-    }
-
-    public ExceptionFilter(Logger logger) {
-        this.logger = logger;
+        super.listener = new ExceptionListener();
     }
 
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
-        try {
-            return invoker.invoke(invocation);
-        } catch (RuntimeException e) {
-            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
-                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
-                    + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
-            throw e;
-        }
+        return invoker.invoke(invocation);
     }
 
-    @Override
-    public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
-        if (result.hasException() && GenericService.class != invoker.getInterface()) {
-            try {
-                Throwable exception = result.getException();
-
-                // directly throw if it's checked exception
-                if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
-                    return result;
-                }
-                // directly throw if the exception appears in the signature
+    static class ExceptionListener implements Listener {
+
+        private Logger logger = LoggerFactory.getLogger(ExceptionListener.class);
+
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            if (result.hasException() && GenericService.class != invoker.getInterface()) {
                 try {
-                    Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
-                    Class[] exceptionClassses = method.getExceptionTypes();
-                    for (Class exceptionClass : exceptionClassses) {
-                        if (exception.getClass().equals(exceptionClass)) {
-                            return result;
+                    Throwable exception = result.getException();
+
+                    // directly throw if it's checked exception
+                    if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
+                        return;
+                    }
+                    // directly throw if the exception appears in the signature
+                    try {
+                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+                        Class[] exceptionClassses = method.getExceptionTypes();
+                        for (Class exceptionClass : exceptionClassses) {
+                            if (exception.getClass().equals(exceptionClass)) {
+                                return;
+                            }
                         }
+                    } catch (NoSuchMethodException e) {
+                        return;
                     }
-                } catch (NoSuchMethodException e) {
-                    return result;
-                }
 
-                // for the exception not found in method's signature, print ERROR message in server's log.
-                logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
-                        + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
-                        + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
+                    // for the exception not found in method's signature, print ERROR message in server's log.
+                    logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
 
-                // directly throw if exception class and interface class are in the same jar file.
-                String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
-                String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
-                if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
-                    return result;
-                }
-                // directly throw if it's JDK exception
-                String className = exception.getClass().getName();
-                if (className.startsWith("java.") || className.startsWith("javax.")) {
-                    return result;
-                }
-                // directly throw if it's dubbo exception
-                if (exception instanceof RpcException) {
-                    return result;
-                }
+                    // directly throw if exception class and interface class are in the same jar file.
+                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
+                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
+                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
+                        return;
+                    }
+                    // directly throw if it's JDK exception
+                    String className = exception.getClass().getName();
+                    if (className.startsWith("java.") || className.startsWith("javax.")) {
+                        return;
+                    }
+                    // directly throw if it's dubbo exception
+                    if (exception instanceof RpcException) {
+                        return;
+                    }
 
-                // otherwise, wrap with RuntimeException and throw back to the client
-                return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
-            } catch (Throwable e) {
-                logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
-                        + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
-                        + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
-                return result;
+                    // otherwise, wrap with RuntimeException and throw back to the client
+                    result.setException(new RuntimeException(StringUtils.toString(exception)));
+                    return;
+                } catch (Throwable e) {
+                    logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
+                    return;
+                }
             }
         }
-        return result;
-    }
 
+        @Override
+        public void onError(Throwable e, Invoker invoker, Invocation invocation) {
+            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
+        }
+
+        // For test purpose
+        public void setLogger(Logger logger) {
+            this.logger = logger;
+        }
+    }
 }
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilter.java
index b335a4141ff..c1b2eb5e6bc 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilter.java
@@ -19,21 +19,29 @@
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcStatus;
 
 /**
+ *
  * The maximum parallel execution request count per method per service for the provider.If the max configured
  * executes is set to 10 and if invoke request where it is already 10 then it will throws exception. It
  * continue the same behaviour un till it is <10.
  *
  */
 @Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
-public class ExecuteLimitFilter implements Filter {
+public class ExecuteLimitFilter extends ListenableFilter {
+
+    private static final String EXECUTELIMIT_FILTER_START_TIME = "execugtelimit_filter_start_time";
+
+    public ExecuteLimitFilter() {
+        super.listener = new ExecuteLimitListener();
+    }
 
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
@@ -46,20 +54,32 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept
                     "\" /> limited.");
         }
 
-        long begin = System.currentTimeMillis();
-        boolean isSuccess = true;
+        invocation.setAttachment(EXECUTELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
         try {
             return invoker.invoke(invocation);
         } catch (Throwable t) {
-            isSuccess = false;
             if (t instanceof RuntimeException) {
                 throw (RuntimeException) t;
             } else {
                 throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
             }
-        } finally {
-            RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
         }
     }
 
+    static class ExecuteLimitListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), true);
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+            RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), false);
+        }
+
+        private long getElapsed(Invocation invocation) {
+            String beginTime = invocation.getAttachment(EXECUTELIMIT_FILTER_START_TIME);
+            return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
+        }
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
index ae7b92c40f1..5c86cae6577 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
@@ -28,14 +28,13 @@
 import org.apache.dubbo.common.utils.PojoUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.service.GenericException;
 import org.apache.dubbo.rpc.service.GenericService;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
@@ -47,11 +46,15 @@
  * GenericInvokerFilter.
  */
 @Activate(group = Constants.PROVIDER, order = -20000)
-public class GenericFilter implements Filter {
+public class GenericFilter extends ListenableFilter {
+
+    public GenericFilter() {
+        super.listener = new GenericListener();
+    }
 
     @Override
     public Result invoke(Invoker invoker, Invocation inv) throws RpcException {
-        if (inv.getMethodName().equals(Constants.$INVOKE)
+        if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
                 && inv.getArguments() != null
                 && inv.getArguments().length == 3
                 && !GenericService.class.isAssignableFrom(invoker.getInterface())) {
@@ -108,33 +111,52 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException {
                         }
                     }
                 }
-                Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
-                if (result.hasException()
-                        && !(result.getException() instanceof GenericException)) {
-                    return new RpcResult(new GenericException(result.getException()));
+                return invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
+            } catch (NoSuchMethodException e) {
+                throw new RpcException(e.getMessage(), e);
+            } catch (ClassNotFoundException e) {
+                throw new RpcException(e.getMessage(), e);
+            }
+        }
+        return invoker.invoke(inv);
+    }
+
+    static class GenericListener implements Listener {
+
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation inv) {
+            if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
+                    && inv.getArguments() != null
+                    && inv.getArguments().length == 3
+                    && !GenericService.class.isAssignableFrom(invoker.getInterface())) {
+
+                String generic = inv.getAttachment(Constants.GENERIC_KEY);
+                if (StringUtils.isBlank(generic)) {
+                    generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY);
+                }
+
+                if (result.hasException() && !(result.getException() instanceof GenericException)) {
+                    result.setException(new GenericException(result.getException()));
                 }
                 if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                     try {
                         UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
-                        ExtensionLoader.getExtensionLoader(Serialization.class)
-                                .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
-                                .serialize(null, os).writeObject(result.getValue());
-                        return new RpcResult(os.toByteArray());
+                        ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(result.getValue());
+                        result.setValue(os.toByteArray());
                     } catch (IOException e) {
                         throw new RpcException("Serialize result failed.", e);
                     }
                 } else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
-                    return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
+                    result.setValue(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
                 } else {
-                    return new RpcResult(PojoUtils.generalize(result.getValue()));
+                    result.setValue(PojoUtils.generalize(result.getValue()));
                 }
-            } catch (NoSuchMethodException e) {
-                throw new RpcException(e.getMessage(), e);
-            } catch (ClassNotFoundException e) {
-                throw new RpcException(e.getMessage(), e);
             }
         }
-        return invoker.invoke(inv);
-    }
 
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
index 6df1262fbef..f9caf44f069 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java
@@ -25,37 +25,42 @@
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.PojoUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.service.GenericException;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
+import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 
 /**
  * GenericImplInvokerFilter
  */
 @Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000)
-public class GenericImplFilter implements Filter {
+public class GenericImplFilter extends ListenableFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
 
     private static final Class[] GENERIC_PARAMETER_TYPES = new Class[]{String.class, String[].class, Object[].class};
 
+    public GenericImplFilter() {
+        super.listener = new GenericImplListener();
+    }
+
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
         String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY);
         if (ProtocolUtils.isGeneric(generic)
-                && !Constants.$INVOKE.equals(invocation.getMethodName())
+                && (!Constants.$INVOKE.equals(invocation.getMethodName()) && !Constants.$INVOKE_ASYNC.equals(invocation.getMethodName()))
                 && invocation instanceof RpcInvocation) {
-            RpcInvocation invocation2 = (RpcInvocation) invocation;
+            RpcInvocation invocation2 = new RpcInvocation(invocation);
             String methodName = invocation2.getMethodName();
             Class[] parameterTypes = invocation2.getParameterTypes();
             Object[] arguments = invocation2.getArguments();
@@ -75,77 +80,15 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept
                 args = PojoUtils.generalize(arguments);
             }
 
-            invocation2.setMethodName(Constants.$INVOKE);
+            if (RpcUtils.isReturnTypeFuture(invocation)) {
+                invocation2.setMethodName(Constants.$INVOKE_ASYNC);
+            } else {
+                invocation2.setMethodName(Constants.$INVOKE);
+            }
             invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
             invocation2.setArguments(new Object[]{methodName, types, args});
-            Result result = invoker.invoke(invocation2);
-
-            if (!result.hasException()) {
-                Object value = result.getValue();
-                try {
-                    Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
-                    if (ProtocolUtils.isBeanGenericSerialization(generic)) {
-                        if (value == null) {
-                            return new RpcResult(value);
-                        } else if (value instanceof JavaBeanDescriptor) {
-                            return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
-                        } else {
-                            throw new RpcException(
-                                    "The type of result value is " +
-                                            value.getClass().getName() +
-                                            " other than " +
-                                            JavaBeanDescriptor.class.getName() +
-                                            ", and the result is " +
-                                            value);
-                        }
-                    } else {
-                        return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));
-                    }
-                } catch (NoSuchMethodException e) {
-                    throw new RpcException(e.getMessage(), e);
-                }
-            } else if (result.getException() instanceof GenericException) {
-                GenericException exception = (GenericException) result.getException();
-                try {
-                    String className = exception.getExceptionClass();
-                    Class clazz = ReflectUtils.forName(className);
-                    Throwable targetException = null;
-                    Throwable lastException = null;
-                    try {
-                        targetException = (Throwable) clazz.newInstance();
-                    } catch (Throwable e) {
-                        lastException = e;
-                        for (Constructor constructor : clazz.getConstructors()) {
-                            try {
-                                targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
-                                break;
-                            } catch (Throwable e1) {
-                                lastException = e1;
-                            }
-                        }
-                    }
-                    if (targetException != null) {
-                        try {
-                            Field field = Throwable.class.getDeclaredField("detailMessage");
-                            if (!field.isAccessible()) {
-                                field.setAccessible(true);
-                            }
-                            field.set(targetException, exception.getExceptionMessage());
-                        } catch (Throwable e) {
-                            logger.warn(e.getMessage(), e);
-                        }
-                        result = new RpcResult(targetException);
-                    } else if (lastException != null) {
-                        throw lastException;
-                    }
-                } catch (Throwable e) {
-                    throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
-                }
-            }
-            return result;
-        }
-
-        if (invocation.getMethodName().equals(Constants.$INVOKE)
+            return invoker.invoke(invocation2);
+        } else if ((invocation.getMethodName().equals(Constants.$INVOKE) || invocation.getMethodName().equals(Constants.$INVOKE_ASYNC))
                 && invocation.getArguments() != null
                 && invocation.getArguments().length == 3
                 && ProtocolUtils.isGeneric(generic)) {
@@ -166,20 +109,89 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept
                 }
             }
 
-            ((RpcInvocation) invocation).setAttachment(
+            invocation.setAttachment(
                     Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY));
         }
         return invoker.invoke(invocation);
     }
 
     private void error(String generic, String expected, String actual) throws RpcException {
-        throw new RpcException(
-                "Generic serialization [" +
-                        generic +
-                        "] only support message type " +
-                        expected +
-                        " and your message type is " +
-                        actual);
+        throw new RpcException("Generic serialization [" + generic + "] only support message type " + expected + " and your message type is " + actual);
+    }
+
+    static class GenericImplListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY);
+            String methodName = invocation.getMethodName();
+            Class[] parameterTypes = invocation.getParameterTypes();
+            if (ProtocolUtils.isGeneric(generic)
+                    && (!Constants.$INVOKE.equals(invocation.getMethodName()) && !Constants.$INVOKE_ASYNC.equals(invocation.getMethodName()))
+                    && invocation instanceof RpcInvocation) {
+                if (!result.hasException()) {
+                    Object value = result.getValue();
+                    try {
+                        Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
+                        if (ProtocolUtils.isBeanGenericSerialization(generic)) {
+                            if (value == null) {
+                                result.setValue(value);
+                            } else if (value instanceof JavaBeanDescriptor) {
+                                result.setValue(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
+                            } else {
+                                throw new RpcException("The type of result value is " + value.getClass().getName() + " other than " + JavaBeanDescriptor.class.getName() + ", and the result is " + value);
+                            }
+                        } else {
+                            Type[] types = ReflectUtils.getReturnTypes(method);
+                            result.setValue(PojoUtils.realize(value, (Class) types[0], types[1]));
+                        }
+                    } catch (NoSuchMethodException e) {
+                        throw new RpcException(e.getMessage(), e);
+                    }
+                } else if (result.getException() instanceof GenericException) {
+                    GenericException exception = (GenericException) result.getException();
+                    try {
+                        String className = exception.getExceptionClass();
+                        Class clazz = ReflectUtils.forName(className);
+                        Throwable targetException = null;
+                        Throwable lastException = null;
+                        try {
+                            targetException = (Throwable) clazz.newInstance();
+                        } catch (Throwable e) {
+                            lastException = e;
+                            for (Constructor constructor : clazz.getConstructors()) {
+                                try {
+                                    targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
+                                    break;
+                                } catch (Throwable e1) {
+                                    lastException = e1;
+                                }
+                            }
+                        }
+                        if (targetException != null) {
+                            try {
+                                Field field = Throwable.class.getDeclaredField("detailMessage");
+                                if (!field.isAccessible()) {
+                                    field.setAccessible(true);
+                                }
+                                field.set(targetException, exception.getExceptionMessage());
+                            } catch (Throwable e) {
+                                logger.warn(e.getMessage(), e);
+                            }
+                            result.setException(targetException);
+                        } else if (lastException != null) {
+                            throw lastException;
+                        }
+                    } catch (Throwable e) {
+                        throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
     }
 
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TimeoutFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TimeoutFilter.java
index c00689fc1c8..b6335823d8e 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TimeoutFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TimeoutFilter.java
@@ -20,12 +20,11 @@
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcInvocation;
 
 import java.util.Arrays;
 
@@ -33,42 +32,40 @@
  * Log any invocation timeout, but don't stop server from running
  */
 @Activate(group = Constants.PROVIDER)
-public class TimeoutFilter implements Filter {
+public class TimeoutFilter extends ListenableFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
 
     private static final String TIMEOUT_FILTER_START_TIME = "timeout_filter_start_time";
 
+    public TimeoutFilter() {
+        super.listener = new TimeoutListener();
+    }
+
     @Override
     public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
-        if (invocation.getAttachments() != null) {
-            long start = System.currentTimeMillis();
-            invocation.getAttachments().put(TIMEOUT_FILTER_START_TIME, String.valueOf(start));
-        } else {
-            if (invocation instanceof RpcInvocation) {
-                RpcInvocation invc = (RpcInvocation) invocation;
-                long start = System.currentTimeMillis();
-                invc.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(start));
-            }
-        }
+        invocation.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
         return invoker.invoke(invocation);
     }
 
-    @Override
-    public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
-        String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
-        if (startAttach != null) {
-            long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
-            if (invoker.getUrl() != null
-                    && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(),
-                    "timeout", Integer.MAX_VALUE)) {
-                if (logger.isWarnEnabled()) {
-                    logger.warn("invoke time out. method: " + invocation.getMethodName()
-                            + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is "
-                            + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
+    static class TimeoutListener implements Listener {
+
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
+            if (startAttach != null) {
+                long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
+                if (invoker.getUrl() != null && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE)) {
+                    if (logger.isWarnEnabled()) {
+                        logger.warn("invoke time out. method: " + invocation.getMethodName() + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is " + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
+                    }
                 }
             }
         }
-        return result;
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
     }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index 3df1f1d741e..58cb4105f86 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -16,7 +16,6 @@
  */
 package org.apache.dubbo.rpc.protocol;
 
-import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.logger.Logger;
@@ -24,13 +23,13 @@
 import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.lang.reflect.InvocationTargetException;
@@ -148,9 +147,8 @@ public Result invoke(Invocation inv) throws RpcException {
              */
             invocation.addAttachments(contextAttachments);
         }
-        if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
-            invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
-        }
+
+        invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
         RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
 
         try {
@@ -158,21 +156,21 @@ public Result invoke(Invocation inv) throws RpcException {
         } catch (InvocationTargetException e) { // biz exception
             Throwable te = e.getTargetException();
             if (te == null) {
-                return new RpcResult(e);
+                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
             } else {
                 if (te instanceof RpcException) {
                     ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
                 }
-                return new RpcResult(te);
+                return AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
             }
         } catch (RpcException e) {
             if (e.isBiz()) {
-                return new RpcResult(e);
+                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
             } else {
                 throw e;
             }
         } catch (Throwable e) {
-            return new RpcResult(e);
+            return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
         }
     }
 
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 814e9182280..a2a6ebb135f 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
@@ -24,6 +24,7 @@
 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.support.ProtocolUtils;
 
 import java.util.ArrayList;
@@ -82,4 +83,11 @@ public void destroy() {
             }
         }
     }
+
+    @Override
+    public  Invoker refer(Class type, URL url) throws RpcException {
+        return new AsyncToSyncInvoker<>(doRefer(type, url));
+    }
+
+    protected abstract  Invoker doRefer(Class type, URL url) throws RpcException;
 }
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 a77a5bdc5f3..e03d3800984 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
@@ -92,13 +92,14 @@ public void unexport() {
     }
 
     @Override
-    public  Invoker refer(final Class type, final URL url) throws RpcException {
-        final Invoker target = proxyFactory.getInvoker(doRefer(type, url), type, url);
+    public  Invoker doRefer(final Class type, final URL url) throws RpcException {
+        final Invoker target = proxyFactory.getInvoker(getFrameworkProxy(type, url), type, url);
         Invoker invoker = new AbstractInvoker(type, url) {
             @Override
             protected Result doInvoke(Invocation invocation) throws Throwable {
                 try {
                     Result result = target.invoke(invocation);
+                    // FIXME result is an AsyncRpcResult instance.
                     Throwable e = result.getException();
                     if (e != null) {
                         for (Class rpcException : rpcExceptions) {
@@ -143,6 +144,6 @@ protected int getErrorCode(Throwable e) {
 
     protected abstract  Runnable doExport(T impl, Class type, URL url) throws RpcException;
 
-    protected abstract  T doRefer(Class type, URL url) throws RpcException;
+    protected abstract  T getFrameworkProxy(Class type, URL url) throws RpcException;
 
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AsyncToSyncInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AsyncToSyncInvoker.java
new file mode 100644
index 00000000000..5c06b4e4d1d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AsyncToSyncInvoker.java
@@ -0,0 +1,89 @@
+/*
+ * 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.remoting.RemotingException;
+import org.apache.dubbo.remoting.TimeoutException;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvokeMode;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * This class will work as a wrapper wrapping outside of each protocol invoker.
+ * @param 
+ */
+public class AsyncToSyncInvoker implements Invoker {
+
+    private Invoker invoker;
+
+    public AsyncToSyncInvoker(Invoker invoker) {
+        this.invoker = invoker;
+    }
+
+    @Override
+    public Class getInterface() {
+        return invoker.getInterface();
+    }
+
+    @Override
+    public Result invoke(Invocation invocation) throws RpcException {
+        Result asyncResult = invoker.invoke(invocation);
+
+        try {
+            if (InvokeMode.SYNC == ((RpcInvocation)invocation).getInvokeMode()) {
+                asyncResult.get();
+            }
+        } catch (InterruptedException e) {
+            throw new RpcException("Interrupted unexpectedly while waiting for remoting result to return!  method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
+        } catch (ExecutionException e) {
+            Throwable t = e.getCause();
+            if (t instanceof TimeoutException) {
+                throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
+            } else if (t instanceof RemotingException) {
+                throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
+            }
+        } catch (Throwable e) {
+            throw new RpcException(e.getMessage(), e);
+        }
+        return asyncResult;
+    }
+
+    @Override
+    public URL getUrl() {
+        return invoker.getUrl();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return invoker.isAvailable();
+    }
+
+    @Override
+    public void destroy() {
+        invoker.destroy();
+    }
+
+    public Invoker getInvoker() {
+        return invoker;
+    }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java
index c5ed5943cd0..2106a4e0da8 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java
@@ -19,11 +19,11 @@
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
@@ -70,14 +70,29 @@ public boolean isAvailable() {
 
                     @Override
                     public Result invoke(Invocation invocation) throws RpcException {
-                        Result result = filter.invoke(next, invocation);
-                        if (result instanceof AsyncRpcResult) {
-                            AsyncRpcResult asyncResult = (AsyncRpcResult) result;
-                            asyncResult.thenApplyWithContext(r -> filter.onResponse(r, invoker, invocation));
-                            return asyncResult;
-                        } else {
-                            return filter.onResponse(result, invoker, invocation);
+                        Result asyncResult;
+                        try {
+                            asyncResult = filter.invoke(next, invocation);
+                        } catch (Exception e) {
+                            // onError callback
+                            if (filter instanceof ListenableFilter) {
+                                Filter.Listener listener = ((ListenableFilter) filter).listener();
+                                if (listener != null) {
+                                    listener.onError(e, invoker, invocation);
+                                }
+                            }
+                            throw e;
                         }
+                        return asyncResult.thenApplyWithContext(r -> {
+                            // onResponse callback
+                            if (filter instanceof ListenableFilter) {
+                                Filter.Listener listener = ((ListenableFilter) filter).listener();
+                                if (listener != null) {
+                                    listener.onResponse(r, invoker, invocation);
+                                }
+                            }
+                            return r;
+                        });
                     }
 
                     @Override
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
index 6a562d6f6fd..aa813e1d1a5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
@@ -19,6 +19,7 @@
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.AsyncContextImpl;
 import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
@@ -26,11 +27,11 @@
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
 
 /**
  * InvokerWrapper
@@ -78,30 +79,47 @@ public boolean isAvailable() {
     public void destroy() {
     }
 
-    // TODO Unified to AsyncResult?
     @Override
     public Result invoke(Invocation invocation) throws RpcException {
-        RpcContext rpcContext = RpcContext.getContext();
         try {
-            Object obj = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
-            if (RpcUtils.isReturnTypeFuture(invocation)) {
-                return new AsyncRpcResult((CompletableFuture) obj);
-            } else if (rpcContext.isAsyncStarted()) { // ignore obj in case of RpcContext.startAsync()? always rely on user to write back.
-                return new AsyncRpcResult(((AsyncContextImpl)(rpcContext.getAsyncContext())).getInternalFuture());
-            } else {
-                return new RpcResult(obj);
-            }
+            Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
+            CompletableFuture future = wrapWithFuture(value, invocation);
+            CompletableFuture appResponseFuture = future.handle((obj, t) -> {
+                AppResponse result = new AppResponse();
+                if (t != null) {
+                    if (t instanceof CompletionException) {
+                        result.setException(t.getCause());
+                    } else {
+                        result.setException(t);
+                    }
+                } else {
+                    result.setValue(obj);
+                }
+                return result;
+            });
+            return new AsyncRpcResult(appResponseFuture, invocation);
         } catch (InvocationTargetException e) {
-            // TODO async throw exception before async thread write back, should stop asyncContext
-            if (rpcContext.isAsyncStarted() && !rpcContext.stopAsync()) {
+            if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
                 logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
             }
-            return new RpcResult(e.getTargetException());
+            return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
         } catch (Throwable e) {
             throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
         }
     }
 
+    private CompletableFuture wrapWithFuture (Object value, Invocation invocation) {
+        if (RpcContext.getContext().isAsyncStarted()) {
+            return ((AsyncContextImpl)(RpcContext.getContext().getAsyncContext())).getInternalFuture();
+        } else if (RpcUtils.isReturnTypeFuture(invocation)) {
+            if (value == null) {
+                return CompletableFuture.completedFuture(null);
+            }
+            return (CompletableFuture) value;
+        }
+        return CompletableFuture.completedFuture(value);
+    }
+
     protected abstract Object doInvoke(T proxy, String methodName, Class[] parameterTypes, Object[] arguments) throws Throwable;
 
     @Override
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
index 3cdb65c742f..1d047cdccf4 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -16,12 +16,10 @@
  */
 package org.apache.dubbo.rpc.proxy;
 
-import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
@@ -54,16 +52,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
             return invoker.equals(args[0]);
         }
 
-        return invoker.invoke(createInvocation(method, args)).recreate();
+        return invoker.invoke(new RpcInvocation(method, args)).recreate();
     }
-
-    private RpcInvocation createInvocation(Method method, Object[] args) {
-        RpcInvocation invocation = new RpcInvocation(method, args);
-        if (RpcUtils.hasFutureReturnType(method)) {
-            invocation.setAttachment(Constants.FUTURE_RETURNTYPE_KEY, "true");
-            invocation.setAttachment(Constants.ASYNC_KEY, "true");
-        }
-        return invocation;
-    }
-
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/service/GenericService.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/service/GenericService.java
index 07517d474a4..d93fa14930a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/service/GenericService.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/service/GenericService.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.rpc.service;
 
+import java.util.concurrent.CompletableFuture;
+
 /**
  * Generic service interface
  *
@@ -35,4 +37,12 @@ public interface GenericService {
      */
     Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
 
+    default CompletableFuture $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException {
+        Object object = $invoke(method, parameterTypes, args);
+        if (object instanceof CompletableFuture) {
+            return (CompletableFuture) object;
+        }
+        return CompletableFuture.completedFuture(object);
+    }
+
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java
index 923c7cb0fd1..a7f973a832a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockInvoker.java
@@ -19,18 +19,18 @@
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.ConfigUtils;
 import org.apache.dubbo.common.utils.PojoUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 
 import com.alibaba.fastjson.JSON;
 
@@ -106,7 +106,7 @@ public Result invoke(Invocation invocation) throws RpcException {
             try {
                 Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
                 Object value = parseMockValue(mock, returnTypes);
-                return new RpcResult(value);
+                return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
             } catch (Exception ew) {
                 throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
                         + ", mock:" + mock + ", url: " + url, ew);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockProtocol.java
index 9237ab955c0..ecf7a4524f5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/MockProtocol.java
@@ -38,7 +38,7 @@ public  Exporter export(Invoker invoker) throws RpcException {
     }
 
     @Override
-    public  Invoker refer(Class type, URL url) throws RpcException {
+    public  Invoker doRefer(Class type, URL url) throws RpcException {
         return new MockInvoker<>(url, type);
     }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
index a5ae76133cf..ca55dfbe8ca 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
@@ -23,15 +23,14 @@
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvokeMode;
 import org.apache.dubbo.rpc.RpcInvocation;
 
 import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
@@ -65,7 +64,6 @@ public static Class getReturnType(Invocation invocation) {
         return null;
     }
 
-    // TODO why not get return type when initialize Invocation?
     public static Type[] getReturnTypes(Invocation invocation) {
         try {
             if (invocation != null && invocation.getInvoker() != null
@@ -80,24 +78,7 @@ public static Type[] getReturnTypes(Invocation invocation) {
                     if (method.getReturnType() == void.class) {
                         return null;
                     }
-                    Class returnType = method.getReturnType();
-                    Type genericReturnType = method.getGenericReturnType();
-                    if (Future.class.isAssignableFrom(returnType)) {
-                        if (genericReturnType instanceof ParameterizedType) {
-                            Type actualArgType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
-                            if (actualArgType instanceof ParameterizedType) {
-                                returnType = (Class) ((ParameterizedType) actualArgType).getRawType();
-                                genericReturnType = actualArgType;
-                            } else {
-                                returnType = (Class) actualArgType;
-                                genericReturnType = returnType;
-                            }
-                        } else {
-                            returnType = null;
-                            genericReturnType = null;
-                        }
-                    }
-                    return new Type[]{returnType, genericReturnType};
+                    return ReflectUtils.getReturnTypes(method);
                 }
             }
         } catch (Throwable t) {
@@ -184,11 +165,22 @@ public static boolean isAsync(URL url, Invocation inv) {
     }
 
     public static boolean isReturnTypeFuture(Invocation inv) {
-        return Boolean.TRUE.toString().equals(inv.getAttachment(Constants.FUTURE_RETURNTYPE_KEY));
+        Class clazz = getReturnType(inv);
+        return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv);
+    }
+
+    public static InvokeMode getInvokeMode(URL url, Invocation inv) {
+        if (isReturnTypeFuture(inv)) {
+            return InvokeMode.FUTURE;
+        } else if (isAsync(url, inv)) {
+            return InvokeMode.ASYNC;
+        } else {
+            return InvokeMode.SYNC;
+        }
     }
 
-    public static boolean hasFutureReturnType(Method method) {
-        return CompletableFuture.class.isAssignableFrom(method.getReturnType());
+    public static boolean isGenericAsync(Invocation inv) {
+        return Constants.$INVOKE_ASYNC.equals(inv.getMethodName());
     }
 
     public static boolean isOneway(URL url, Invocation inv) {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcResultTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java
similarity index 100%
rename from dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcResultTest.java
rename to dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ActiveLimitFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ActiveLimitFilterTest.java
index 6d085f70617..9c749c983c5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ActiveLimitFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ActiveLimitFilterTest.java
@@ -17,9 +17,9 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.support.BlockMyInvoker;
@@ -35,13 +35,14 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * ActiveLimitFilterTest.java
  */
 public class ActiveLimitFilterTest {
 
-    Filter activeLimitFilter = new ActiveLimitFilter();
+    ActiveLimitFilter activeLimitFilter = new ActiveLimitFilter();
 
     @Test
     public void testInvokeNoActives() {
@@ -97,7 +98,7 @@ public void run() {
     }
 
     @Test
-    public void testInvokeTimeOut() {
+    public void testInvokeTimeOut() throws Exception {
         int totalThread = 100;
         int maxActives = 10;
         long timeout = 1;
@@ -120,9 +121,14 @@ public void run() {
                             e.printStackTrace();
                         }
                         try {
-                            activeLimitFilter.invoke(invoker, invocation);
+                            Result asyncResult = activeLimitFilter.invoke(invoker, invocation);
+                            Result result = asyncResult.get();
+                            activeLimitFilter.listener().onResponse(result, invoker, invocation);
                         } catch (RpcException expected) {
                             count.incrementAndGet();
+//                            activeLimitFilter.listener().onError(expected, invoker, invocation);
+                        } catch (Exception e) {
+                            fail();
                         }
                     } finally {
                         latchBlocking.countDown();
@@ -142,7 +148,7 @@ public void run() {
     }
 
     @Test
-    public void testInvokeNotTimeOut() {
+    public void testInvokeNotTimeOut() throws Exception {
         int totalThread = 100;
         int maxActives = 10;
         long timeout = 1000;
@@ -163,9 +169,14 @@ public void run() {
                             e.printStackTrace();
                         }
                         try {
-                            activeLimitFilter.invoke(invoker, invocation);
+                            Result asyncResult = activeLimitFilter.invoke(invoker, invocation);
+                            Result result = asyncResult.get();
+                            activeLimitFilter.listener().onResponse(result, invoker, invocation);
                         } catch (RpcException expected) {
                             count.incrementAndGet();
+                            activeLimitFilter.listener().onError(expected, invoker, invocation);
+                        } catch (Exception e) {
+                            fail();
                         }
                     } finally {
                         latchBlocking.countDown();
@@ -208,6 +219,7 @@ public void testInvokeRuntimeExceptionWithActiveCountMatch() {
         try {
             activeLimitFilter.invoke(invoker, invocation);
         } catch (RuntimeException ex) {
+            activeLimitFilter.listener().onError(ex, invoker, invocation);
             int afterExceptionActiveCount = count.getActive();
             assertEquals(beforeExceptionActiveCount, afterExceptionActiveCount, "After exception active count should be same");
         }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/CompatibleFilterFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/CompatibleFilterFilterTest.java
index b91c92d1640..e16dfbd8f61 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/CompatibleFilterFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/CompatibleFilterFilterTest.java
@@ -17,13 +17,14 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.DemoService;
 import org.apache.dubbo.rpc.support.Type;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
@@ -37,7 +38,7 @@
  * CompatibleFilterTest.java
  */
 public class CompatibleFilterFilterTest {
-    private Filter compatibleFilter = new CompatibleFilter();
+    private CompatibleFilter compatibleFilter = new CompatibleFilter();
     private Invocation invocation;
     private Invoker invoker;
 
@@ -56,7 +57,7 @@ public void testInvokerGeneric() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@@ -76,7 +77,7 @@ public void testResulthasException() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setException(new RuntimeException());
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
@@ -88,7 +89,7 @@ public void testResulthasException() {
     }
 
     @Test
-    public void testInvokerJsonPojoSerialization() {
+    public void testInvokerJsonPojoSerialization() throws Exception {
         invocation = mock(Invocation.class);
         given(invocation.getMethodName()).willReturn("enumlength");
         given(invocation.getParameterTypes()).willReturn(new Class[]{Type[].class});
@@ -97,18 +98,20 @@ public void testInvokerJsonPojoSerialization() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
-        given(invoker.invoke(invocation)).willReturn(result);
+        given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(result, invocation));
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&serialization=json");
         given(invoker.getUrl()).willReturn(url);
 
-        Result filterResult = compatibleFilter.invoke(invoker, invocation);
-        assertEquals(Type.High, filterResult.getValue());
+        Result asyncResult = compatibleFilter.invoke(invoker, invocation);
+        Result rpcResult = asyncResult.get();
+        compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
+        assertEquals(Type.High, rpcResult.getValue());
     }
 
     @Test
-    public void testInvokerNonJsonEnumSerialization() {
+    public void testInvokerNonJsonEnumSerialization() throws Exception {
         invocation = mock(Invocation.class);
         given(invocation.getMethodName()).willReturn("enumlength");
         given(invocation.getParameterTypes()).willReturn(new Class[]{Type[].class});
@@ -117,14 +120,16 @@ public void testInvokerNonJsonEnumSerialization() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
-        given(invoker.invoke(invocation)).willReturn(result);
+        given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(result, invocation));
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
         given(invoker.getUrl()).willReturn(url);
 
-        Result filterResult = compatibleFilter.invoke(invoker, invocation);
-        assertEquals(Type.High, filterResult.getValue());
+        Result asyncResult = compatibleFilter.invoke(invoker, invocation);
+        Result rpcResult = asyncResult.get();
+        compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
+        assertEquals(Type.High, rpcResult.getValue());
     }
 
     @Test
@@ -137,7 +142,7 @@ public void testInvokerNonJsonNonPojoSerialization() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue(new String[]{"High"});
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@@ -157,7 +162,7 @@ public void testInvokerNonJsonPojoSerialization() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("hello");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ConsumerContextFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ConsumerContextFilterTest.java
index a906af47be2..93635528338 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ConsumerContextFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ConsumerContextFilterTest.java
@@ -21,6 +21,7 @@
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.support.DemoService;
 import org.apache.dubbo.rpc.support.MockInvocation;
@@ -41,11 +42,13 @@ public void testSetContext() {
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
         Invoker invoker = new MyInvoker(url);
         Invocation invocation = new MockInvocation();
-        consumerContextFilter.invoke(invoker, invocation);
-        assertEquals(invoker, RpcContext.getContext().getInvoker());
-        assertEquals(invocation, RpcContext.getContext().getInvocation());
-        assertEquals(NetUtils.getLocalHost() + ":0", RpcContext.getContext().getLocalAddressString());
-        assertEquals("test:11", RpcContext.getContext().getRemoteAddressString());
-
+        Result asyncResult = consumerContextFilter.invoke(invoker, invocation);
+        asyncResult.thenApplyWithContext(result -> {
+            assertEquals(invoker, RpcContext.getContext().getInvoker());
+            assertEquals(invocation, RpcContext.getContext().getInvocation());
+            assertEquals(NetUtils.getLocalHost() + ":0", RpcContext.getContext().getLocalAddressString());
+            assertEquals("test:11", RpcContext.getContext().getRemoteAddressString());
+            return result;
+        });
     }
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ContextFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ContextFilterTest.java
index c753c7e0e2e..506c10db5d5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ContextFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ContextFilterTest.java
@@ -17,12 +17,12 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.DemoService;
 import org.apache.dubbo.rpc.support.MockInvocation;
 import org.apache.dubbo.rpc.support.MyInvoker;
@@ -55,7 +55,7 @@ public void testSetContext() {
         invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/EchoFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/EchoFilterTest.java
index 02f367b8a8e..3befdc77df9 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/EchoFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/EchoFilterTest.java
@@ -17,11 +17,11 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.DemoService;
 
 import org.junit.jupiter.api.Test;
@@ -46,7 +46,7 @@ public void testEcho() {
         Invoker invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@@ -68,7 +68,7 @@ public void testNonEcho() {
         Invoker invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
index f40f8776e45..964e06ecb15 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
@@ -17,13 +17,13 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.DemoService;
 import org.apache.dubbo.rpc.support.LocalException;
 
@@ -33,6 +33,8 @@
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
+import java.util.concurrent.CompletableFuture;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
@@ -51,18 +53,20 @@ public void testRpcException() {
         RpcContext.getContext().setRemoteAddress("127.0.0.1", 1234);
         RpcException exception = new RpcException("TestRpcException");
 
-        ExceptionFilter exceptionFilter = new ExceptionFilter(logger);
+        ExceptionFilter exceptionFilter = new ExceptionFilter();
         RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"});
         Invoker invoker = mock(Invoker.class);
         given(invoker.getInterface()).willReturn(DemoService.class);
         given(invoker.invoke(eq(invocation))).willThrow(exception);
 
-
         try {
             exceptionFilter.invoke(invoker, invocation);
         } catch (RpcException e) {
             assertEquals("TestRpcException", e.getMessage());
+            ((ExceptionFilter.ExceptionListener) exceptionFilter.listener()).setLogger(logger);
+            exceptionFilter.listener().onError(e, invoker, invocation);
         }
+
         Mockito.verify(logger).error(eq("Got unchecked and undeclared exception which called by 127.0.0.1. service: "
                 + DemoService.class.getName() + ", method: sayHello, exception: "
                 + RpcException.class.getName() + ": TestRpcException"), eq(exception));
@@ -76,7 +80,7 @@ public void testJavaException() {
         ExceptionFilter exceptionFilter = new ExceptionFilter();
         RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"});
 
-        RpcResult rpcResult = new RpcResult();
+        AppResponse rpcResult = new AppResponse();
         rpcResult.setException(new IllegalArgumentException("java"));
 
         Invoker invoker = mock(Invoker.class);
@@ -96,7 +100,7 @@ public void testRuntimeException() {
         ExceptionFilter exceptionFilter = new ExceptionFilter();
         RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"});
 
-        RpcResult rpcResult = new RpcResult();
+        AppResponse rpcResult = new AppResponse();
         rpcResult.setException(new LocalException("localException"));
 
         Invoker invoker = mock(Invoker.class);
@@ -111,27 +115,28 @@ public void testRuntimeException() {
 
     @SuppressWarnings("unchecked")
     @Test
-    public void testConvertToRunTimeException() {
+    public void testConvertToRunTimeException() throws Exception {
 
         ExceptionFilter exceptionFilter = new ExceptionFilter();
         RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"});
 
-        RpcResult rpcResult = new RpcResult();
-        rpcResult.setException(new HessianException("hessian"));
+        AppResponse mockRpcResult = new AppResponse();
+        mockRpcResult.setException(new HessianException("hessian"));
+        Result mockAsyncResult = new AsyncRpcResult(CompletableFuture.completedFuture(mockRpcResult), invocation);
+
 
         Invoker invoker = mock(Invoker.class);
-        when(invoker.invoke(invocation)).thenReturn(rpcResult);
+        when(invoker.invoke(invocation)).thenReturn(mockAsyncResult);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
-        Result newResult = exceptionFilter.invoke(invoker, invocation);
-
-        newResult = exceptionFilter.onResponse(newResult, invoker, invocation);
+        Result asyncResult = exceptionFilter.invoke(invoker, invocation);
 
-        Assertions.assertFalse(newResult.getException() instanceof HessianException);
+        Result rpcResult = asyncResult.get();
+        exceptionFilter.listener().onResponse(rpcResult, invoker, invocation);
 
-        Assertions.assertEquals(newResult.getException().getClass(), RuntimeException.class);
-        Assertions.assertEquals(newResult.getException().getMessage(), StringUtils.toString(rpcResult.getException()));
+        Assertions.assertFalse(rpcResult.getException() instanceof HessianException);
 
+        Assertions.assertEquals(rpcResult.getException().getClass(), RuntimeException.class);
     }
 
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilterTest.java
index 2fab7894e32..4cbc280275d 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExecuteLimitFilterTest.java
@@ -17,11 +17,11 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.support.BlockMyInvoker;
 
@@ -43,7 +43,7 @@ public class ExecuteLimitFilterTest {
     @Test
     public void testNoExecuteLimitInvoke() throws Exception {
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
         when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1"));
 
         Invocation invocation = Mockito.mock(Invocation.class);
@@ -56,7 +56,7 @@ public void testNoExecuteLimitInvoke() throws Exception {
     @Test
     public void testExecuteLimitInvoke() throws Exception {
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
         when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&executes=10"));
 
         Invocation invocation = Mockito.mock(Invocation.class);
@@ -82,6 +82,7 @@ public void testExecuteLimitInvokeWitException() throws Exception {
             executeLimitFilter.invoke(invoker, invocation);
         } catch (Exception e) {
             Assertions.assertTrue(e instanceof RpcException);
+            executeLimitFilter.listener().onError(e, invoker, invocation);
         }
         Assertions.assertEquals(1, RpcStatus.getStatus(url, invocation.getMethodName()).getFailed());
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
index bd680e7502e..f2358ddaeca 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
@@ -18,7 +18,13 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.*;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.service.GenericService;
 import org.apache.dubbo.rpc.support.DemoService;
 import org.apache.dubbo.rpc.support.Person;
@@ -52,14 +58,16 @@ public void testInvokeWithDefault() throws Exception {
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1");
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(AsyncRpcResult.newDefaultAsyncResult(new Person("person", 10), invocation));
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
-        Result result = genericFilter.invoke(invoker, invocation);
+        Result asyncResult = genericFilter.invoke(invoker, invocation);
 
-        Assertions.assertEquals(HashMap.class, result.getValue().getClass());
-        Assertions.assertEquals(10, ((HashMap) result.getValue()).get("age"));
+        Result rpcResult = asyncResult.get();
+        genericFilter.listener().onResponse(rpcResult, invoker, invocation);
+        Assertions.assertEquals(HashMap.class, rpcResult.getValue().getClass());
+        Assertions.assertEquals(10, ((HashMap) rpcResult.getValue()).get("age"));
 
     }
 
@@ -79,7 +87,7 @@ public void testInvokeWithJavaException() throws Exception {
             URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                     "accesslog=true&group=dubbo&version=1.1");
             Invoker invoker = Mockito.mock(Invoker.class);
-            when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
+            when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
             when(invoker.getUrl()).thenReturn(url);
             when(invoker.getInterface()).thenReturn(DemoService.class);
 
@@ -102,7 +110,7 @@ public void testInvokeWithJavaException() throws Exception {
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1");
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
@@ -126,7 +134,7 @@ public void testInvokeWithMethodArgumentSizeIsNot3() {
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1");
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
index 43930ef6605..469632b9ec3 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
@@ -18,11 +18,12 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.service.GenericException;
 import org.apache.dubbo.rpc.service.GenericService;
 import org.apache.dubbo.rpc.support.DemoService;
@@ -35,6 +36,7 @@
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.when;
@@ -58,11 +60,14 @@ public void testInvoke() throws Exception {
         person.put("name", "dubbo");
         person.put("age", 10);
 
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(person));
+        AppResponse mockRpcResult = new AppResponse(person);
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AsyncRpcResult(CompletableFuture.completedFuture(mockRpcResult), invocation));
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
-        Result result = genericImplFilter.invoke(invoker, invocation);
+        Result asyncResult = genericImplFilter.invoke(invoker, invocation);
+        Result result = asyncResult.get();
+        genericImplFilter.listener().onResponse(result, invoker, invocation);
 
         Assertions.assertEquals(Person.class, result.getValue().getClass());
         Assertions.assertEquals(10, ((Person) result.getValue()).getAge());
@@ -78,12 +83,14 @@ public void testInvokeWithException() throws Exception {
                 "accesslog=true&group=dubbo&version=1.1&generic=true");
         Invoker invoker = Mockito.mock(Invoker.class);
 
-        when(invoker.invoke(any(Invocation.class))).thenReturn(
-                new RpcResult(new GenericException(new RuntimeException("failed"))));
+        AppResponse mockRpcResult = new AppResponse(new GenericException(new RuntimeException("failed")));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AsyncRpcResult(CompletableFuture.completedFuture(mockRpcResult), invocation));
         when(invoker.getUrl()).thenReturn(url);
         when(invoker.getInterface()).thenReturn(DemoService.class);
 
-        Result result = genericImplFilter.invoke(invoker, invocation);
+        Result asyncResult = genericImplFilter.invoke(invoker, invocation);
+        Result result = asyncResult.get();
+        genericImplFilter.listener().onResponse(result, invoker, invocation);
         Assertions.assertEquals(RuntimeException.class, result.getException().getClass());
 
     }
@@ -103,7 +110,7 @@ public void testInvokeWithException() throws Exception {
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1&generic=true");
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
         when(invoker.getUrl()).thenReturn(url);
 
         genericImplFilter.invoke(invoker, invocation);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TimeoutFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TimeoutFilterTest.java
index f99a0f35480..be853653335 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TimeoutFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TimeoutFilterTest.java
@@ -17,10 +17,10 @@
 package org.apache.dubbo.rpc.filter;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.BlockMyInvoker;
 
 import org.junit.jupiter.api.Assertions;
@@ -39,7 +39,7 @@ public void testInvokeWithoutTimeout() throws Exception {
         int timeout = 3000;
 
         Invoker invoker = Mockito.mock(Invoker.class);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
         when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&timeout=" + timeout));
 
         Invocation invocation = Mockito.mock(Invocation.class);
@@ -60,7 +60,7 @@ public void testInvokeWithTimeout() throws Exception {
         when(invocation.getMethodName()).thenReturn("testInvokeWithTimeout");
 
         Result result = timeoutFilter.invoke(invoker, invocation);
-        Assertions.assertEquals("alibaba", result.getValue());
+        Assertions.assertEquals("Dubbo", result.getValue());
 
     }
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
index fea5c300e07..5b86459624a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/TokenFilterTest.java
@@ -18,11 +18,11 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -45,7 +45,7 @@ public void testInvokeWithToken() throws Exception {
         Invoker invoker = Mockito.mock(Invoker.class);
         URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
         when(invoker.getUrl()).thenReturn(url);
-        when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+        when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
 
         Map attachments = new HashMap();
         attachments.put(Constants.TOKEN_KEY, token);
@@ -64,7 +64,7 @@ public void testInvokeWithWrongToken() throws Exception {
             Invoker invoker = Mockito.mock(Invoker.class);
             URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
             when(invoker.getUrl()).thenReturn(url);
-            when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+            when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
 
             Map attachments = new HashMap();
             attachments.put(Constants.TOKEN_KEY, "wrongToken");
@@ -83,7 +83,7 @@ public void testInvokeWithoutToken() throws Exception {
             Invoker invoker = Mockito.mock(Invoker.class);
             URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
             when(invoker.getUrl()).thenReturn(url);
-            when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
+            when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
 
             Invocation invocation = Mockito.mock(Invocation.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/BlockMyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/BlockMyInvoker.java
index 28baacbf611..7fec8d49fdb 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/BlockMyInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/BlockMyInvoker.java
@@ -17,10 +17,11 @@
 package org.apache.dubbo.rpc.support;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 
 public class BlockMyInvoker extends MyInvoker {
 
@@ -38,19 +39,18 @@ public BlockMyInvoker(URL url, boolean hasException, long blockTime) {
 
     @Override
     public Result invoke(Invocation invocation) throws RpcException {
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         if (hasException == false) {
             try {
                 Thread.sleep(blockTime);
             } catch (InterruptedException e) {
             }
-            result.setValue("alibaba");
-            return result;
+            result.setValue("Dubbo");
         } else {
             result.setException(new RuntimeException("mocked exception"));
-            return result;
         }
 
+        return AsyncRpcResult.newDefaultAsyncResult(result, invocation);
     }
 
     public long getBlockTime() {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
index 5b7261de7b4..13a2ae244f4 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
@@ -51,6 +51,16 @@ public Map getAttachments() {
         return attachments;
     }
 
+    @Override
+    public void setAttachment(String key, String value) {
+
+    }
+
+    @Override
+    public void setAttachmentIfAbsent(String key, String value) {
+
+    }
+
     public Invoker getInvoker() {
         return null;
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java
index eb944c9bc1a..1b74e6bc41b 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java
@@ -17,11 +17,14 @@
 package org.apache.dubbo.rpc.support;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
+
+import java.util.concurrent.CompletableFuture;
 
 /**
  * MockInvoker.java
@@ -58,15 +61,14 @@ public boolean isAvailable() {
     }
 
     public Result invoke(Invocation invocation) throws RpcException {
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         if (hasException == false) {
             result.setValue("alibaba");
-            return result;
         } else {
             result.setException(new RuntimeException("mocked exception"));
-            return result;
         }
 
+        return new AsyncRpcResult(CompletableFuture.completedFuture(result), invocation);
     }
 
     @Override
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
index 5eb4dd9205f..0c92aea5bf5 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -31,6 +31,7 @@
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.AsyncToSyncInvoker;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -144,7 +145,7 @@ private static Object referOrDestroyCallbackService(Channel channel, URL url, Cl
                 if (!isInstancesOverLimit(channel, referurl, clazz.getName(), instid, true)) {
                     @SuppressWarnings("rawtypes")
                     Invoker invoker = new ChannelWrappedInvoker(clazz, channel, referurl, String.valueOf(instid));
-                    proxy = PROXY_FACTORY.getProxy(invoker);
+                    proxy = PROXY_FACTORY.getProxy(new AsyncToSyncInvoker<>(invoker));
                     channel.setAttribute(proxyCacheKey, proxy);
                     channel.setAttribute(invokerCacheKey, invoker);
                     increaseInstanceCount(channel, countkey);
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
index 4c78c1cceb9..5a22127d3ef 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
@@ -25,16 +25,20 @@
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
 import org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
 import org.apache.dubbo.remoting.transport.ClientDelegate;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
+import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
 
 /**
+ * Server push uses this Invoker to continuously push data to client.
  * Wrap the existing invoker on the channel.
  */
 class ChannelWrappedInvoker extends AbstractInvoker {
@@ -58,15 +62,12 @@ protected Result doInvoke(Invocation invocation) throws Throwable {
         inv.setAttachment(Constants.CALLBACK_SERVICE_KEY, serviceKey);
 
         try {
-            if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { // may have concurrency issue
+            if (RpcUtils.isOneway(getUrl(), inv)) { // may have concurrency issue
                 currentClient.send(inv, getUrl().getMethodParameter(invocation.getMethodName(), Constants.SENT_KEY, false));
-                return new RpcResult();
-            }
-            int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
-            if (timeout > 0) {
-                return (Result) currentClient.request(inv, timeout).get();
+                return AsyncRpcResult.newDefaultAsyncResult(invocation);
             } else {
-                return (Result) currentClient.request(inv).get();
+                CompletableFuture appResponseFuture = currentClient.request(inv).thenApply(obj -> (AppResponse) obj);
+                return new AsyncRpcResult(appResponseFuture, inv);
             }
         } catch (RpcException e) {
             throw e;
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
index fac7c64a6ca..e6d3f84a781 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java
@@ -28,8 +28,8 @@
 import org.apache.dubbo.remoting.Decodeable;
 import org.apache.dubbo.remoting.exchange.Response;
 import org.apache.dubbo.remoting.transport.CodecSupport;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.io.IOException;
@@ -38,7 +38,7 @@
 import java.lang.reflect.Type;
 import java.util.Map;
 
-public class DecodeableRpcResult extends RpcResult implements Codec, Decodeable {
+public class DecodeableRpcResult extends AppResponse implements Codec, Decodeable {
 
     private static final Logger log = LoggerFactory.getLogger(DecodeableRpcResult.class);
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodec.java
index 4093eb266d6..40c6e2912d3 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodec.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodec.java
@@ -24,8 +24,8 @@
 import org.apache.dubbo.remoting.exchange.Request;
 import org.apache.dubbo.remoting.exchange.Response;
 import org.apache.dubbo.remoting.exchange.support.MultiMessage;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 
 import java.io.IOException;
 
@@ -75,7 +75,7 @@ private void logMessageLength(Object result, int bytes) {
             }
         } else if (result instanceof Response) {
             try {
-                ((RpcResult) ((Response) result).getResult()).setAttachment(
+                ((AppResponse) ((Response) result).getResult()).setAttachment(
                         Constants.OUTPUT_KEY, String.valueOf(bytes));
             } catch (Throwable e) {
                 /* ignore */
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
index 5f2c61b1cae..d8513a31806 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java
@@ -23,7 +23,7 @@
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.TimeoutException;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
-import org.apache.dubbo.remoting.exchange.ResponseFuture;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -31,12 +31,11 @@
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
-import org.apache.dubbo.rpc.SimpleAsyncRpcResult;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -80,32 +79,17 @@ protected Result doInvoke(final Invocation invocation) throws Throwable {
             currentClient = clients[index.getAndIncrement() % clients.length];
         }
         try {
-            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
-            boolean isAsyncFuture = RpcUtils.isReturnTypeFuture(inv);
             boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
             int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
             if (isOneway) {
                 boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                 currentClient.send(inv, isSent);
                 RpcContext.getContext().setFuture(null);
-                return new RpcResult();
-            } else if (isAsync) {
-                ResponseFuture future = currentClient.request(inv, timeout);
-                // For compatibility
-                FutureAdapter futureAdapter = new FutureAdapter<>(future);
-                RpcContext.getContext().setFuture(futureAdapter);
-
-                Result result;
-                if (isAsyncFuture) {
-                    // register resultCallback, sometimes we need the async result being processed by the filter chain.
-                    result = new AsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
-                } else {
-                    result = new SimpleAsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
-                }
-                return result;
+                return AsyncRpcResult.newDefaultAsyncResult(invocation);
             } else {
-                RpcContext.getContext().setFuture(null);
-                return (Result) currentClient.request(inv, timeout).get();
+                CompletableFuture appResponseFuture = currentClient.request(inv, timeout).thenApply(obj -> (AppResponse) obj);
+                RpcContext.getContext().setFuture(new FutureAdapter(appResponseFuture));
+                return new AsyncRpcResult(appResponseFuture, inv);
             }
         } catch (TimeoutException e) {
             throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
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 77443016cfb..926a940cd5d 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
@@ -37,7 +37,6 @@
 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.AsyncRpcResult;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -59,6 +58,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
 
 /**
  * dubbo protocol support.
@@ -123,16 +123,9 @@ public CompletableFuture reply(ExchangeChannel channel, Object message)
                     return null;
                 }
             }
-            RpcContext rpcContext = RpcContext.getContext();
-            rpcContext.setRemoteAddress(channel.getRemoteAddress());
+            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
             Result result = invoker.invoke(inv);
-
-            if (result instanceof AsyncRpcResult) {
-                return ((AsyncRpcResult) result).getResultFuture().thenApply(r -> (Object) r);
-
-            } else {
-                return CompletableFuture.completedFuture(result);
-            }
+            return result.thenApply(Function.identity());
         }
 
         @Override
@@ -382,7 +375,7 @@ private void optimizeSerialization(URL url) throws RpcException {
     }
 
     @Override
-    public  Invoker refer(Class serviceType, URL url) throws RpcException {
+    public  Invoker doRefer(Class serviceType, URL url) throws RpcException {
         optimizeSerialization(url);
 
         // create rpc invoker.
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/FutureAdapter.java
index 28645410d42..9fc47f6f9c9 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/FutureAdapter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/FutureAdapter.java
@@ -16,12 +16,11 @@
  */
 package org.apache.dubbo.rpc.protocol.dubbo;
 
-import org.apache.dubbo.remoting.exchange.ResponseCallback;
-import org.apache.dubbo.remoting.exchange.ResponseFuture;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -31,45 +30,35 @@
  */
 public class FutureAdapter extends CompletableFuture {
 
-    private final ResponseFuture future;
     private CompletableFuture resultFuture;
 
-    public FutureAdapter(ResponseFuture future) {
-        this.future = future;
-        this.resultFuture = new CompletableFuture<>();
-        future.setCallback(new ResponseCallback() {
-            @Override
-            public void done(Object response) {
-                Result result = (Result) response;
-                FutureAdapter.this.resultFuture.complete(result);
-                V value = null;
-                try {
-                    value = (V) result.recreate();
-                } catch (Throwable t) {
-                    FutureAdapter.this.completeExceptionally(t);
+    public FutureAdapter(CompletableFuture future) {
+        this.resultFuture = future;
+        future.whenComplete((result, t) -> {
+            if (t != null) {
+                if (t instanceof CompletionException) {
+                    t = t.getCause();
+                }
+                this.completeExceptionally(t);
+            } else {
+                if (result.hasException()) {
+                    this.completeExceptionally(result.getException());
+                } else {
+                    this.complete((V)result.getValue());
                 }
-                FutureAdapter.this.complete(value);
-            }
-
-            @Override
-            public void caught(Throwable exception) {
-                FutureAdapter.this.completeExceptionally(exception);
             }
         });
     }
 
-    public ResponseFuture getFuture() {
-        return future;
-    }
-
+    // TODO figure out the meaning of cancel in DefaultFuture.
     @Override
     public boolean cancel(boolean mayInterruptIfRunning) {
-        return false;
+        return resultFuture.cancel(mayInterruptIfRunning);
     }
 
     @Override
     public boolean isCancelled() {
-        return false;
+        return resultFuture.isCancelled();
     }
 
     @Override
@@ -101,15 +90,4 @@ public V get(long timeout, TimeUnit unit) throws InterruptedException, Execution
         }
     }
 
-    /**
-     * FIXME
-     * This method has no need open to the the end user.
-     * Mostly user use RpcContext.getFuture() to refer the instance of this class, so the user will get a CompletableFuture, this method will rarely be noticed.
-     *
-     * @return
-     */
-    public CompletableFuture getResultFuture() {
-        return resultFuture;
-    }
-
 }
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
index f2bc4534fdc..c11ffca421d 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
@@ -27,9 +27,9 @@
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
 import org.apache.dubbo.remoting.exchange.ExchangeHandler;
 import org.apache.dubbo.remoting.exchange.Exchangers;
-import org.apache.dubbo.remoting.exchange.ResponseFuture;
 
 import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -84,7 +84,7 @@ private void initClient() throws RemotingException {
     }
 
     @Override
-    public ResponseFuture request(Object request) throws RemotingException {
+    public CompletableFuture request(Object request) throws RemotingException {
         warning();
         initClient();
         return client.request(request);
@@ -105,7 +105,7 @@ public InetSocketAddress getRemoteAddress() {
     }
 
     @Override
-    public ResponseFuture request(Object request, int timeout) throws RemotingException {
+    public CompletableFuture request(Object request, int timeout) throws RemotingException {
         warning();
         initClient();
         return client.request(request, timeout);
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
index faafe082852..cb589e228ec 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
@@ -24,9 +24,9 @@
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
 import org.apache.dubbo.remoting.exchange.ExchangeHandler;
-import org.apache.dubbo.remoting.exchange.ResponseFuture;
 
 import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -52,7 +52,7 @@ public void reset(URL url) {
     }
 
     @Override
-    public ResponseFuture request(Object request) throws RemotingException {
+    public CompletableFuture request(Object request) throws RemotingException {
         return client.request(request);
     }
 
@@ -72,7 +72,7 @@ public ChannelHandler getChannelHandler() {
     }
 
     @Override
-    public ResponseFuture request(Object request, int timeout) throws RemotingException {
+    public CompletableFuture request(Object request, int timeout) throws RemotingException {
         return client.request(request, timeout);
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
index 98283f450c8..737e7390048 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -20,10 +20,9 @@
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.rpc.AsyncRpcResult;
-import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -37,10 +36,14 @@
  * EventFilter
  */
 @Activate(group = Constants.CONSUMER)
-public class FutureFilter implements Filter {
+public class FutureFilter extends ListenableFilter {
 
     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
 
+    public FutureFilter() {
+        super.listener = new FutureListener();
+    }
+
     @Override
     public Result invoke(final Invoker invoker, final Invocation invocation) throws RpcException {
         fireInvokeCallback(invoker, invocation);
@@ -49,37 +52,6 @@ public Result invoke(final Invoker invoker, final Invocation invocation) thro
         return invoker.invoke(invocation);
     }
 
-    @Override
-    public Result onResponse(Result result, Invoker invoker, Invocation invocation) {
-        if (result instanceof AsyncRpcResult) {
-            AsyncRpcResult asyncResult = (AsyncRpcResult) result;
-            asyncResult.thenApplyWithContext(r -> {
-                asyncCallback(invoker, invocation, r);
-                return r;
-            });
-            return asyncResult;
-        } else {
-            syncCallback(invoker, invocation, result);
-            return result;
-        }
-    }
-
-    private void syncCallback(final Invoker invoker, final Invocation invocation, final Result result) {
-        if (result.hasException()) {
-            fireThrowCallback(invoker, invocation, result.getException());
-        } else {
-            fireReturnCallback(invoker, invocation, result.getValue());
-        }
-    }
-
-    private void asyncCallback(final Invoker invoker, final Invocation invocation, Result result) {
-        if (result.hasException()) {
-            fireThrowCallback(invoker, invocation, result.getException());
-        } else {
-            fireReturnCallback(invoker, invocation, result.getValue());
-        }
-    }
-
     private void fireInvokeCallback(final Invoker invoker, final Invocation invocation) {
         final ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = getAsyncMethodInfo(invoker, invocation);
         if (asyncMethodInfo == null) {
@@ -224,4 +196,20 @@ private ConsumerMethodModel.AsyncMethodInfo getAsyncMethodInfo(Invoker invoke
 
         return asyncMethodInfo;
     }
+
+    class FutureListener implements Listener {
+        @Override
+        public void onResponse(Result result, Invoker invoker, Invocation invocation) {
+            if (result.hasException()) {
+                fireThrowCallback(invoker, invocation, result.getException());
+            } else {
+                fireReturnCallback(invoker, invocation, result.getValue());
+            }
+        }
+
+        @Override
+        public void onError(Throwable t, Invoker invoker, Invocation invocation) {
+
+        }
+    }
 }
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
index 56fe5b8e564..3998026b70b 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
@@ -16,7 +16,6 @@
  */
 package org.apache.dubbo.rpc.protocol.dubbo.telnet;
 
-import com.alibaba.fastjson.JSON;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
@@ -24,11 +23,13 @@
 import org.apache.dubbo.remoting.Channel;
 import org.apache.dubbo.remoting.telnet.TelnetHandler;
 import org.apache.dubbo.remoting.telnet.support.Help;
-import org.apache.dubbo.rpc.RpcResult;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ProviderMethodModel;
 import org.apache.dubbo.rpc.model.ProviderModel;
 
+import com.alibaba.fastjson.JSON;
+
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -124,7 +125,7 @@ public String telnet(Channel channel, String message) {
                     Object[] array = realize(list.toArray(), invokeMethod.getParameterTypes(),
                             invokeMethod.getGenericParameterTypes());
                     long start = System.currentTimeMillis();
-                    RpcResult result = new RpcResult();
+                    AppResponse result = new AppResponse();
                     try {
                         Object o = invokeMethod.invoke(selectedProvider.getServiceInstance(), array);
                         result.setValue(o);
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
index e2b71d05432..9a965cd80ea 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
@@ -19,17 +19,18 @@
 
 import org.apache.dubbo.common.Constants;
 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.NetUtils;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.protocol.AsyncToSyncInvoker;
 import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -62,7 +63,7 @@ public void test_Normal_available() {
         URL url = URL.valueOf("dubbo://127.0.0.1:20883/org.apache.dubbo.rpc.protocol.dubbo.IDemoService");
         ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
 
-        DubboInvoker invoker = (DubboInvoker) protocol.refer(IDemoService.class, url);
+        DubboInvoker invoker = (DubboInvoker) protocol.doRefer(IDemoService.class, url);
         Assertions.assertEquals(true, invoker.isAvailable());
         invoker.destroy();
         Assertions.assertEquals(false, invoker.isAvailable());
@@ -73,7 +74,7 @@ public void test_Normal_ChannelReadOnly() throws Exception {
         URL url = URL.valueOf("dubbo://127.0.0.1:20883/org.apache.dubbo.rpc.protocol.dubbo.IDemoService");
         ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
 
-        DubboInvoker invoker = (DubboInvoker) protocol.refer(IDemoService.class, url);
+        DubboInvoker invoker = (DubboInvoker) protocol.doRefer(IDemoService.class, url);
         Assertions.assertEquals(true, invoker.isAvailable());
 
         getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
@@ -91,7 +92,7 @@ public void test_normal_channel_close_wait_gracefully() throws Exception {
         Exporter exporter = ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
         Exporter exporter0 = ProtocolUtils.export(new DemoServiceImpl0(), IDemoService.class, url);
 
-        DubboInvoker invoker = (DubboInvoker) protocol.refer(IDemoService.class, url);
+        DubboInvoker invoker = (DubboInvoker) protocol.doRefer(IDemoService.class, url);
 
         long start = System.currentTimeMillis();
 
@@ -113,7 +114,7 @@ public void test_NoInvokers() throws Exception {
         URL url = URL.valueOf("dubbo://127.0.0.1:20883/org.apache.dubbo.rpc.protocol.dubbo.IDemoService?connections=1");
         ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
 
-        DubboInvoker invoker = (DubboInvoker) protocol.refer(IDemoService.class, url);
+        DubboInvoker invoker = (DubboInvoker) protocol.doRefer(IDemoService.class, url);
 
         ExchangeClient[] clients = getClients(invoker);
         clients[0].close();
@@ -126,11 +127,10 @@ public void test_Lazy_ChannelReadOnly() throws Exception {
         URL url = URL.valueOf("dubbo://127.0.0.1:20883/org.apache.dubbo.rpc.protocol.dubbo.IDemoService?lazy=true&connections=1&timeout=10000");
         ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
 
-        DubboInvoker invoker = (DubboInvoker) protocol.refer(IDemoService.class, url);
+        AsyncToSyncInvoker invoker = (AsyncToSyncInvoker) protocol.refer(IDemoService.class, url);
         Assertions.assertEquals(true, invoker.isAvailable());
-
         try {
-            getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+            getClients((DubboInvoker) invoker.getInvoker())[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
             fail();
         } catch (IllegalStateException e) {
 
@@ -140,7 +140,7 @@ public void test_Lazy_ChannelReadOnly() throws Exception {
         Assertions.assertEquals("ok", service.get());
 
         Assertions.assertEquals(true, invoker.isAvailable());
-        getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
+        getClients((DubboInvoker) invoker.getInvoker())[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE);
         Assertions.assertEquals(false, invoker.isAvailable());
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
index 7e9818d07f1..e37bd7d0dc9 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -18,12 +18,12 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Filter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter;
 import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService;
 
@@ -57,7 +57,7 @@ public void testSyncCallback() {
         Invoker invoker = mock(Invoker.class);
         given(invoker.isAvailable()).willReturn(true);
         given(invoker.getInterface()).willReturn(DemoService.class);
-        RpcResult result = new RpcResult();
+        AppResponse result = new AppResponse();
         result.setValue("High");
         given(invoker.invoke(invocation)).willReturn(result);
         URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@@ -74,7 +74,7 @@ public void testSyncCallbackHasException() throws RpcException, Throwable {
             Invoker invoker = mock(Invoker.class);
             given(invoker.isAvailable()).willReturn(true);
             given(invoker.getInterface()).willReturn(DemoService.class);
-            RpcResult result = new RpcResult();
+            AppResponse result = new AppResponse();
             result.setException(new RuntimeException());
             given(invoker.invoke(invocation)).willReturn(result);
             URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&" + Constants.ON_THROW_METHOD_KEY + "=echo");
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
index 2baa67ccae6..30ffda71fdf 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
@@ -237,7 +237,7 @@ public void test_Sync_NoFuture() throws Exception {
         Person ret = demoProxy.get(requestId);
         Assertions.assertEquals(requestId, ret.getId());
         Future pFuture = RpcContext.getContext().getFuture();
-        Assertions.assertEquals(null, pFuture);
+        Assertions.assertEquals(ret, pFuture.get());
         destroyService();
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
index b77e5e18b3c..3f2c0cf21d4 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java
@@ -26,7 +26,9 @@
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.protocol.AsyncToSyncInvoker;
 import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
@@ -271,8 +273,7 @@ private ExchangeClient getInvokerClient(Invoker invoker) {
     }
 
     private List getInvokerClientList(Invoker invoker) {
-        @SuppressWarnings("rawtypes")
-        DubboInvoker dInvoker = (DubboInvoker) invoker;
+        @SuppressWarnings("rawtypes") DubboInvoker dInvoker = (DubboInvoker) ((AsyncToSyncInvoker) invoker).getInvoker();
         try {
             Field clientField = DubboInvoker.class.getDeclaredField("clients");
             clientField.setAccessible(true);
diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
index 3b9239171a9..1eead54f164 100644
--- a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
@@ -94,7 +94,7 @@ public void run() {
 
     @Override
     @SuppressWarnings("unchecked")
-    protected  T doRefer(Class serviceType, URL url) throws RpcException {
+    protected  T getFrameworkProxy(Class serviceType, URL url) throws RpcException {
         String generic = url.getParameter(Constants.GENERIC_KEY);
         boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
         if (isGeneric) {
diff --git a/dubbo-rpc/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java b/dubbo-rpc/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
index 0188a0fe329..7b99e2d80d5 100644
--- a/dubbo-rpc/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
@@ -18,8 +18,8 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.remoting.http.HttpBinder;
 import org.apache.dubbo.remoting.http.HttpHandler;
 import org.apache.dubbo.remoting.http.HttpServer;
@@ -110,7 +110,7 @@ private  HttpInvokerServiceExporter createExporter(T impl, Class type) {
 
     @Override
     @SuppressWarnings("unchecked")
-    protected  T doRefer(final Class serviceType, final URL url) throws RpcException {
+    protected  T getFrameworkProxy(final Class serviceType, final URL url) throws RpcException {
         final String generic = url.getParameter(Constants.GENERIC_KEY);
         final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
 
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 1181e899d71..5a9bb845e15 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
@@ -88,7 +88,7 @@ public  Exporter export(Invoker invoker) throws RpcException {
     }
 
     @Override
-    public  Invoker refer(Class serviceType, URL url) throws RpcException {
+    public  Invoker doRefer(Class serviceType, URL url) throws RpcException {
         return new InjvmInvoker(serviceType, url, url.getServiceKey(), exporterMap);
     }
 
diff --git a/dubbo-rpc/dubbo-rpc-memcached/src/main/java/org/apache/dubbo/rpc/protocol/memcached/MemcachedProtocol.java b/dubbo-rpc/dubbo-rpc-memcached/src/main/java/org/apache/dubbo/rpc/protocol/memcached/MemcachedProtocol.java
index 8fa088bb95f..eef3bf6aa58 100644
--- a/dubbo-rpc/dubbo-rpc-memcached/src/main/java/org/apache/dubbo/rpc/protocol/memcached/MemcachedProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-memcached/src/main/java/org/apache/dubbo/rpc/protocol/memcached/MemcachedProtocol.java
@@ -18,12 +18,12 @@
 
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 import org.apache.dubbo.rpc.protocol.AbstractProtocol;
 
@@ -56,7 +56,7 @@ public  Exporter export(final Invoker invoker) throws RpcException {
     }
 
     @Override
-    public  Invoker refer(final Class type, final URL url) throws RpcException {
+    public  Invoker doRefer(final Class type, final URL url) throws RpcException {
         try {
             String address = url.getAddress();
             String backup = url.getParameter(Constants.BACKUP_KEY);
@@ -73,26 +73,26 @@ public  Invoker refer(final Class type, final URL url) throws RpcExcept
                 @Override
                 protected Result doInvoke(Invocation invocation) throws Throwable {
                     try {
+                        Object value = null;
                         if (get.equals(invocation.getMethodName())) {
                             if (invocation.getArguments().length != 1) {
                                 throw new IllegalArgumentException("The memcached get method arguments mismatch, must only one arguments. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url);
                             }
-                            return new RpcResult(memcachedClient.get(String.valueOf(invocation.getArguments()[0])));
+                            value = memcachedClient.get(String.valueOf(invocation.getArguments()[0]));
                         } else if (set.equals(invocation.getMethodName())) {
                             if (invocation.getArguments().length != 2) {
                                 throw new IllegalArgumentException("The memcached set method arguments mismatch, must be two arguments. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url);
                             }
                             memcachedClient.set(String.valueOf(invocation.getArguments()[0]), expiry, invocation.getArguments()[1]);
-                            return new RpcResult();
                         } else if (delete.equals(invocation.getMethodName())) {
                             if (invocation.getArguments().length != 1) {
                                 throw new IllegalArgumentException("The memcached delete method arguments mismatch, must only one arguments. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url);
                             }
                             memcachedClient.delete(String.valueOf(invocation.getArguments()[0]));
-                            return new RpcResult();
                         } else {
                             throw new UnsupportedOperationException("Unsupported method " + invocation.getMethodName() + " in memcached service.");
                         }
+                        return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
                     } catch (Throwable t) {
                         RpcException re = new RpcException("Failed to invoke memcached service method. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url + ", cause: " + t.getMessage(), t);
                         if (t instanceof TimeoutException || t instanceof SocketTimeoutException) {
diff --git a/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java b/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java
index ba35c298ec4..38aeae826ea 100644
--- a/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java
@@ -23,12 +23,12 @@
 import org.apache.dubbo.common.serialize.ObjectOutput;
 import org.apache.dubbo.common.serialize.Serialization;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 import org.apache.dubbo.rpc.protocol.AbstractProtocol;
 
@@ -68,7 +68,7 @@ private Serialization getSerialization(URL url) {
     }
 
     @Override
-    public  Invoker refer(final Class type, final URL url) throws RpcException {
+    protected  Invoker doRefer(final Class type, final URL url) throws RpcException {
         try {
             GenericObjectPoolConfig config = new GenericObjectPoolConfig();
             config.setTestOnBorrow(url.getParameter("test.on.borrow", true));
@@ -119,10 +119,10 @@ protected Result doInvoke(Invocation invocation) throws Throwable {
                             }
                             byte[] value = jedis.get(String.valueOf(invocation.getArguments()[0]).getBytes());
                             if (value == null) {
-                                return new RpcResult();
+                                return AsyncRpcResult.newDefaultAsyncResult(invocation);
                             }
                             ObjectInput oin = getSerialization(url).deserialize(url, new ByteArrayInputStream(value));
-                            return new RpcResult(oin.readObject());
+                            return AsyncRpcResult.newDefaultAsyncResult(oin.readObject(), invocation);
                         } else if (set.equals(invocation.getMethodName())) {
                             if (invocation.getArguments().length != 2) {
                                 throw new IllegalArgumentException("The redis set method arguments mismatch, must be two arguments. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url);
@@ -135,13 +135,13 @@ protected Result doInvoke(Invocation invocation) throws Throwable {
                             if (expiry > 1000) {
                                 jedis.expire(key, expiry / 1000);
                             }
-                            return new RpcResult();
+                            return AsyncRpcResult.newDefaultAsyncResult(invocation);
                         } else if (delete.equals(invocation.getMethodName())) {
                             if (invocation.getArguments().length != 1) {
                                 throw new IllegalArgumentException("The redis delete method arguments mismatch, must only one arguments. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url);
                             }
                             jedis.del(String.valueOf(invocation.getArguments()[0]).getBytes());
-                            return new RpcResult();
+                            return AsyncRpcResult.newDefaultAsyncResult(invocation);
                         } else {
                             throw new UnsupportedOperationException("Unsupported method " + invocation.getMethodName() + " in redis service.");
                         }
diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java
index 6265254d4c6..09ba8c30aee 100644
--- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java
@@ -128,7 +128,7 @@ protected  Runnable doExport(T impl, Class type, URL url) throws RpcExcept
     }
 
     @Override
-    protected  T doRefer(Class serviceType, URL url) throws RpcException {
+    protected  T getFrameworkProxy(Class serviceType, URL url) throws RpcException {
 
         // TODO more configs to add
         PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java
index 63e9fcdfff1..823d042142e 100644
--- a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java
@@ -22,6 +22,7 @@
 import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
 import org.apache.dubbo.rpc.service.GenericService;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
+
 import org.springframework.remoting.RemoteAccessException;
 import org.springframework.remoting.rmi.RmiProxyFactoryBean;
 import org.springframework.remoting.rmi.RmiServiceExporter;
@@ -69,7 +70,7 @@ public void run() {
 
     @Override
     @SuppressWarnings("unchecked")
-    protected  T doRefer(final Class serviceType, final URL url) throws RpcException {
+    protected  T getFrameworkProxy(final Class serviceType, final URL url) throws RpcException {
         final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
         final String generic = url.getParameter(Constants.GENERIC_KEY);
         final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java
index 93a5900d753..75dd2d248df 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java
+++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java
@@ -26,9 +26,9 @@
 import org.apache.dubbo.remoting.buffer.ChannelBufferInputStream;
 import org.apache.dubbo.remoting.exchange.Request;
 import org.apache.dubbo.remoting.exchange.Response;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.protocol.thrift.io.RandomAccessByteArrayOutputStream;
 import org.apache.thrift.TApplicationException;
 import org.apache.thrift.TBase;
@@ -284,7 +284,7 @@ private Object decode(TProtocol protocol)
                 throw new IOException(e.getMessage(), e);
             }
 
-            RpcResult result = new RpcResult();
+            AppResponse result = new AppResponse();
 
             result.setException(new RpcException(exception.getMessage()));
 
@@ -375,7 +375,7 @@ private Object decode(TProtocol protocol)
 
             response.setId(id);
 
-            RpcResult rpcResult = new RpcResult();
+            AppResponse rpcResult = new AppResponse();
 
             if (realResult instanceof Throwable) {
                 rpcResult.setException((Throwable) realResult);
@@ -536,7 +536,7 @@ private void encodeRequest(Channel channel, ChannelBuffer buffer, Request reques
     private void encodeResponse(Channel channel, ChannelBuffer buffer, Response response)
             throws IOException {
 
-        RpcResult result = (RpcResult) response.getResult();
+        AppResponse result = (AppResponse) response.getResult();
 
         RequestData rd = CACHED_REQUEST.get(response.getId());
 
@@ -610,7 +610,7 @@ private void encodeResponse(Channel channel, ChannelBuffer buffer, Response resp
             }
 
         } else {
-            Object realResult = result.getResult();
+            Object realResult = result.getValue();
             // result field id is 0
             String fieldName = resultObj.fieldForId(0).getFieldName();
             String setMethodName = ThriftUtils.generateSetMethodName(fieldName);
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java
index fa123e232a5..e02fac3a176 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java
@@ -22,6 +22,8 @@
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.TimeoutException;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
@@ -29,8 +31,10 @@
 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.dubbo.FutureAdapter;
 
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.locks.ReentrantLock;
 /**
  * @since 2.7.0, use https://github.com/dubbo/dubbo-rpc-native-thrift instead
@@ -85,10 +89,9 @@ protected Result doInvoke(Invocation invocation) throws Throwable {
             int timeout = getUrl().getMethodParameter(
                     methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
 
-            RpcContext.getContext().setFuture(null);
-
-            return (Result) currentClient.request(inv, timeout).get();
-
+            CompletableFuture appResponseFuture = currentClient.request(inv, timeout).thenApply(obj -> (AppResponse) obj);
+            RpcContext.getContext().setFuture(new FutureAdapter(appResponseFuture));
+            return new AsyncRpcResult(appResponseFuture, invocation);
         } catch (TimeoutException e) {
             throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
         } catch (RemotingException e) {
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 055af6f8bb3..61c13ccce65 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
@@ -32,6 +32,7 @@
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
+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;
@@ -42,6 +43,8 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
 /**
  * @since 2.7.0, use https://github.com/dubbo/dubbo-rpc-native-thrift instead
  */
@@ -83,8 +86,8 @@ public CompletableFuture reply(ExchangeChannel channel, Object msg) thro
 
                 RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
 
-                return CompletableFuture.completedFuture(exporter.getInvoker().invoke(inv));
-
+                Result result = exporter.getInvoker().invoke(inv);
+                return result.thenApply(Function.identity());
             }
 
             throw new RemotingException(channel,
@@ -157,7 +160,7 @@ public void destroy() {
     } // ~ end of method destroy
 
     @Override
-    public  Invoker refer(Class type, URL url) throws RpcException {
+    protected  Invoker doRefer(Class type, URL url) throws RpcException {
 
         ThriftInvoker invoker = new ThriftInvoker(type, url, getClients(url), invokers);
 
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java
index a1d5daadea8..1c781f85af2 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java
+++ b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java
@@ -24,9 +24,9 @@
 import org.apache.dubbo.remoting.exchange.Request;
 import org.apache.dubbo.remoting.exchange.Response;
 import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
+import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
-import org.apache.dubbo.rpc.RpcResult;
 import org.apache.dubbo.rpc.gen.thrift.Demo;
 import org.apache.dubbo.rpc.protocol.thrift.io.RandomAccessByteArrayOutputStream;
 import org.apache.thrift.TApplicationException;
@@ -186,13 +186,13 @@ public void testDecodeReplyResponse() throws Exception {
 
         Assertions.assertEquals(request.getId(), response.getId());
 
-        Assertions.assertTrue(response.getResult() instanceof RpcResult);
+        Assertions.assertTrue(response.getResult() instanceof AppResponse);
 
-        RpcResult result = (RpcResult) response.getResult();
+        AppResponse result = (AppResponse) response.getResult();
 
-        Assertions.assertTrue(result.getResult() instanceof String);
+        Assertions.assertTrue(result.getValue() instanceof String);
 
-        Assertions.assertEquals(methodResult.success, result.getResult());
+        Assertions.assertEquals(methodResult.success, result.getValue());
 
     }
 
@@ -256,9 +256,9 @@ public void testDecodeExceptionResponse() throws Exception {
 
         Response response = (Response) obj;
 
-        Assertions.assertTrue(response.getResult() instanceof RpcResult);
+        Assertions.assertTrue(response.getResult() instanceof AppResponse);
 
-        RpcResult result = (RpcResult) response.getResult();
+        AppResponse result = (AppResponse) response.getResult();
 
         Assertions.assertTrue(result.hasException());
 
@@ -275,8 +275,8 @@ public void testEncodeReplyResponse() throws Exception {
 
         Request request = createRequest();
 
-        RpcResult rpcResult = new RpcResult();
-        rpcResult.setResult("Hello, World!");
+        AppResponse rpcResult = new AppResponse();
+        rpcResult.setValue("Hello, World!");
 
         Response response = new Response();
         response.setResult(rpcResult);
@@ -333,7 +333,7 @@ public void testEncodeExceptionResponse() throws Exception {
 
         Request request = createRequest();
 
-        RpcResult rpcResult = new RpcResult();
+        AppResponse rpcResult = new AppResponse();
         String exceptionMessage = "failed";
         rpcResult.setException(new RuntimeException(exceptionMessage));
 
diff --git a/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java b/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
index ddb270def40..007f62beedc 100644
--- a/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
@@ -107,7 +107,7 @@ public void run() {
 
     @Override
     @SuppressWarnings("unchecked")
-    protected  T doRefer(final Class serviceType, final URL url) throws RpcException {
+    protected  T getFrameworkProxy(final Class serviceType, final URL url) throws RpcException {
         ClientProxyFactoryBean proxyFactoryBean = new ClientProxyFactoryBean();
         proxyFactoryBean.setAddress(url.setProtocol("http").toIdentityString());
         proxyFactoryBean.setServiceClass(serviceType);