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 a17c4abcbd2..3b98859728b 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 @@ -23,11 +23,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; @@ -103,7 +103,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..539686ed0e4 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,18 +18,18 @@ 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; import java.util.List; /** - * When invoke fails, log the error message and ignore this error by returning an empty RpcResult. + * When invoke fails, log the error message and ignore this error by returning an empty Result. * Usually used to write audit logs and other operations * * Fail-safe @@ -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 70a6eaa75ff..2c352b2cee9 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 @@ -40,6 +40,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; /** + * 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 @@ -70,7 +72,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 d23459e909d..d3243c4c086 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 @@ -22,12 +22,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; @@ -40,17 +40,16 @@ 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; -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; +import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; import static org.apache.dubbo.rpc.Constants.MERGER_KEY; +/** + * @param + */ @SuppressWarnings("unchecked") public class MergeableClusterInvoker extends AbstractClusterInvoker { @@ -90,26 +89,21 @@ 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); + RpcInvocation subInvocation = new RpcInvocation(invocation, invoker); + subInvocation.setAttachment(ASYNC_KEY, "true"); + results.put(invoker.getUrl().getServiceKey(), invoker.invoke(subInvocation)); } Object result = null; List resultList = new ArrayList(results.size()); - int timeout = getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 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(), @@ -123,13 +117,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(".")) { @@ -177,7 +171,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 c85eed476bb..77eceb0e75c 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 @@ -21,12 +21,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; @@ -115,7 +115,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 878e3742df6..cb2656f33b9 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 @@ -19,12 +19,12 @@ 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 ec3541eed1f..5245a2cb69d 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 @@ -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-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 635014863b1..6a03001ddf3 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 @@ -18,12 +18,12 @@ 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 07bae73e2f5..3223b27ca8f 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 @@ -17,10 +17,11 @@ package org.apache.dubbo.rpc.cluster.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.RpcResult; import org.apache.dubbo.rpc.cluster.Directory; import org.junit.jupiter.api.Assertions; @@ -120,7 +121,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; } @@ -136,7 +137,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; } @@ -196,14 +197,14 @@ public void testAddMenu() throws Exception { given(firstInvoker.getUrl()).willReturn( url.addParameter(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(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/RpcConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RpcConstants.java index 75c70852666..b40a9c1d58c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RpcConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RpcConstants.java @@ -42,6 +42,62 @@ public interface RpcConstants { String $INVOKE = "$invoke"; + String $INVOKE_ASYNC = "$invokeAsync"; + + String $ECHO = "$echo"; + + String RETURN_PREFIX = "return "; + + String THROW_PREFIX = "throw"; + + String FAIL_PREFIX = "fail:"; + + String FORCE_PREFIX = "force:"; + + String MERGER_KEY = "merger"; + + String IS_SERVER_KEY = "isserver"; + + String FORCE_USE_TAG = "dubbo.force.tag"; + + String GENERIC_SERIALIZATION_NATIVE_JAVA = "nativejava"; + + String GENERIC_SERIALIZATION_DEFAULT = "true"; + + String GENERIC_SERIALIZATION_BEAN = "bean"; + + String GENERIC_SERIALIZATION_PROTOBUF = "protobuf-json"; + + String TPS_LIMIT_RATE_KEY = "tps"; + + String TPS_LIMIT_INTERVAL_KEY = "tps.interval"; + + long DEFAULT_TPS_LIMIT_INTERVAL = 60 * 1000; + + String AUTO_ATTACH_INVOCATIONID_KEY = "invocationid.autoattach"; + + String STUB_EVENT_KEY = "dubbo.stub.event"; + + boolean DEFAULT_STUB_EVENT = false; + + String STUB_EVENT_METHODS_KEY = "dubbo.stub.event.methods"; + + String PROXY_KEY = "proxy"; + + String EXECUTES_KEY = "executes"; + + String REFERENCE_FILTER_KEY = "reference.filter"; + + String INVOKER_LISTENER_KEY = "invoker.listener"; + + String SERVICE_FILTER_KEY = "service.filter"; + + String EXPORTER_LISTENER_KEY = "exporter.listener"; + + String ACCESS_LOG_KEY = "accesslog"; + + String ACTIVES_KEY = "actives"; + String CONNECTIONS_KEY = "connections"; } 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/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java index f46235daaa1..298f15c2f02 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java @@ -22,12 +22,14 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -401,6 +403,32 @@ public void testForName2() { }); } + @Test + public void testGetReturnTypes () throws Exception{ + Class clazz = TypeClass.class; + + Type[] types = ReflectUtils.getReturnTypes(clazz.getMethod("getFuture")); + Assertions.assertEquals("java.lang.String", types[0].getTypeName()); + Assertions.assertEquals("java.lang.String", types[1].getTypeName()); + + Type[] types1 = ReflectUtils.getReturnTypes(clazz.getMethod("getString")); + Assertions.assertEquals("java.lang.String", types1[0].getTypeName()); + Assertions.assertEquals("java.lang.String", types1[1].getTypeName()); + + Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture")); + Assertions.assertEquals("java.util.List", types2[0].getTypeName()); + Assertions.assertEquals("java.util.List", types2[1].getTypeName()); + } + + public static interface TypeClass { + + CompletableFuture getFuture(); + + String getString(); + + CompletableFuture> getListFuture(); + } + public static class EmptyClass { private EmptyProperty property; public boolean set; 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 9d960c219df..d283c63b26a 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, org.apache.dubbo.rpc.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..07f5df9bf5a 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 @@ -18,11 +18,30 @@ package com.alibaba.dubbo.rpc; import java.util.Map; +import java.util.function.Function; @Deprecated public interface Result extends org.apache.dubbo.rpc.Result { - class CompatibleResult implements Result { + @Override + default void setValue(Object value) { + + } + + @Override + default void setException(Throwable t) { + + } + + abstract class AbstractResult extends org.apache.dubbo.rpc.AbstractResult implements Result { + + @Override + public org.apache.dubbo.rpc.Result thenApplyWithContext(Function fn) { + return null; + } + } + + class CompatibleResult extends AbstractResult { private org.apache.dubbo.rpc.Result delegate; public CompatibleResult(org.apache.dubbo.rpc.Result result) { @@ -38,11 +57,21 @@ public Object getValue() { return delegate.getValue(); } + @Override + public void setValue(Object value) { + delegate.setValue(value); + } + @Override public Throwable getException() { return delegate.getException(); } + @Override + public void setException(Throwable t) { + delegate.setException(t); + } + @Override public boolean hasException() { return delegate.hasException(); @@ -53,11 +82,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/main/java/com/alibaba/dubbo/rpc/RpcContext.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java index bf6160c06ac..28ae5f1757a 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java @@ -29,7 +29,7 @@ private static RpcContext newInstance(org.apache.dubbo.rpc.RpcContext rpcContext RpcContext copy = new RpcContext(); copy.getAttachments().putAll(rpcContext.getAttachments()); copy.get().putAll(rpcContext.get()); - copy.setFuture(rpcContext.getFuture()); + copy.setFuture(rpcContext.getCompletableFuture()); copy.setUrls(rpcContext.getUrls()); copy.setUrl(rpcContext.getUrl()); copy.setMethodName(rpcContext.getMethodName()); diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java index 7f1ab98de5c..d7db04fe106 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/support/RpcUtils.java @@ -20,7 +20,6 @@ import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; @@ -74,10 +73,6 @@ public static boolean isReturnTypeFuture(Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.isReturnTypeFuture(inv); } - public static boolean hasFutureReturnType(Method method) { - return org.apache.dubbo.rpc.support.RpcUtils.hasFutureReturnType(method); - } - public static boolean isOneway(URL url, Invocation inv) { return org.apache.dubbo.rpc.support.RpcUtils.isOneway(url.getOriginalURL(), inv); } 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 761ce4b99e7..c088917e51d 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 @@ -63,6 +63,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-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 24fa51cd531..772a16b5789 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 @@ -21,12 +21,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; @@ -48,10 +48,10 @@ * 3)<dubbo:provider cache="expiring" /> * 4)<dubbo:consumer cache="jcache" /> * - * If cache type is defined in method level then method level type will get precedence. According to above provided - * example, if service has two method, method1 and method2, method2 will have cache type as threadlocal where others will - * be backed by lru - * + *If cache type is defined in method level then method level type will get precedence. According to above provided + *example, if service has two method, method1 and method2, method2 will have cache type as threadlocal where others will + *be backed by lru + * * * @see org.apache.dubbo.rpc.Filter * @see org.apache.dubbo.cache.support.lru.LruCacheFactory @@ -62,6 +62,7 @@ * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache * @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory * @see org.apache.dubbo.cache.support.expiring.ExpiringCache + * */ @Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY) public class CacheFilter implements Filter { @@ -83,7 +84,6 @@ public void setCacheFactory(CacheFactory cacheFactory) { * If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store * then it will return otherwise call the remote method and return value. If remote method's return valeu has error * then it will not cache the value. - * * @param invoker service * @param invocation invocation. * @return Cache returned value if found by the underlying cache store. If cache miss it will call target method. @@ -98,9 +98,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); @@ -122,7 +122,7 @@ static class ValueWrapper implements Serializable { private final Object value; - public ValueWrapper(Object value) { + public ValueWrapper (Object value) { this.value = value; } 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 5d2551dd149..d2f0539d83a 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 @@ -18,12 +18,12 @@ 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; @@ -90,7 +90,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 2221089426c..7e606a53b94 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 @@ -88,4 +88,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 ef498063dbd..8c1f19a2892 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 @@ -24,9 +24,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; @@ -52,10 +52,14 @@ * MonitorFilter. (SPI, Singleton, ThreadSafe) */ @Activate(group = {PROVIDER, 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 */ @@ -70,6 +74,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 * @@ -81,105 +86,10 @@ public void setMonitorFactory(MonitorFactory monitorFactory) { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (invoker.getUrl().hasParameter(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(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(APPLICATION_KEY); - String service = invoker.getInterface().getName(); // service name - String method = RpcUtils.getMethodName(invocation); // method name - String group = invoker.getUrl().getParameter(GROUP_KEY); - String version = invoker.getUrl().getParameter(VERSION_KEY); - - int localPort; - String remoteKey, remoteValue; - if (CONSUMER_SIDE.equals(invoker.getUrl().getParameter(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(INPUT_KEY) != null) { - input = invocation.getAttachment(INPUT_KEY); - } - if (result != null && result.getAttachment(OUTPUT_KEY) != null) { - output = result.getAttachment(OUTPUT_KEY); - } - - return new URL(COUNT_PROTOCOL, - NetUtils.getLocalHost(), localPort, - service + 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), - INPUT_KEY, input, - OUTPUT_KEY, output, - GROUP_KEY, group, - VERSION_KEY, version); + return invoker.invoke(invocation); // proceed invocation chain } // concurrent counter @@ -193,4 +103,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(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(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(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(APPLICATION_KEY); + String service = invoker.getInterface().getName(); // service name + String method = RpcUtils.getMethodName(invocation); // method name + String group = invoker.getUrl().getParameter(GROUP_KEY); + String version = invoker.getUrl().getParameter(VERSION_KEY); + + int localPort; + String remoteKey, remoteValue; + if (CONSUMER_SIDE.equals(invoker.getUrl().getParameter(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(INPUT_KEY) != null) { + input = invocation.getAttachment(INPUT_KEY); + } + if (result != null && result.getAttachment(OUTPUT_KEY) != null) { + output = result.getAttachment(OUTPUT_KEY); + } + + return new URL(COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + 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), INPUT_KEY, input, OUTPUT_KEY, output, GROUP_KEY, group, 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 586ea60d10d..56d372c1d2b 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 @@ -21,6 +21,7 @@ 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; @@ -77,7 +78,7 @@ public boolean isAvailable() { public Result invoke(Invocation invocation) throws RpcException { lastInvocation = invocation; - return null; + return AsyncRpcResult.newDefaultAsyncResult(invocation); } @Override @@ -119,7 +120,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); } @@ -155,7 +160,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-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java b/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java index fa16ae2f574..761de5945dc 100644 --- a/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java +++ b/dubbo-monitor/dubbo-monitor-default/src/main/java/org/apache/dubbo/monitor/dubbo/MetricsFilter.java @@ -23,7 +23,7 @@ import org.apache.dubbo.common.store.DataStore; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.monitor.MetricsService; -import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -31,7 +31,6 @@ 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 com.alibaba.fastjson.JSON; @@ -66,6 +65,7 @@ import static org.apache.dubbo.monitor.Constants.METRICS_PORT; import static org.apache.dubbo.monitor.Constants.METRICS_PROTOCOL; import static org.apache.dubbo.monitor.Constants.SERVICE; +import static org.apache.dubbo.remoting.Constants.EXECUTOR_SERVICE_COMPONENT_KEY; public class MetricsFilter implements Filter { @@ -178,7 +178,7 @@ private void setCompassQuantity(String groupName, String result, long duration, private List getThreadPoolMessage() { DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension(); - Map executors = dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY); + Map executors = dataStore.get(EXECUTOR_SERVICE_COMPONENT_KEY); List threadPoolMtricList = new ArrayList<>(); for (Map.Entry entry : executors.entrySet()) { @@ -235,12 +235,9 @@ public Result invoke(Invocation invocation) throws RpcException { collector.collect(entry.getKey(), entry.getValue(), timestamp); } - RpcResult result = new RpcResult(); - List res = collector.build(); res.addAll(getThreadPoolMessage()); - result.setValue(JSON.toJSONString(res)); - return result; + return AsyncRpcResult.newDefaultAsyncResult(JSON.toJSONString(res), invocation); } @Override 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-registry/dubbo-registry-sofa/pom.xml b/dubbo-registry/dubbo-registry-sofa/pom.xml index 7a2415fc216..9faccea3868 100644 --- a/dubbo-registry/dubbo-registry-sofa/pom.xml +++ b/dubbo-registry/dubbo-registry-sofa/pom.xml @@ -32,7 +32,7 @@ 2.1 -Dnetwork_interface_denylist=docker0 - + org.apache.dubbo 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/ResponseCallback.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java deleted file mode 100644 index 37fe6cc6e34..00000000000 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ResponseCallback.java +++ /dev/null @@ -1,38 +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; - -/** - * 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 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 7a860a396fd..1dab931a9c4 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 @@ -28,17 +28,13 @@ 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; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; @@ -46,7 +42,7 @@ /** * DefaultFuture. */ -public class DefaultFuture implements ResponseFuture { +public class DefaultFuture extends CompletableFuture { private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class); @@ -66,12 +62,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; @@ -170,144 +162,34 @@ 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 = 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) { - // remove from pending task - PENDING_TASKS.remove(future.getId()); - - if (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) { - if (c == 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 { - c.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()); - c.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()); - c.caught(re); - } catch (Exception e) { - logger.error("callback invoke error ,url:" + channel.getUrl(), e); - } + this.completeExceptionally(new RemotingException(channel, res.getErrorMessage())); } } - 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()); - } - throw new RemotingException(channel, res.getErrorMessage()); - } - private long getId() { return id; } @@ -328,27 +210,10 @@ 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); - } - } - private String getTimeoutMessage(boolean scan) { long nowTimestamp = System.currentTimeMillis(); return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side") @@ -361,4 +226,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 f9193de3e41..2529ac7e480 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 @@ -27,10 +27,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; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; @@ -99,12 +99,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(TIMEOUT_KEY, 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 8bd5665ad7a..72348e927d9 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; @@ -65,7 +65,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); } @@ -80,7 +80,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 327eb7e3d22..220d5d78c83 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/MockResult.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockResult.java index 78369527efc..c353621056a 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockResult.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockResult.java @@ -19,7 +19,7 @@ import java.io.Serializable; /** - * RpcResult. + * AppResponse. */ public class MockResult implements Serializable { 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 1b58bf78a23..ca26a647f34 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 8a2cb256ef7..52bed1e0f5e 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 @@ -30,6 +30,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 index b898934c63c..2db743d5716 100644 --- 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 @@ -16,59 +16,10 @@ */ package org.apache.dubbo.rpc; -import org.apache.dubbo.common.utils.StringUtils; - -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.CompletableFuture; /** * */ -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); - } - +public abstract class AbstractResult extends CompletableFuture implements Result { } 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..16e938095a5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java @@ -0,0 +1,146 @@ +/* + * 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.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 described as follow, an abstraction of the definition of AsyncRpcResult: + *
+ *  {@code
+ *   Public class AsyncRpcResult implements CompletionStage {
+ *       ......
+ *  }
+ * 
+ * AsyncRpcResult is a future representing an unfinished RPC call, while AppResponse is the actual return type of this call. + * In theory, AppResponse does'n have 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 extends AbstractResult implements 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 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..9546e2a39ce 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 @@ -25,176 +25,174 @@ import java.util.function.Function; /** - * NOTICE!! - * + * This class represents an unfinished RPC call, it will hold some context information for this call, for example RpcContext and Invocation, + * so that when the call finishes and the result returns, it can guarantee all the contexts being recovered as the same as when the call was made + * before any callback is invoked. *

- * 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. + * TODO if it's reasonable or even right to keep a reference to Invocation? + *

+ * As {@link Result} implements CompletionStage, {@link AsyncRpcResult} allows you to easily build a async filter chain whose status will be + * driven entirely by the state of the underlying RPC call. + *

+ * AsyncRpcResult does not contain any concrete value (except the underlying value bring by CompletableFuture), consider it as a status transfer node. + * {@link #getValue()} and {@link #getException()} are all inherited from {@link Result} interface, implementing them are mainly + * for compatibility consideration. Because many legacy {@link Filter} implementation are most possibly to call getValue directly. */ public class AsyncRpcResult extends AbstractResult { 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; - - protected CompletableFuture resultFuture; + private Invocation invocation; - public AsyncRpcResult(CompletableFuture future) { - this(future, true); + public AsyncRpcResult(Invocation invocation) { + this.invocation = invocation; + this.storedContext = RpcContext.getContext(); + this.storedServerContext = RpcContext.getServerContext(); } - public AsyncRpcResult(CompletableFuture future, boolean registerCallback) { - this(future, new CompletableFuture<>(), registerCallback); + public AsyncRpcResult(AsyncRpcResult asyncRpcResult) { + this.invocation = asyncRpcResult.getInvocation(); + this.storedContext = asyncRpcResult.getStoredContext(); + this.storedServerContext = asyncRpcResult.getStoredServerContext(); } /** - * @param future - * @param rFuture - * @param registerCallback + * Notice the return type of {@link #getValue} is the actual type of the RPC method, not {@link AppResponse} + * + * @return */ - 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(); - } - @Override public Object getValue() { - return getRpcResult().getValue(); + return getAppResponse().getValue(); } @Override - public Throwable getException() { - return getRpcResult().getException(); + public void setValue(Object value) { + AppResponse appResponse = new AppResponse(); + appResponse.setValue(value); + this.complete(appResponse); } @Override - public boolean hasException() { - return getRpcResult().hasException(); + public Throwable getException() { + return getAppResponse().getException(); } @Override - public Object getResult() { - return getRpcResult().getResult(); - } - - public CompletableFuture getValueFuture() { - return valueFuture; + public void setException(Throwable t) { + AppResponse appResponse = new AppResponse(); + appResponse.setException(t); + this.complete(appResponse); } - public CompletableFuture getResultFuture() { - return resultFuture; - } - - public void setResultFuture(CompletableFuture resultFuture) { - this.resultFuture = resultFuture; + @Override + public boolean hasException() { + return getAppResponse().hasException(); } - public Result getRpcResult() { + public Result getAppResponse() { try { - if (resultFuture.isDone()) { - return resultFuture.get(); + if (this.isDone()) { + return this.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 appResponse = new AppResponse(); + CompletableFuture future = new CompletableFuture<>(); + appResponse.setValue(future); + this.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 appResponse.recreate(); + } else if (this.isDone()) { + return this.get().recreate(); + } + return (new AppResponse()).recreate(); + } + + public Result thenApplyWithContext(Function fn) { + CompletableFuture future = this.thenApply(fn.compose(beforeContext).andThen(afterContext)); + AsyncRpcResult nextAsyncRpcResult = new AsyncRpcResult(this); + nextAsyncRpcResult.subscribeTo(future); + return nextAsyncRpcResult; } - public void thenApplyWithContext(Function fn) { - this.resultFuture = resultFuture.thenApply(fn.compose(beforeContext).andThen(afterContext)); + public void subscribeTo(CompletableFuture future) { + future.whenComplete((obj, t) -> { + if (t != null) { + this.completeExceptionally(t); + } else { + this.complete((Result) obj); + } + }); } @Override public Map getAttachments() { - return getRpcResult().getAttachments(); + return getAppResponse().getAttachments(); } @Override public void setAttachments(Map map) { - getRpcResult().setAttachments(map); + getAppResponse().setAttachments(map); } @Override public void addAttachments(Map map) { - getRpcResult().addAttachments(map); + getAppResponse().addAttachments(map); } @Override public String getAttachment(String key) { - return getRpcResult().getAttachment(key); + return getAppResponse().getAttachment(key); } @Override public String getAttachment(String key, String defaultValue) { - return getRpcResult().getAttachment(key, defaultValue); + return getAppResponse().getAttachment(key, defaultValue); } @Override public void setAttachment(String key, String value) { - getRpcResult().setAttachment(key, value); + getAppResponse().setAttachment(key, value); + } + + public RpcContext getStoredContext() { + return storedContext; + } + + public RpcContext getStoredServerContext() { + return storedServerContext; + } + + public Invocation getInvocation() { + return invocation; } /** @@ -203,18 +201,51 @@ 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 appResponse, Invocation invocation) { + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation); + asyncRpcResult.complete(appResponse); + return asyncRpcResult; + } + + 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) { + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation); + AppResponse appResponse = new AppResponse(); + if (t != null) { + appResponse.setException(t); + } else { + appResponse.setValue(value); + } + asyncRpcResult.complete(appResponse); + return asyncRpcResult; + } } 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..53ad128efa7 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,29 @@ */ @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 + * Filter itself should only be response for passing invocation, all callbacks has been placed into {@link Listener} * - * @param result result - * @param invoker invoker - * @param invocation invocation - * @return Return {@link Result} + * @param appResponse + * @param invoker + * @param invocation + * @return */ - default Result onResponse(Result result, Invoker invoker, Invocation invocation) { - return result; + @Deprecated + default Result onResponse(Result appResponse, Invoker invoker, Invocation invocation) { + return appResponse; + } + + interface Listener { + + void onResponse(Result appResponse, 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..8d5a1b62fa3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/FutureContext.java @@ -0,0 +1,54 @@ +/* + * 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.threadlocal.InternalThreadLocal; + +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 InternalThreadLocal> futureTL = new InternalThreadLocal<>(); + + /** + * 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-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java similarity index 82% rename from dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java rename to dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java index 893b262094b..a97a0be61f4 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java @@ -14,12 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.dubbo.rpc; -package com.alibaba.dubbo.remoting.exchange; +public enum InvokeMode { + + SYNC, ASYNC, FUTURE; -/** - * 2019-04-18 - */ -@Deprecated -public interface ResponseCallback extends org.apache.dubbo.remoting.exchange.ResponseCallback { } diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java similarity index 74% rename from dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java rename to dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java index 07a69871af7..622ef1f0309 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java @@ -14,16 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.alibaba.dubbo.rpc.protocol.dubbo; - -import org.apache.dubbo.remoting.exchange.ResponseFuture; +package org.apache.dubbo.rpc; /** - * 2019-04-18 + * */ -public class FutureAdapter extends org.apache.dubbo.rpc.protocol.dubbo.FutureAdapter { - public FutureAdapter(ResponseFuture future) { - super(future); +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..1f3a5138319 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,16 +18,27 @@ import java.io.Serializable; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Future; +import java.util.function.Function; /** - * RPC invoke result. (API, Prototype, NonThreadSafe) + * (API, Prototype, NonThreadSafe) + * + * An RPC {@link Result}. + * + * Known implementations are: + * 1. {@link AsyncRpcResult}, it's a {@link CompletionStage} whose underlying value signifies the return value of an RPC call. + * 2. {@link AppResponse}, it inevitably inherits {@link CompletionStage} and {@link Future}, but you should never treat AppResponse as a type of Future, + * instead, it is a normal concrete type. * * @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 { +public interface Result extends CompletionStage, Future, Serializable { /** * Get invoke result. @@ -36,6 +47,8 @@ public interface Result extends Serializable { */ Object getValue(); + void setValue(Object value); + /** * Get exception. * @@ -43,6 +56,8 @@ public interface Result extends Serializable { */ Throwable getException(); + void setException(Throwable t); + /** * Has exception. * @@ -66,14 +81,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 +118,26 @@ public interface Result extends Serializable { void setAttachment(String key, String value); + /** + * Returns the specified {@code valueIfAbsent} when not complete, or + * returns the result value or throws an exception when complete. + * + * @see CompletableFuture#getNow(Object) + */ + Result getNow(Result valueIfAbsent); + + /** + * Add a callback which can be triggered when the RPC call finishes. + *

+ * Just as the method name implies, this method will guarantee the callback being triggered under the same context as when the call was started, + * see implementation in {@link AsyncRpcResult#thenApplyWithContext(Function)} + * + * @param fn + * @return + */ + Result thenApplyWithContext(Function fn); + + default CompletableFuture completionFuture() { + return toCompletableFuture(); + } } \ 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 fc658db052d..50221704099 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 @@ -75,7 +75,6 @@ protected RpcContext initialValue() { private final Map attachments = new HashMap(); private final Map values = new HashMap(); - private Future future; private List urls; @@ -144,32 +143,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. * @@ -251,7 +224,7 @@ public boolean isConsumerSide() { */ @SuppressWarnings("unchecked") public CompletableFuture getCompletableFuture() { - return (CompletableFuture) future; + return FutureContext.getCompletableFuture(); } /** @@ -262,7 +235,7 @@ public CompletableFuture getCompletableFuture() { */ @SuppressWarnings("unchecked") public Future getFuture() { - return (Future) future; + return FutureContext.getCompletableFuture(); } /** @@ -270,8 +243,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 a03e3dc15a2..e422bef6cf1 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 @@ -52,6 +52,10 @@ public class RpcInvocation implements Invocation, Serializable { private transient Invoker invoker; + private transient Class returnType; + + private transient InvokeMode invokeMode; + public RpcInvocation() { } @@ -94,6 +98,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) { @@ -212,6 +217,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 0e1df8f1d02..19090c1e0fb 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 @@ -18,9 +18,11 @@ 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; @@ -42,50 +44,75 @@ * @see Filter */ @Activate(group = CONSUMER, value = 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, ACTIVES_KEY, 0); - RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()); + RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()); if (!RpcStatus.beginCount(url, methodName, max)) { long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 0); long start = System.currentTimeMillis(); long remain = timeout; - synchronized (count) { + 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 { - RpcStatus.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 appResponse, Invoker invoker, Invocation invocation) { + String methodName = invocation.getMethodName(); + URL url = invoker.getUrl(); + int max = invoker.getUrl().getMethodParameter(methodName, 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, 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 5842b32c04d..54aea0d7378 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 @@ -20,17 +20,18 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CompatibleTypeUtils; import org.apache.dubbo.common.utils.PojoUtils; -import org.apache.dubbo.remoting.Constants; 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; +import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; + /** * CompatibleFilter make the remote method's return value compatible to invoker's version of object. * To make return object compatible it does @@ -45,44 +46,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 appResponse, Invoker invoker, Invocation invocation) { + if (!invocation.getMethodName().startsWith("$") && !appResponse.hasException()) { + Object value = appResponse.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(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) { + appResponse.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 f1696f4a077..43353f7e7dc 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 @@ -16,17 +16,18 @@ */ package org.apache.dubbo.rpc.filter; -import org.apache.dubbo.common.constants.CommonConstants; 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; import org.apache.dubbo.rpc.RpcInvocation; +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; + /** * ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port * for consumer invoker.It does it to make the requires info available to execution thread's RpcContext. @@ -34,8 +35,12 @@ * @see org.apache.dubbo.rpc.Filter * @see RpcContext */ -@Activate(group = CommonConstants.CONSUMER, order = -10000) -public class ConsumerContextFilter implements Filter { +@Activate(group = CONSUMER, order = -10000) +public class ConsumerContextFilter extends ListenableFilter { + + public ConsumerContextFilter() { + super.listener = new ConsumerContextListener(); + } @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { @@ -49,18 +54,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 appResponse, Invoker invoker, Invocation invocation) { + RpcContext.getServerContext().setAttachments(appResponse.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 8939c3ace2a..49e2a602d21 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 @@ -17,9 +17,9 @@ package org.apache.dubbo.rpc.filter; 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; @@ -48,9 +48,13 @@ * @see RpcContext */ @Activate(group = PROVIDER, order = -10000) -public class ContextFilter implements Filter { +public class ContextFilter extends ListenableFilter { private static final String TAG_KEY = "dubbo.tag"; + public ContextFilter() { + super.listener = new ContextListener(); + } + @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { Map attachments = invocation.getAttachments(); @@ -97,10 +101,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 appResponse, Invoker invoker, Invocation invocation) { + // pass attachments to result + appResponse.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 b50e9ffe16c..d51e6d4a2a3 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.CommonConstants; 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; import static org.apache.dubbo.rpc.Constants.$ECHO; @@ -36,7 +36,7 @@ public class EchoFilter implements Filter { @Override public Result invoke(Invoker invoker, Invocation inv) throws RpcException { if (inv.getMethodName().equals($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 bc4018513fc..8f5411b179c 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 = CommonConstants.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 appResponse, Invoker invoker, Invocation invocation) { + if (appResponse.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 = appResponse.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 + appResponse.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 f14dddfab68..e1af529b15f 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,9 +19,10 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; 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; @@ -30,12 +31,20 @@ /** + * * 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 = CommonConstants.PROVIDER, value = 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 { @@ -48,20 +57,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 appResponse, 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 07702dcb3e9..fd83e1f4271 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; @@ -44,20 +43,25 @@ import java.lang.reflect.Method; import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE; -import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_NATIVE_JAVA; +import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC; +import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_BEAN; +import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_NATIVE_JAVA; import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_PROTOBUF; -import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; /** * GenericInvokerFilter. */ @Activate(group = CommonConstants.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($INVOKE) + if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) && inv.getArguments() != null && inv.getArguments().length == 3 && !GenericService.class.isAssignableFrom(invoker.getInterface())) { @@ -134,18 +138,38 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException { args[0].getClass().getName()); } } - 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 appResponse, Invoker invoker, Invocation inv) { + if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC)) + && inv.getArguments() != null + && inv.getArguments().length == 3 + && !GenericService.class.isAssignableFrom(invoker.getInterface())) { + + String generic = inv.getAttachment(GENERIC_KEY); + if (StringUtils.isBlank(generic)) { + generic = RpcContext.getContext().getAttachment(GENERIC_KEY); + } + + if (appResponse.hasException() && !(appResponse.getException() instanceof GenericException)) { + appResponse.setException(new GenericException(appResponse.getException())); } if (ProtocolUtils.isJavaGenericSerialization(generic)) { try { UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512); - ExtensionLoader.getExtensionLoader(Serialization.class) - .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA) - .serialize(null, os).writeObject(result.getValue()); - return new RpcResult(os.toByteArray()); + ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(appResponse.getValue()); + appResponse.setValue(os.toByteArray()); } catch (IOException e) { throw new RpcException( "Generic serialization [" + @@ -153,29 +177,28 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException { "] serialize result failed.", e); } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { - return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD)); + appResponse.setValue(JavaBeanSerializeUtil.serialize(appResponse.getValue(), JavaBeanAccessor.METHOD)); } else if (ProtocolUtils.isProtobufGenericSerialization(generic)) { try { UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension(GENERIC_SERIALIZATION_PROTOBUF) - .serialize(null, os).writeObject(result.getValue()); - return new RpcResult(os.toString()); + .serialize(null, os).writeObject(appResponse.getValue()); + appResponse.setValue(os.toString()); } catch (IOException e) { throw new RpcException("Generic serialization [" + GENERIC_SERIALIZATION_PROTOBUF + "] serialize result failed.", e); } } else { - return new RpcResult(PojoUtils.generalize(result.getValue())); + appResponse.setValue(PojoUtils.generalize(appResponse.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 d422fb474e9..ed5de7e1571 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,40 +25,46 @@ 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; import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE; +import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; /** * GenericImplInvokerFilter */ @Activate(group = CommonConstants.CONSUMER, value = 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(GENERIC_KEY); if (ProtocolUtils.isGeneric(generic) - && !$INVOKE.equals(invocation.getMethodName()) + && (!$INVOKE.equals(invocation.getMethodName()) && !$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(); @@ -78,77 +84,15 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept args = PojoUtils.generalize(arguments); } - invocation2.setMethodName($INVOKE); + if (RpcUtils.isReturnTypeFuture(invocation)) { + invocation2.setMethodName($INVOKE_ASYNC); + } else { + invocation2.setMethodName($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($INVOKE) + return invoker.invoke(invocation2); + } else if ((invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC)) && invocation.getArguments() != null && invocation.getArguments().length == 3 && ProtocolUtils.isGeneric(generic)) { @@ -169,19 +113,89 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept } } - ((RpcInvocation) invocation).setAttachment(GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY)); + invocation.setAttachment( + GENERIC_KEY, invoker.getUrl().getParameter(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 appResponse, Invoker invoker, Invocation invocation) { + String generic = invoker.getUrl().getParameter(GENERIC_KEY); + String methodName = invocation.getMethodName(); + Class[] parameterTypes = invocation.getParameterTypes(); + if (ProtocolUtils.isGeneric(generic) + && (!$INVOKE.equals(invocation.getMethodName()) && !$INVOKE_ASYNC.equals(invocation.getMethodName())) + && invocation instanceof RpcInvocation) { + if (!appResponse.hasException()) { + Object value = appResponse.getValue(); + try { + Method method = invoker.getInterface().getMethod(methodName, parameterTypes); + if (ProtocolUtils.isBeanGenericSerialization(generic)) { + if (value == null) { + appResponse.setValue(value); + } else if (value instanceof JavaBeanDescriptor) { + appResponse.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); + appResponse.setValue(PojoUtils.realize(value, (Class) types[0], types[1])); + } + } catch (NoSuchMethodException e) { + throw new RpcException(e.getMessage(), e); + } + } else if (appResponse.getException() instanceof GenericException) { + GenericException exception = (GenericException) appResponse.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); + } + appResponse.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 d0f7b572497..31b0dc977a8 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 = CommonConstants.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 appResponse, 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 e218fa25f09..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 @@ -23,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; @@ -38,8 +38,6 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; - /** * AbstractInvoker. */ @@ -149,9 +147,8 @@ public Result invoke(Invocation inv) throws RpcException { */ invocation.addAttachments(contextAttachments); } - if (getUrl().getMethodParameter(invocation.getMethodName(), ASYNC_KEY, false)) { - invocation.setAttachment(ASYNC_KEY, Boolean.TRUE.toString()); - } + + invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation)); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); try { @@ -159,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 5c49e473ddb..ceeb92e1803 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; @@ -84,4 +85,11 @@ public void destroy() { } } } + + @Override + public Invoker refer(Class type, URL url) throws RpcException { + return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url)); + } + + protected abstract Invoker protocolBindingRefer(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 735b9fe4b7f..d45f9e3951f 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 @@ -95,13 +95,14 @@ public void unexport() { } @Override - public Invoker refer(final Class type, final URL url) throws RpcException { + protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { final Invoker target = proxyFactory.getInvoker(doRefer(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) { 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 f0039dbb7ab..9ee7d3e1f2b 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.URL; import org.apache.dubbo.common.constants.CommonConstants; 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; @@ -75,14 +75,31 @@ 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); + } + } else { + filter.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..fe42fb2c6be 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,48 @@ 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); + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation); + future.whenComplete((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); + } + asyncRpcResult.complete(result); + }); + return asyncRpcResult; } 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 a76f081122c..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 @@ -20,14 +20,10 @@ 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; -import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; -import static org.apache.dubbo.rpc.Constants.FUTURE_RETURNTYPE_KEY; - /** * InvokerHandler */ @@ -56,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(FUTURE_RETURNTYPE_KEY, "true"); - invocation.setAttachment(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 32fc5ae6324..e8f8209e69b 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 @@ -18,18 +18,18 @@ 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; @@ -112,7 +112,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..46089968172 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 protocolBindingRefer(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 18e59ba4170..7bf4533100e 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 @@ -22,23 +22,22 @@ 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; import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE; -import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; -import static org.apache.dubbo.rpc.Constants.ID_KEY; +import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC; import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; +import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; import static org.apache.dubbo.rpc.Constants.FUTURE_GENERATED_KEY; -import static org.apache.dubbo.rpc.Constants.FUTURE_RETURNTYPE_KEY; +import static org.apache.dubbo.rpc.Constants.ID_KEY; import static org.apache.dubbo.rpc.Constants.RETURN_KEY; /** * RpcUtils @@ -71,7 +70,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 @@ -86,24 +84,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) { @@ -190,11 +171,22 @@ public static boolean isAsync(URL url, Invocation inv) { } public static boolean isReturnTypeFuture(Invocation inv) { - return Boolean.TRUE.toString().equals(inv.getAttachment(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 $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 78% 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 index 9c983a544a5..2fcace07a9a 100644 --- 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 @@ -20,13 +20,13 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class RpcResultTest { +public class AppResponseTest { @Test - public void testRpcResultWithNormalException() { + public void testAppResponseWithNormalException() { NullPointerException npe = new NullPointerException(); - RpcResult rpcResult = new RpcResult(npe); + AppResponse appResponse = new AppResponse(npe); - StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace(); + StackTraceElement[] stackTrace = appResponse.getException().getStackTrace(); Assertions.assertNotNull(stackTrace); Assertions.assertTrue(stackTrace.length > 1); } @@ -35,14 +35,14 @@ public void testRpcResultWithNormalException() { * please run this test in Run mode */ @Test - public void testRpcResultWithEmptyStackTraceException() { + public void testAppResponseWithEmptyStackTraceException() { Throwable throwable = buildEmptyStackTraceException(); if (throwable == null) { return; } - RpcResult rpcResult = new RpcResult(throwable); + AppResponse appResponse = new AppResponse(throwable); - StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace(); + StackTraceElement[] stackTrace = appResponse.getException().getStackTrace(); Assertions.assertNotNull(stackTrace); Assertions.assertTrue(stackTrace.length == 0); } @@ -50,10 +50,10 @@ public void testRpcResultWithEmptyStackTraceException() { @Test public void testSetExceptionWithNormalException() { NullPointerException npe = new NullPointerException(); - RpcResult rpcResult = new RpcResult(); - rpcResult.setException(npe); + AppResponse appResponse = new AppResponse(); + appResponse.setException(npe); - StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace(); + StackTraceElement[] stackTrace = appResponse.getException().getStackTrace(); Assertions.assertNotNull(stackTrace); Assertions.assertTrue(stackTrace.length > 1); } @@ -67,10 +67,10 @@ public void testSetExceptionWithEmptyStackTraceException() { if (throwable == null) { return; } - RpcResult rpcResult = new RpcResult(); - rpcResult.setException(throwable); + AppResponse appResponse = new AppResponse(); + appResponse.setException(throwable); - StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace(); + StackTraceElement[] stackTrace = appResponse.getException().getStackTrace(); Assertions.assertNotNull(stackTrace); Assertions.assertTrue(stackTrace.length == 0); } 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..709401d4278 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); + AppResponse appResponse = (AppResponse) asyncResult.get(); + compatibleFilter.listener().onResponse(appResponse, invoker, invocation); + assertEquals(Type.High, appResponse.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); + AppResponse appResponse = (AppResponse) asyncResult.get(); + compatibleFilter.listener().onResponse(appResponse, invoker, invocation); + assertEquals(Type.High, appResponse.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..bc9c1320372 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; @@ -51,18 +51,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,16 +78,16 @@ public void testJavaException() { ExceptionFilter exceptionFilter = new ExceptionFilter(); RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"}); - RpcResult rpcResult = new RpcResult(); - rpcResult.setException(new IllegalArgumentException("java")); + AppResponse appResponse = new AppResponse(); + appResponse.setException(new IllegalArgumentException("java")); Invoker invoker = mock(Invoker.class); - when(invoker.invoke(invocation)).thenReturn(rpcResult); + when(invoker.invoke(invocation)).thenReturn(appResponse); when(invoker.getInterface()).thenReturn(DemoService.class); Result newResult = exceptionFilter.invoke(invoker, invocation); - Assertions.assertEquals(rpcResult.getException(), newResult.getException()); + Assertions.assertEquals(appResponse.getException(), newResult.getException()); } @@ -96,42 +98,43 @@ public void testRuntimeException() { ExceptionFilter exceptionFilter = new ExceptionFilter(); RpcInvocation invocation = new RpcInvocation("sayHello", new Class[]{String.class}, new Object[]{"world"}); - RpcResult rpcResult = new RpcResult(); - rpcResult.setException(new LocalException("localException")); + AppResponse appResponse = new AppResponse(); + appResponse.setException(new LocalException("localException")); Invoker invoker = mock(Invoker.class); - when(invoker.invoke(invocation)).thenReturn(rpcResult); + when(invoker.invoke(invocation)).thenReturn(appResponse); when(invoker.getInterface()).thenReturn(DemoService.class); Result newResult = exceptionFilter.invoke(invoker, invocation); - Assertions.assertEquals(rpcResult.getException(), newResult.getException()); + Assertions.assertEquals(appResponse.getException(), newResult.getException()); } @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 = AsyncRpcResult.newDefaultAsyncResult(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); + AppResponse appResponse = (AppResponse) asyncResult.get(); + exceptionFilter.listener().onResponse(appResponse, invoker, invocation); - Assertions.assertEquals(newResult.getException().getClass(), RuntimeException.class); - Assertions.assertEquals(newResult.getException().getMessage(), StringUtils.toString(rpcResult.getException())); + Assertions.assertFalse(appResponse.getException() instanceof HessianException); + Assertions.assertEquals(appResponse.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 f7639f7f57f..46bc99ce9c4 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 @@ -17,7 +17,13 @@ package org.apache.dubbo.rpc.filter; 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; @@ -54,14 +60,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")); + AppResponse appResponse = (AppResponse) asyncResult.get(); + genericFilter.listener().onResponse(appResponse, invoker, invocation); + Assertions.assertEquals(HashMap.class, appResponse.getValue().getClass()); + Assertions.assertEquals(10, ((HashMap) appResponse.getValue()).get("age")); } @@ -81,7 +89,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); @@ -104,7 +112,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); @@ -128,7 +136,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 5adbdb728ab..73e0d82b6c0 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 @@ -17,11 +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.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; @@ -60,11 +61,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(AsyncRpcResult.newDefaultAsyncResult(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()); @@ -80,12 +84,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(AsyncRpcResult.newDefaultAsyncResult(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()); } @@ -105,7 +111,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 275ddabcdc6..839416fa9c9 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 @@ -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.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -46,7 +46,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(TOKEN_KEY, token); @@ -65,7 +65,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(TOKEN_KEY, "wrongToken"); @@ -84,7 +84,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 44e330724c8..f28022dca38 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 @@ -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-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..57f1f1e6cc5 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,12 @@ 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; /** * MockInvoker.java @@ -58,15 +59,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 AsyncRpcResult.newDefaultAsyncResult(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 7e26606bd99..0ebe357f780 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 @@ -30,6 +30,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; @@ -155,7 +156,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 54d74d3ef6c..101851240c0 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 @@ -24,25 +24,27 @@ 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; -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.remoting.Constants.SENT_KEY; -import static org.apache.dubbo.rpc.protocol.dubbo.Constants.CALLBACK_SERVICE_KEY; -import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; +import static org.apache.dubbo.rpc.protocol.dubbo.Constants.CALLBACK_SERVICE_KEY; /** + * Server push uses this Invoker to continuously push data to client. * Wrap the existing invoker on the channel. */ class ChannelWrappedInvoker extends AbstractInvoker { @@ -66,15 +68,20 @@ protected Result doInvoke(Invocation invocation) throws Throwable { inv.setAttachment(CALLBACK_SERVICE_KEY, serviceKey); try { - if (getUrl().getMethodParameter(invocation.getMethodName(), ASYNC_KEY, false)) { // may have concurrency issue + if (RpcUtils.isOneway(getUrl(), inv)) { // may have concurrency issue currentClient.send(inv, getUrl().getMethodParameter(invocation.getMethodName(), SENT_KEY, false)); - return new RpcResult(); - } - int timeout = getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, DEFAULT_TIMEOUT); - if (timeout > 0) { - return (Result) currentClient.request(inv, timeout).get(); + return AsyncRpcResult.newDefaultAsyncResult(invocation); } else { - return (Result) currentClient.request(inv).get(); + CompletableFuture responseFuture = currentClient.request(inv); + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv); + responseFuture.whenComplete((appResponse, t) -> { + if (t != null) { + asyncRpcResult.completeExceptionally(t); + } else { + asyncRpcResult.complete((AppResponse) appResponse); + } + }); + return asyncRpcResult; } } 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 0edfdb8f3a3..9515a1ba51d 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 fab4dd82b9c..1b1cd1414fd 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 @@ -23,8 +23,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; @@ -76,7 +76,7 @@ private void logMessageLength(Object result, int bytes) { } } else if (result instanceof Response) { try { - ((RpcResult) ((Response) result).getResult()).setAttachment(OUTPUT_KEY, String.valueOf(bytes)); + ((AppResponse) ((Response) result).getResult()).setAttachment(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 cc0b9e7e225..f40b664e160 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; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; @@ -88,32 +87,25 @@ 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, TIMEOUT_KEY, 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(); + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv); + CompletableFuture responseFuture = currentClient.request(inv, timeout); + responseFuture.whenComplete((obj, t) -> { + if (t != null) { + asyncRpcResult.completeExceptionally(t); + } else { + asyncRpcResult.complete((AppResponse) obj); + } + }); + RpcContext.getContext().setFuture(new FutureAdapter(asyncRpcResult)); + return asyncRpcResult; } } 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 f1a627d8c7a..7f9281cc5da 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 @@ -36,7 +36,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; @@ -57,6 +56,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; @@ -148,16 +148,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.completionFuture().thenApply(Function.identity()); } @Override @@ -407,7 +400,7 @@ private void optimizeSerialization(URL url) throws RpcException { } @Override - public Invoker refer(Class serviceType, URL url) throws RpcException { + public Invoker protocolBindingRefer(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..03954d1b345 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.AppResponse; 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; + private CompletableFuture appResponseFuture; - 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.appResponseFuture = future; + future.whenComplete((appResponse, t) -> { + if (t != null) { + if (t instanceof CompletionException) { + t = t.getCause(); + } + this.completeExceptionally(t); + } else { + if (appResponse.hasException()) { + this.completeExceptionally(appResponse.getException()); + } else { + this.complete((V) appResponse.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 appResponseFuture.cancel(mayInterruptIfRunning); } @Override public boolean isCancelled() { - return false; + return appResponseFuture.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 eadbf20681a..a495775d111 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 @@ -26,9 +26,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; @@ -87,7 +87,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); @@ -108,7 +108,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 fd32951b320..c7074aa9dbe 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; import static org.apache.dubbo.remoting.Constants.RECONNECT_KEY; @@ -56,7 +56,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); } @@ -76,7 +76,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 a64dc2b66e7..7b1c17c7517 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; @@ -39,10 +38,14 @@ * EventFilter */ @Activate(group = CommonConstants.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); @@ -51,37 +54,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) { @@ -226,4 +198,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 804f05856b7..7c6a34c40bf 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 @@ -24,6 +24,7 @@ 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; @@ -63,7 +64,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.protocolBindingRefer(IDemoService.class, url); Assertions.assertEquals(true, invoker.isAvailable()); invoker.destroy(); Assertions.assertEquals(false, invoker.isAvailable()); @@ -74,7 +75,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.protocolBindingRefer(IDemoService.class, url); Assertions.assertEquals(true, invoker.isAvailable()); getClients(invoker)[0].setAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY, Boolean.TRUE); @@ -92,7 +93,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.protocolBindingRefer(IDemoService.class, url); long start = System.currentTimeMillis(); @@ -114,7 +115,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.protocolBindingRefer(IDemoService.class, url); ExchangeClient[] clients = getClients(invoker); clients[0].close(); @@ -127,11 +128,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) { @@ -141,7 +141,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 2429b4b9580..500a95eeacb 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 @@ -17,12 +17,12 @@ package org.apache.dubbo.rpc.protocol.dubbo; 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&" + 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 fd5979b7469..0a3a9d161b5 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 @@ -236,7 +236,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 1f01e658c45..ca5a306df43 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 @@ -25,7 +25,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; @@ -273,8 +275,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-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 abbfe82dac6..7d621913928 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 @@ -47,8 +47,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; +import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.RpcConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; 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 cc273aafbe3..3e681b27117 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 @@ -93,7 +93,7 @@ public Exporter export(Invoker invoker) throws RpcException { } @Override - public Invoker refer(Class serviceType, URL url) throws RpcException { + public Invoker protocolBindingRefer(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 2633e961d85..b056a795ee9 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.URL; import org.apache.dubbo.common.constants.RemotingConstants; +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 protocolBindingRefer(final Class type, final URL url) throws RpcException { try { String address = url.getAddress(); String backup = url.getParameter(RemotingConstants.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-native-thrift/pom.xml b/dubbo-rpc/dubbo-rpc-native-thrift/pom.xml index 524cd1a706b..bcc3cf9ec8a 100644 --- a/dubbo-rpc/dubbo-rpc-native-thrift/pom.xml +++ b/dubbo-rpc/dubbo-rpc-native-thrift/pom.xml @@ -28,7 +28,7 @@ false - + org.apache.dubbo diff --git a/dubbo-rpc/dubbo-rpc-native-thrift/src/main/java/org/apache/dubbo/rpc/protocol/nativethrift/ThriftProtocol.java b/dubbo-rpc/dubbo-rpc-native-thrift/src/main/java/org/apache/dubbo/rpc/protocol/nativethrift/ThriftProtocol.java index c869c60337b..028d3cf42e2 100644 --- a/dubbo-rpc/dubbo-rpc-native-thrift/src/main/java/org/apache/dubbo/rpc/protocol/nativethrift/ThriftProtocol.java +++ b/dubbo-rpc/dubbo-rpc-native-thrift/src/main/java/org/apache/dubbo/rpc/protocol/nativethrift/ThriftProtocol.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol; + import org.apache.thrift.TException; import org.apache.thrift.TMultiplexedProcessor; import org.apache.thrift.TProcessor; 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 2601a09b48f..b5081887c2c 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.Serialization; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Constants; +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; @@ -71,7 +71,7 @@ private Serialization getSerialization(URL url) { } @Override - public Invoker refer(final Class type, final URL url) throws RpcException { + protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { try { GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setTestOnBorrow(url.getParameter("test.on.borrow", true)); @@ -122,10 +122,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); @@ -138,13 +138,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/pom.xml b/dubbo-rpc/dubbo-rpc-rest/pom.xml index c1d9e4e727b..b162030fd8d 100644 --- a/dubbo-rpc/dubbo-rpc-rest/pom.xml +++ b/dubbo-rpc/dubbo-rpc-rest/pom.xml @@ -91,22 +91,22 @@ io.swagger swagger-annotations - - - javax.ws.rs - jsr311-api - - + + + javax.ws.rs + jsr311-api + + io.swagger swagger-jaxrs - - - javax.ws.rs - jsr311-api - - + + + javax.ws.rs + jsr311-api + + 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 cbf16872840..d570b2f01f4 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 @@ -21,6 +21,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; @@ -30,11 +31,11 @@ import java.net.SocketTimeoutException; import java.rmi.RemoteException; +import static org.apache.dubbo.common.Version.isRelease263OrHigher; +import static org.apache.dubbo.common.Version.isRelease270OrHigher; import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; import static org.apache.dubbo.common.constants.RpcConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; -import static org.apache.dubbo.common.Version.isRelease263OrHigher; -import static org.apache.dubbo.common.Version.isRelease270OrHigher; /** * RmiProtocol. 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 e37fab6a68b..14c2947017a 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 @@ -25,9 +25,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; @@ -287,7 +287,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())); @@ -378,15 +378,15 @@ private Object decode(TProtocol protocol) response.setId(id); - RpcResult rpcResult = new RpcResult(); + AppResponse appResponse = new AppResponse(); if (realResult instanceof Throwable) { - rpcResult.setException((Throwable) realResult); + appResponse.setException((Throwable) realResult); } else { - rpcResult.setValue(realResult); + appResponse.setValue(realResult); } - response.setResult(rpcResult); + response.setResult(appResponse); return response; @@ -539,7 +539,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()); @@ -613,7 +613,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 7d29f5813af..3b0e3d9352f 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,7 @@ import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.ExchangeClient; +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 +30,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; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; @@ -91,10 +94,11 @@ protected Result doInvoke(Invocation invocation) throws Throwable { try { int timeout = getUrl().getMethodParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT); - RpcContext.getContext().setFuture(null); - - return (Result) currentClient.request(inv, timeout).get(); - + AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation); + CompletableFuture responseFuture = currentClient.request(inv, timeout); + asyncRpcResult.subscribeTo(responseFuture); + RpcContext.getContext().setFuture(new FutureAdapter(asyncRpcResult)); + return asyncRpcResult; } 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 0da5ee8be6f..d6a88cbe4e4 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,10 +43,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; -import static org.apache.dubbo.rpc.Constants.IS_SERVER_KEY; import static org.apache.dubbo.common.constants.RpcConstants.CONNECTIONS_KEY; +import static org.apache.dubbo.rpc.Constants.IS_SERVER_KEY; /** * @since 2.7.0, use https://github.com/dubbo/dubbo-rpc-native-thrift instead @@ -88,8 +90,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.completionFuture().thenApply(Function.identity()); } throw new RemotingException(channel, @@ -162,7 +164,7 @@ public void destroy() { } // ~ end of method destroy @Override - public Invoker refer(Class type, URL url) throws RpcException { + protected Invoker protocolBindingRefer(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 93421c4bcfb..8cdbae7f99e 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 @@ -23,9 +23,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; @@ -189,13 +189,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()); } @@ -259,9 +259,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()); @@ -278,11 +278,11 @@ public void testEncodeReplyResponse() throws Exception { Request request = createRequest(); - RpcResult rpcResult = new RpcResult(); - rpcResult.setResult("Hello, World!"); + AppResponse appResponse = new AppResponse(); + appResponse.setValue("Hello, World!"); Response response = new Response(); - response.setResult(rpcResult); + response.setResult(appResponse); response.setId(request.getId()); ChannelBuffer bos = ChannelBuffers.dynamicBuffer(1024); @@ -324,7 +324,7 @@ public void testEncodeReplyResponse() throws Exception { result.read(protocol); protocol.readMessageEnd(); - Assertions.assertEquals(rpcResult.getValue(), result.getSuccess()); + Assertions.assertEquals(appResponse.getValue(), result.getSuccess()); } @Test @@ -336,12 +336,12 @@ public void testEncodeExceptionResponse() throws Exception { Request request = createRequest(); - RpcResult rpcResult = new RpcResult(); + AppResponse appResponse = new AppResponse(); String exceptionMessage = "failed"; - rpcResult.setException(new RuntimeException(exceptionMessage)); + appResponse.setException(new RuntimeException(exceptionMessage)); Response response = new Response(); - response.setResult(rpcResult); + response.setResult(appResponse); response.setId(request.getId()); ChannelBuffer bos = ChannelBuffers.dynamicBuffer(1024); diff --git a/dubbo-rpc/dubbo-rpc-xml/src/main/java/org/apache/dubbo/xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java b/dubbo-rpc/dubbo-rpc-xml/src/main/java/org/apache/dubbo/xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java index c62bd8c85b8..68927d7bd1f 100644 --- a/dubbo-rpc/dubbo-rpc-xml/src/main/java/org/apache/dubbo/xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java +++ b/dubbo-rpc/dubbo-rpc-xml/src/main/java/org/apache/dubbo/xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java @@ -16,16 +16,6 @@ */ package org.apache.dubbo.xml.rpc.protocol.xmlrpc; -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.apache.dubbo.common.URL; import org.apache.dubbo.remoting.http.HttpBinder; import org.apache.dubbo.remoting.http.HttpHandler; @@ -33,6 +23,7 @@ import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol; + import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcRequest; import org.apache.xmlrpc.server.PropertyHandlerMapping; @@ -41,6 +32,15 @@ import org.apache.xmlrpc.webserver.XmlRpcServletServer; import org.springframework.remoting.RemoteAccessException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class XmlRpcProtocol extends AbstractProxyProtocol { public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";