Async optimization (#3738)
* Result implement CF * Result implement CF * Result implement CF * Add AsyncRpcResult * Fix bugs and refactor Filter * Try to add onSend onError for Filter * invoke different filter method according to result status. * make generic work with async call, including add $invokeAsync * refactor legacy Filter implementation to work with onResponse. * demo changes * Fixes #3620, provider attachment lose on consumer side, fix this by reverting RpcContext copy * AsyncRpcResult should always holds an Invocation instance * refactor filter signature * reimplement embedded Filters * use ProviderModel modification in 3.x * Fix address notification processing workflow after merging 3.x branch * Fix UT * Fix UT * Unit test of JValidator; Clean code of JValidator (#3723) * Fixes #3625 (#3730) use constant to replace magic number * Fix conflict when merging master and 3.x * Fix conflict when merging master and 3.x * Result interface itself has Future status. * Fix DefaultFuture UT * Wrap all protocol Invoker with AsyncToSyncInvoker & Fix UT * Add license * fix UT * Fix ut in MonitorFilterTest * avoid duplicate async to sync wrapper * return async result in CacheFilter. * fix UT in CacheFilterTest * Add generic condition check to GenericFilter callback. * Fix UT * Get generic from RpcContext if the value in Invocation is empty. * Fix RSocketProtocol to meet AbstractProtocol adjustment * rename RpcResult to AppResponse to help avoid confusion with AsyncRpcResult. * RSocket module switch to AsyncRpcResult
This commit is contained in:
parent
91554bc84b
commit
003e400b6f
|
|
@ -24,11 +24,11 @@ import org.apache.dubbo.common.timer.Timeout;
|
|||
import org.apache.dubbo.common.timer.Timer;
|
||||
import org.apache.dubbo.common.timer.TimerTask;
|
||||
import org.apache.dubbo.common.utils.NamedThreadFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.cluster.support;
|
|||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* NOTICE! This implementation does not work well with async call.
|
||||
*
|
||||
* Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources.
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
|
||||
|
|
@ -66,7 +68,6 @@ public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
} else {
|
||||
selected = new ArrayList<>();
|
||||
for (int i = 0; i < forks; i++) {
|
||||
// TODO. Add some comment here, refer chinese version for more details.
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
|
||||
if (!selected.contains(invoker)) {
|
||||
//Avoid add the same invoker several times.
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ import org.apache.dubbo.common.logger.Logger;
|
|||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ConfigUtils;
|
||||
import org.apache.dubbo.common.utils.NamedThreadFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
import org.apache.dubbo.rpc.cluster.Merger;
|
||||
|
|
@ -41,12 +41,13 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* NOTICE! Does not work with async call.
|
||||
* @param <T>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
|
|
@ -86,26 +87,19 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
returnType = null;
|
||||
}
|
||||
|
||||
Map<String, Future<Result>> results = new HashMap<String, Future<Result>>();
|
||||
Map<String, Result> results = new HashMap<>();
|
||||
for (final Invoker<T> invoker : invokers) {
|
||||
Future<Result> future = executor.submit(new Callable<Result>() {
|
||||
@Override
|
||||
public Result call() throws Exception {
|
||||
return invoker.invoke(new RpcInvocation(invocation, invoker));
|
||||
}
|
||||
});
|
||||
results.put(invoker.getUrl().getServiceKey(), future);
|
||||
results.put(invoker.getUrl().getServiceKey(), invoker.invoke(new RpcInvocation(invocation, invoker)));
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
|
||||
List<Result> resultList = new ArrayList<Result>(results.size());
|
||||
|
||||
int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
|
||||
for (Map.Entry<String, Future<Result>> entry : results.entrySet()) {
|
||||
Future<Result> future = entry.getValue();
|
||||
for (Map.Entry<String, Result> entry : results.entrySet()) {
|
||||
Result asyncResult = entry.getValue();
|
||||
try {
|
||||
Result r = future.get(timeout, TimeUnit.MILLISECONDS);
|
||||
Result r = asyncResult.get();
|
||||
if (r.hasException()) {
|
||||
log.error("Invoke " + getGroupDescFromServiceKey(entry.getKey()) +
|
||||
" failed: " + r.getException().getMessage(),
|
||||
|
|
@ -119,13 +113,13 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
}
|
||||
|
||||
if (resultList.isEmpty()) {
|
||||
return new RpcResult((Object) null);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(invocation);
|
||||
} else if (resultList.size() == 1) {
|
||||
return resultList.iterator().next();
|
||||
}
|
||||
|
||||
if (returnType == void.class) {
|
||||
return new RpcResult((Object) null);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(invocation);
|
||||
}
|
||||
|
||||
if (merger.startsWith(".")) {
|
||||
|
|
@ -173,7 +167,7 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
throw new RpcException("There is no merger to merge result.");
|
||||
}
|
||||
}
|
||||
return new RpcResult(result);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(result, invocation);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import org.apache.dubbo.common.logger.Logger;
|
|||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.support.MockInvoker;
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ public class MockClusterInvoker<T> implements Invoker<T> {
|
|||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ package org.apache.dubbo.rpc.cluster;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
|
@ -48,7 +48,7 @@ public class StickyTest {
|
|||
private Invoker<StickyTest> invoker2 = mock(Invoker.class);
|
||||
private RpcInvocation invocation;
|
||||
private Directory<StickyTest> dic;
|
||||
private Result result = new RpcResult();
|
||||
private Result result = new AppResponse();
|
||||
private StickyClusterInvoker<StickyTest> clusterinvoker = null;
|
||||
private URL url = URL.valueOf("test://test:11/test?"
|
||||
+ "&loadbalance=roundrobin"
|
||||
|
|
|
|||
|
|
@ -51,6 +51,16 @@ public class MockDirInvocation implements Invocation {
|
|||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttachment(String key, String value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttachmentIfAbsent(String key, String value) {
|
||||
|
||||
}
|
||||
|
||||
public Invoker<?> getInvoker() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ package org.apache.dubbo.rpc.cluster.router.file;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
import org.apache.dubbo.rpc.cluster.RouterFactory;
|
||||
|
|
@ -52,7 +52,7 @@ public class FileRouterEngineTest {
|
|||
Invoker<FileRouterEngineTest> invoker2 = mock(Invoker.class);
|
||||
Invocation invocation;
|
||||
StaticDirectory<FileRouterEngineTest> dic;
|
||||
Result result = new RpcResult();
|
||||
Result result = new AppResponse();
|
||||
private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
|
||||
|
||||
@BeforeAll
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.cluster.support;
|
|||
|
||||
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<DemoService> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<DemoService> dic;
|
||||
Result result = new RpcResult();
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ package org.apache.dubbo.rpc.cluster.support;
|
|||
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<FailbackClusterInvokerTest> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<FailbackClusterInvokerTest> dic;
|
||||
Result result = new RpcResult();
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
|
|
|
|||
|
|
@ -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<FailfastClusterInvokerTest> invoker1 = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<FailfastClusterInvokerTest> dic;
|
||||
Result result = new RpcResult();
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
|
|
|
|||
|
|
@ -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<FailoverClusterInvokerTest> invoker2 = mock(Invoker.class);
|
||||
private RpcInvocation invocation = new RpcInvocation();
|
||||
private Directory<FailoverClusterInvokerTest> dic;
|
||||
private Result result = new RpcResult();
|
||||
private Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
|
|
|
|||
|
|
@ -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<ForkingClusterInvokerTest> invoker3 = mock(Invoker.class);
|
||||
private RpcInvocation invocation = new RpcInvocation();
|
||||
private Directory<ForkingClusterInvokerTest> dic;
|
||||
private Result result = new RpcResult();
|
||||
private Result result = new AppResponse();
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ package org.apache.dubbo.rpc.cluster.support;
|
|||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
|
@ -119,7 +120,7 @@ public class MergeableClusterInvokerTest {
|
|||
return MenuService.class;
|
||||
}
|
||||
if ("invoke".equals(method.getName())) {
|
||||
return new RpcResult(firstMenu);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -135,7 +136,7 @@ public class MergeableClusterInvokerTest {
|
|||
return MenuService.class;
|
||||
}
|
||||
if ("invoke".equals(method.getName())) {
|
||||
return new RpcResult(secondMenu);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -195,14 +196,14 @@ public class MergeableClusterInvokerTest {
|
|||
given(firstInvoker.getUrl()).willReturn(
|
||||
url.addParameter(Constants.GROUP_KEY, "first"));
|
||||
given(firstInvoker.getInterface()).willReturn(MenuService.class);
|
||||
given(firstInvoker.invoke(invocation)).willReturn(new RpcResult())
|
||||
given(firstInvoker.invoke(invocation)).willReturn(new AppResponse())
|
||||
;
|
||||
given(firstInvoker.isAvailable()).willReturn(true);
|
||||
|
||||
given(secondInvoker.getUrl()).willReturn(
|
||||
url.addParameter(Constants.GROUP_KEY, "second"));
|
||||
given(secondInvoker.getInterface()).willReturn(MenuService.class);
|
||||
given(secondInvoker.invoke(invocation)).willReturn(new RpcResult())
|
||||
given(secondInvoker.invoke(invocation)).willReturn(new AppResponse())
|
||||
;
|
||||
given(secondInvoker.isAvailable()).willReturn(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ public class Constants {
|
|||
|
||||
public static final String $INVOKE = "$invoke";
|
||||
|
||||
public static final String $INVOKE_ASYNC = "$invokeAsync";
|
||||
|
||||
public static final String $ECHO = "$echo";
|
||||
|
||||
public static final int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32);
|
||||
|
|
@ -827,16 +829,16 @@ public class Constants {
|
|||
*/
|
||||
public static final String DEVELOPMENT_ENVIRONMENT = "develop";
|
||||
|
||||
/**
|
||||
* Production environment key.
|
||||
*/
|
||||
public static final String PRODUCTION_ENVIRONMENT = "product";
|
||||
|
||||
/**
|
||||
* Consumer side 's proxy class
|
||||
*/
|
||||
public static final String PROXY_CLASS_REF = "refClass";
|
||||
|
||||
/**
|
||||
* Production environment key.
|
||||
*/
|
||||
public static final String PRODUCTION_ENVIRONMENT = "product";
|
||||
|
||||
public static final String ETCD3_NOTIFY_MAXTHREADS_KEYS = "etcd3.notify.maxthreads";
|
||||
|
||||
public static final int DEFAULT_ETCD3_NOTIFY_THREADS = DEFAULT_IO_THREADS;
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ public interface Logger {
|
|||
|
||||
/**
|
||||
* Is debug logging currently enabled?
|
||||
*
|
||||
*
|
||||
* @return true if debug is enabled
|
||||
*/
|
||||
boolean isDebugEnabled();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import java.lang.reflect.GenericArrayType;
|
|||
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.List;
|
|||
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 final class ReflectUtils {
|
|||
|
||||
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};
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.dubbo.common.config;
|
||||
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,15 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setAttachmentIfAbsent(String key, String value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setAttachment(String key, String value) {
|
||||
|
||||
}
|
||||
|
||||
class CompatibleInvocation implements Invocation {
|
||||
|
||||
private org.apache.dubbo.rpc.Invocation delegate;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,42 @@
|
|||
|
||||
package com.alibaba.dubbo.rpc;
|
||||
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Deprecated
|
||||
public interface Result extends org.apache.dubbo.rpc.Result {
|
||||
|
||||
@Override
|
||||
default void setValue(Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setException(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default org.apache.dubbo.rpc.Result thenApplyWithContext(Function<AppResponse, AppResponse> fn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default <U> CompletableFuture<U> thenApply(Function<org.apache.dubbo.rpc.Result, ? extends U> fn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default org.apache.dubbo.rpc.Result get() throws InterruptedException, ExecutionException {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
class CompatibleResult implements Result {
|
||||
private org.apache.dubbo.rpc.Result delegate;
|
||||
|
||||
|
|
@ -53,11 +84,6 @@ public interface Result extends org.apache.dubbo.rpc.Result {
|
|||
return delegate.recreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult() {
|
||||
return delegate.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttachments() {
|
||||
return delegate.getAttachments();
|
||||
|
|
|
|||
|
|
@ -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 class LegacyInvoker<T> implements Invoker<T> {
|
|||
}
|
||||
|
||||
public Result invoke(Invocation invocation) throws RpcException {
|
||||
RpcResult result = new RpcResult();
|
||||
AppResponse result = new AppResponse();
|
||||
if (hasException == false) {
|
||||
result.setValue("alibaba");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ public class MockInvocation implements Invocation {
|
|||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttachment(String key, String value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttachmentIfAbsent(String key, String value) {
|
||||
|
||||
}
|
||||
|
||||
public Invoker<?> getInvoker() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ public class ReferenceConfig<T> extends AbstractReferenceConfig {
|
|||
checkStubAndLocal(interfaceClass);
|
||||
checkMock(interfaceClass);
|
||||
|
||||
ConsumerModel consumerModel = new ConsumerModel(interfaceName, group, version, interfaceClass);
|
||||
ConsumerModel consumerModel = new ConsumerModel(interfaceName, group, version, getActualInterface());
|
||||
ApplicationModel.initConsumerModel(URL.buildKey(interfaceName, group, version), consumerModel);
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
|
|
@ -321,6 +321,17 @@ public class ReferenceConfig<T> extends AbstractReferenceConfig {
|
|||
consumerModel.getServiceMetadata().addAttribute(Constants.PROXY_CLASS_REF, ref);
|
||||
}
|
||||
|
||||
private Class<?> getActualInterface() {
|
||||
Class actualInterface = interfaceClass;
|
||||
if (interfaceClass == GenericService.class) {
|
||||
try {
|
||||
actualInterface = Class.forName(interfaceName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return actualInterface;
|
||||
}
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
|
||||
private T createProxy(Map<String, String> map) {
|
||||
if (shouldJvmRefer(map)) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ import java.util.concurrent.CountDownLatch;
|
|||
*/
|
||||
|
||||
public class CacheListener implements DataListener {
|
||||
private static final int MIN_PATH_DEPTH = 5;
|
||||
|
||||
private Map<String, Set<ConfigurationListener>> keyListeners = new ConcurrentHashMap<>();
|
||||
private CountDownLatch initializedLatch;
|
||||
private String rootPath;
|
||||
|
|
@ -89,7 +91,7 @@ public class CacheListener implements DataListener {
|
|||
// TODO We limit the notification of config changes to a specific path level, for example
|
||||
// /dubbo/config/service/configurators, other config changes not in this level will not get notified,
|
||||
// say /dubbo/config/dubbo.properties
|
||||
if (path.split("/").length >= 5) {
|
||||
if (path.split("/").length >= MIN_PATH_DEPTH) {
|
||||
String key = pathToKey(path);
|
||||
ConfigChangeType changeType;
|
||||
switch (eventType) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import org.apache.dubbo.demo.DemoService;
|
|||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component("demoServiceComponent")
|
||||
public class DemoServiceComponent implements DemoService {
|
||||
@Reference
|
||||
|
|
@ -33,4 +35,9 @@ public class DemoServiceComponent implements DemoService {
|
|||
public String sayHello(String name) {
|
||||
return demoService.sayHello(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> sayHelloAsync(String name) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Service
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
|
||||
|
|
@ -35,4 +37,9 @@ public class DemoServiceImpl implements DemoService {
|
|||
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> sayHelloAsync(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
|
||||
|
||||
|
|
@ -31,4 +33,9 @@ public class DemoServiceImpl implements DemoService {
|
|||
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> sayHelloAsync(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@
|
|||
*/
|
||||
package org.apache.dubbo.demo;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface DemoService {
|
||||
|
||||
String sayHello(String name);
|
||||
|
||||
CompletableFuture<String> sayHelloAsync(String name);
|
||||
}
|
||||
|
|
@ -20,16 +20,21 @@ import org.apache.dubbo.demo.DemoService;
|
|||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Application {
|
||||
/**
|
||||
* In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
|
||||
* launch the application
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws Exception {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
|
||||
context.start();
|
||||
DemoService demoService = context.getBean("demoService", DemoService.class);
|
||||
String hello = demoService.sayHello("world");
|
||||
System.out.println("result: " + hello);
|
||||
// String hello = demoService.sayHello("world");
|
||||
CompletableFuture<String> helloFuture = demoService.sayHelloAsync("world");
|
||||
// System.out.println("result: " + hello);
|
||||
System.out.println("result: " + helloFuture.get());
|
||||
System.in.read();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,6 @@
|
|||
|
||||
<!-- generate proxy for the remote service, then demoService can be used in the same way as the
|
||||
local regular interface -->
|
||||
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
|
||||
<dubbo:reference timeout="600000" id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
|
||||
|
||||
|
|
@ -31,4 +33,14 @@ public class DemoServiceImpl implements DemoService {
|
|||
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> sayHelloAsync(String name) {
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return CompletableFuture.completedFuture("future return value!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import org.apache.dubbo.common.Constants;
|
|||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.ConfigUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
|
@ -95,9 +95,9 @@ public class CacheFilter implements Filter {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ import org.apache.dubbo.cache.support.jcache.JCacheFactory;
|
|||
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 class CacheFilterTest {
|
|||
|
||||
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 class CacheFilterTest {
|
|||
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 class CacheFilterTest {
|
|||
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 class CacheFilterTest {
|
|||
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 class CacheFilterTest {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ package org.apache.dubbo.validation.filter;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.ConfigUtils;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.validation.Validation;
|
||||
import org.apache.dubbo.validation.Validator;
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ public class ValidationFilter implements Filter {
|
|||
} catch (RpcException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
return new RpcResult(t);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
|
||||
}
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class JValidator implements Validator {
|
|||
factory = Validation.buildDefaultValidatorFactory();
|
||||
}
|
||||
this.validator = factory.getValidator();
|
||||
this.methodClassMap = new ConcurrentHashMap<String, Class>();
|
||||
this.methodClassMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private static boolean isPrimitives(Class<?> cls) {
|
||||
|
|
@ -117,7 +117,7 @@ public class JValidator implements Validator {
|
|||
String parameterClassName = generateMethodParameterClassName(clazz, method);
|
||||
Class<?> parameterClass;
|
||||
try {
|
||||
parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
|
||||
parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
|
||||
CtClass ctClass = pool.makeClass(parameterClassName);
|
||||
|
|
@ -243,14 +243,14 @@ public class JValidator implements Validator {
|
|||
|
||||
@Override
|
||||
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
|
||||
List<Class<?>> groups = new ArrayList<Class<?>>();
|
||||
List<Class<?>> groups = new ArrayList<>();
|
||||
Class<?> methodClass = methodClass(methodName);
|
||||
if (methodClass != null) {
|
||||
groups.add(methodClass);
|
||||
}
|
||||
Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
|
||||
Set<ConstraintViolation<?>> violations = new HashSet<>();
|
||||
Method method = clazz.getMethod(methodName, parameterTypes);
|
||||
Class<?>[] methodClasses = null;
|
||||
Class<?>[] methodClasses;
|
||||
if (method.isAnnotationPresent(MethodValidated.class)){
|
||||
methodClasses = method.getAnnotation(MethodValidated.class).value();
|
||||
groups.addAll(Arrays.asList(methodClasses));
|
||||
|
|
@ -260,7 +260,7 @@ public class JValidator implements Validator {
|
|||
groups.add(1, clazz);
|
||||
|
||||
// convert list to array
|
||||
Class<?>[] classgroups = groups.toArray(new Class[0]);
|
||||
Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);
|
||||
|
||||
Object parameterBean = getMethodParameterBean(clazz, method, arguments);
|
||||
if (parameterBean != null) {
|
||||
|
|
|
|||
|
|
@ -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 class ValidationFilterTest {
|
|||
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 class ValidationFilterTest {
|
|||
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 class ValidationFilterTest {
|
|||
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 class ValidationFilterTest {
|
|||
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 class ValidationFilterTest {
|
|||
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});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JValidatorTest {
|
||||
@Test
|
||||
|
|
@ -56,4 +60,27 @@ public class JValidatorTest {
|
|||
JValidator jValidator = new JValidator(url);
|
||||
jValidator.validate("someMethod2", new Class<?>[]{ValidationParameter.class}, new Object[]{new ValidationParameter("NotBeNull")});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItWithArrayArg() throws Exception {
|
||||
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
|
||||
JValidator jValidator = new JValidator(url);
|
||||
jValidator.validate("someMethod3", new Class<?>[]{ValidationParameter[].class}, new Object[]{new ValidationParameter[]{new ValidationParameter("parameter")}});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItWithCollectionArg() throws Exception {
|
||||
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
|
||||
JValidator jValidator = new JValidator(url);
|
||||
jValidator.validate("someMethod4", new Class<?>[]{List.class}, new Object[]{Arrays.asList("parameter")});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItWithMapArg() throws Exception {
|
||||
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
|
||||
JValidator jValidator = new JValidator(url);
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("key", "value");
|
||||
jValidator.validate("someMethod5", new Class<?>[]{Map.class}, new Object[]{map});
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ package org.apache.dubbo.validation.support.jvalidation.mock;
|
|||
import org.apache.dubbo.validation.MethodValidated;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface JValidatorTestTarget {
|
||||
@MethodValidated
|
||||
|
|
@ -27,6 +29,12 @@ public interface JValidatorTestTarget {
|
|||
@MethodValidated(Test2.class)
|
||||
public void someMethod2(@NotNull ValidationParameter validationParameter);
|
||||
|
||||
public void someMethod3(ValidationParameter[] parameters);
|
||||
|
||||
public void someMethod4(List<String> strings);
|
||||
|
||||
public void someMethod5(Map<String, String> map);
|
||||
|
||||
@interface Test2 {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,4 +86,5 @@ public interface MonitorService {
|
|||
*/
|
||||
List<URL> lookup(URL query);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -25,9 +25,9 @@ import org.apache.dubbo.common.utils.NetUtils;
|
|||
import org.apache.dubbo.monitor.Monitor;
|
||||
import org.apache.dubbo.monitor.MonitorFactory;
|
||||
import org.apache.dubbo.monitor.MonitorService;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
|
@ -41,10 +41,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
* MonitorFilter. (SPI, Singleton, ThreadSafe)
|
||||
*/
|
||||
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
|
||||
public class MonitorFilter implements Filter {
|
||||
public class MonitorFilter extends ListenableFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
|
||||
private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time";
|
||||
|
||||
public MonitorFilter() {
|
||||
super.listener = new MonitorListener();
|
||||
}
|
||||
/**
|
||||
* The Concurrent counter
|
||||
*/
|
||||
|
|
@ -59,6 +63,7 @@ public class MonitorFilter implements Filter {
|
|||
this.monitorFactory = monitorFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The invocation interceptor,it will collect the invoke data about this invocation and send it to monitor center
|
||||
*
|
||||
|
|
@ -70,105 +75,10 @@ public class MonitorFilter implements Filter {
|
|||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
|
||||
RpcContext context = RpcContext.getContext(); // provider must fetch context before invoke() gets called
|
||||
String remoteHost = context.getRemoteHost();
|
||||
long start = System.currentTimeMillis(); // record start timestamp
|
||||
invocation.setAttachment(MONITOR_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
|
||||
getConcurrent(invoker, invocation).incrementAndGet(); // count up
|
||||
try {
|
||||
Result result = invoker.invoke(invocation); // proceed invocation chain
|
||||
collect(invoker, invocation, result, remoteHost, start, false);
|
||||
return result;
|
||||
} catch (RpcException e) {
|
||||
collect(invoker, invocation, null, remoteHost, start, true);
|
||||
throw e;
|
||||
} finally {
|
||||
getConcurrent(invoker, invocation).decrementAndGet(); // count down
|
||||
}
|
||||
} else {
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The collector logic, it will be handled by the default monitor
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @param result the invoke result
|
||||
* @param remoteHost the remote host address
|
||||
* @param start the timestamp the invoke begin
|
||||
* @param error if there is an error on the invoke
|
||||
*/
|
||||
private void collect(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
|
||||
try {
|
||||
URL monitorUrl = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY);
|
||||
Monitor monitor = monitorFactory.getMonitor(monitorUrl);
|
||||
if (monitor == null) {
|
||||
return;
|
||||
}
|
||||
URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error);
|
||||
monitor.collect(statisticsURL);
|
||||
} catch (Throwable t) {
|
||||
logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create statistics url
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @param result
|
||||
* @param remoteHost
|
||||
* @param start
|
||||
* @param error
|
||||
* @return
|
||||
*/
|
||||
private URL createStatisticsUrl(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
|
||||
// ---- service statistics ----
|
||||
long elapsed = System.currentTimeMillis() - start; // invocation cost
|
||||
int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count
|
||||
String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
|
||||
String service = invoker.getInterface().getName(); // service name
|
||||
String method = RpcUtils.getMethodName(invocation); // method name
|
||||
String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
|
||||
String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
|
||||
|
||||
int localPort;
|
||||
String remoteKey, remoteValue;
|
||||
if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) {
|
||||
// ---- for service consumer ----
|
||||
localPort = 0;
|
||||
remoteKey = MonitorService.PROVIDER;
|
||||
remoteValue = invoker.getUrl().getAddress();
|
||||
} else {
|
||||
// ---- for service provider ----
|
||||
localPort = invoker.getUrl().getPort();
|
||||
remoteKey = MonitorService.CONSUMER;
|
||||
remoteValue = remoteHost;
|
||||
}
|
||||
String input = "", output = "";
|
||||
if (invocation.getAttachment(Constants.INPUT_KEY) != null) {
|
||||
input = invocation.getAttachment(Constants.INPUT_KEY);
|
||||
}
|
||||
if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) {
|
||||
output = result.getAttachment(Constants.OUTPUT_KEY);
|
||||
}
|
||||
|
||||
return new URL(Constants.COUNT_PROTOCOL,
|
||||
NetUtils.getLocalHost(), localPort,
|
||||
service + Constants.PATH_SEPARATOR + method,
|
||||
MonitorService.APPLICATION, application,
|
||||
MonitorService.INTERFACE, service,
|
||||
MonitorService.METHOD, method,
|
||||
remoteKey, remoteValue,
|
||||
error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1",
|
||||
MonitorService.ELAPSED, String.valueOf(elapsed),
|
||||
MonitorService.CONCURRENT, String.valueOf(concurrent),
|
||||
Constants.INPUT_KEY, input,
|
||||
Constants.OUTPUT_KEY, output,
|
||||
Constants.GROUP_KEY, group,
|
||||
Constants.VERSION_KEY, version);
|
||||
return invoker.invoke(invocation); // proceed invocation chain
|
||||
}
|
||||
|
||||
// concurrent counter
|
||||
|
|
@ -182,4 +92,93 @@ public class MonitorFilter implements Filter {
|
|||
return concurrent;
|
||||
}
|
||||
|
||||
class MonitorListener implements Listener {
|
||||
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
|
||||
collect(invoker, invocation, result, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), false);
|
||||
getConcurrent(invoker, invocation).decrementAndGet(); // count down
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
|
||||
collect(invoker, invocation, null, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), true);
|
||||
getConcurrent(invoker, invocation).decrementAndGet(); // count down
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The collector logic, it will be handled by the default monitor
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @param result the invoke result
|
||||
* @param remoteHost the remote host address
|
||||
* @param start the timestamp the invoke begin
|
||||
* @param error if there is an error on the invoke
|
||||
*/
|
||||
private void collect(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
|
||||
try {
|
||||
URL monitorUrl = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY);
|
||||
Monitor monitor = monitorFactory.getMonitor(monitorUrl);
|
||||
if (monitor == null) {
|
||||
return;
|
||||
}
|
||||
URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error);
|
||||
monitor.collect(statisticsURL);
|
||||
} catch (Throwable t) {
|
||||
logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create statistics url
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @param result
|
||||
* @param remoteHost
|
||||
* @param start
|
||||
* @param error
|
||||
* @return
|
||||
*/
|
||||
private URL createStatisticsUrl(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
|
||||
// ---- service statistics ----
|
||||
long elapsed = System.currentTimeMillis() - start; // invocation cost
|
||||
int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count
|
||||
String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
|
||||
String service = invoker.getInterface().getName(); // service name
|
||||
String method = RpcUtils.getMethodName(invocation); // method name
|
||||
String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
|
||||
String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
|
||||
|
||||
int localPort;
|
||||
String remoteKey, remoteValue;
|
||||
if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) {
|
||||
// ---- for service consumer ----
|
||||
localPort = 0;
|
||||
remoteKey = MonitorService.PROVIDER;
|
||||
remoteValue = invoker.getUrl().getAddress();
|
||||
} else {
|
||||
// ---- for service provider ----
|
||||
localPort = invoker.getUrl().getPort();
|
||||
remoteKey = MonitorService.CONSUMER;
|
||||
remoteValue = remoteHost;
|
||||
}
|
||||
String input = "", output = "";
|
||||
if (invocation.getAttachment(Constants.INPUT_KEY) != null) {
|
||||
input = invocation.getAttachment(Constants.INPUT_KEY);
|
||||
}
|
||||
if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) {
|
||||
output = result.getAttachment(Constants.OUTPUT_KEY);
|
||||
}
|
||||
|
||||
return new URL(Constants.COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + Constants.PATH_SEPARATOR + method, MonitorService.APPLICATION, application, MonitorService.INTERFACE, service, MonitorService.METHOD, method, remoteKey, remoteValue, error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1", MonitorService.ELAPSED, String.valueOf(elapsed), MonitorService.CONCURRENT, String.valueOf(concurrent), Constants.INPUT_KEY, input, Constants.OUTPUT_KEY, output, Constants.GROUP_KEY, group, Constants.VERSION_KEY, version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,14 @@ import org.apache.dubbo.common.utils.NetUtils;
|
|||
import org.apache.dubbo.monitor.Monitor;
|
||||
import org.apache.dubbo.monitor.MonitorFactory;
|
||||
import org.apache.dubbo.monitor.MonitorService;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
|
@ -73,7 +75,7 @@ public class MonitorFilterTest {
|
|||
|
||||
public Result invoke(Invocation invocation) throws RpcException {
|
||||
lastInvocation = invocation;
|
||||
return null;
|
||||
return AsyncRpcResult.newDefaultAsyncResult(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -115,7 +117,11 @@ public class MonitorFilterTest {
|
|||
monitorFilter.setMonitorFactory(monitorFactory);
|
||||
Invocation invocation = new RpcInvocation("aaa", new Class<?>[0], new Object[0]);
|
||||
RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
|
||||
monitorFilter.invoke(serviceInvoker, invocation);
|
||||
Result result = monitorFilter.invoke(serviceInvoker, invocation);
|
||||
result.thenApplyWithContext((r) -> {
|
||||
monitorFilter.listener().onResponse(r, serviceInvoker, invocation);
|
||||
return r;
|
||||
});
|
||||
while (lastStatistics == null) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
|
|
@ -151,7 +157,11 @@ public class MonitorFilterTest {
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import org.apache.dubbo.remoting.ChannelHandler;
|
|||
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 class MockChannel implements ExchangeChannel {
|
|||
return null;
|
||||
}
|
||||
|
||||
public ResponseFuture send(Object request, int timeout) throws RemotingException {
|
||||
public CompletableFuture<Object> send(Object request, int timeout) throws RemotingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -85,11 +85,11 @@ public class MockChannel implements ExchangeChannel {
|
|||
return null;
|
||||
}
|
||||
|
||||
public ResponseFuture request(Object request) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object request) throws RemotingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ResponseFuture request(Object request, int timeout) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ import org.apache.dubbo.remoting.Codec;
|
|||
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 class MockedClient implements ExchangeClient {
|
|||
this.sent = msg;
|
||||
}
|
||||
|
||||
public ResponseFuture request(Object msg) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object msg) throws RemotingException {
|
||||
return request(msg, 0);
|
||||
}
|
||||
|
||||
public ResponseFuture request(Object msg, int timeout) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object msg, int timeout) throws RemotingException {
|
||||
this.invoked = msg;
|
||||
return new ResponseFuture() {
|
||||
public Object get() throws RemotingException {
|
||||
return new CompletableFuture<Object>() {
|
||||
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) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import java.net.InetSocketAddress;
|
|||
* 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)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import java.net.InetSocketAddress;
|
|||
* 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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package org.apache.dubbo.remoting.exchange;
|
|||
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<Object> 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<Object> request(Object request, int timeout) throws RemotingException;
|
||||
|
||||
/**
|
||||
* get message handler.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@
|
|||
*/
|
||||
package org.apache.dubbo.remoting.exchange;
|
||||
|
||||
import org.apache.dubbo.remoting.Channel;
|
||||
import org.apache.dubbo.remoting.Codec;
|
||||
import org.apache.dubbo.remoting.Decodeable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*/
|
||||
|
|
@ -173,4 +183,55 @@ public class Response {
|
|||
return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent
|
||||
+ ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]";
|
||||
}
|
||||
|
||||
public static class AppResult {
|
||||
|
||||
protected Map<String, String> attachments = new HashMap<String, String>();
|
||||
|
||||
protected Object result;
|
||||
|
||||
protected Throwable exception;
|
||||
|
||||
public Map<String, String> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public void setAttachments(Map<String, String> attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DecodeableAppResult implements Codec, Decodeable {
|
||||
|
||||
@Override
|
||||
public void decode() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Channel channel, OutputStream output, Object message) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(Channel channel, InputStream input) throws IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -29,22 +29,18 @@ import org.apache.dubbo.remoting.RemotingException;
|
|||
import org.apache.dubbo.remoting.TimeoutException;
|
||||
import org.apache.dubbo.remoting.exchange.Request;
|
||||
import org.apache.dubbo.remoting.exchange.Response;
|
||||
import org.apache.dubbo.remoting.exchange.ResponseCallback;
|
||||
import org.apache.dubbo.remoting.exchange.ResponseFuture;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* DefaultFuture.
|
||||
*/
|
||||
public class DefaultFuture implements ResponseFuture {
|
||||
public class DefaultFuture extends CompletableFuture<Object> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
|
||||
|
||||
|
|
@ -62,12 +58,8 @@ public class DefaultFuture implements ResponseFuture {
|
|||
private final Channel channel;
|
||||
private final Request request;
|
||||
private final int timeout;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition done = lock.newCondition();
|
||||
private final long start = System.currentTimeMillis();
|
||||
private volatile long sent;
|
||||
private volatile Response response;
|
||||
private volatile ResponseCallback callback;
|
||||
|
||||
private DefaultFuture(Channel channel, Request request, int timeout) {
|
||||
this.channel = channel;
|
||||
|
|
@ -79,14 +71,6 @@ public class DefaultFuture implements ResponseFuture {
|
|||
CHANNELS.put(id, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* check time out of the future
|
||||
*/
|
||||
private static void timeoutCheck(DefaultFuture future) {
|
||||
TimeoutCheckTask task = new TimeoutCheckTask(future);
|
||||
TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* init a DefaultFuture
|
||||
* 1.init a DefaultFuture
|
||||
|
|
@ -160,70 +144,77 @@ public class DefaultFuture implements ResponseFuture {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object get() throws RemotingException {
|
||||
return get(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int timeout) throws RemotingException {
|
||||
if (timeout <= 0) {
|
||||
timeout = Constants.DEFAULT_TIMEOUT;
|
||||
}
|
||||
if (!isDone()) {
|
||||
long start = System.currentTimeMillis();
|
||||
lock.lock();
|
||||
try {
|
||||
while (!isDone()) {
|
||||
done.await(timeout, TimeUnit.MILLISECONDS);
|
||||
if (isDone() || System.currentTimeMillis() - start > timeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (!isDone()) {
|
||||
throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
|
||||
}
|
||||
}
|
||||
return returnFromResponse();
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
Response errorResult = new Response(id);
|
||||
errorResult.setStatus(Response.CLIENT_ERROR);
|
||||
errorResult.setErrorMessage("request future has been canceled.");
|
||||
this.doReceived(errorResult);
|
||||
FUTURES.remove(id);
|
||||
CHANNELS.remove(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
Response errorResult = new Response(id);
|
||||
errorResult.setErrorMessage("request future has been canceled.");
|
||||
response = errorResult;
|
||||
FUTURES.remove(id);
|
||||
CHANNELS.remove(id);
|
||||
this.cancel(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);
|
||||
}
|
||||
private void doReceived(Response res) {
|
||||
if (res == null) {
|
||||
throw new IllegalStateException("response cannot be null");
|
||||
}
|
||||
if (res.getStatus() == Response.OK) {
|
||||
this.complete(res.getResult());
|
||||
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
|
||||
this.completeExceptionally(new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()));
|
||||
} else {
|
||||
this.completeExceptionally(new RemotingException(channel, res.getErrorMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
private boolean isSent() {
|
||||
return sent > 0;
|
||||
}
|
||||
|
||||
public Request getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
private int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
private void doSent() {
|
||||
sent = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* check time out of the future
|
||||
*/
|
||||
private static void timeoutCheck(DefaultFuture future) {
|
||||
TimeoutCheckTask task = new TimeoutCheckTask(future);
|
||||
TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private String getTimeoutMessage(boolean scan) {
|
||||
long nowTimestamp = System.currentTimeMillis();
|
||||
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
|
||||
+ (scan ? " by scan timer" : "") + ". start time: "
|
||||
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
|
||||
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
|
||||
+ (sent > 0 ? " client elapsed: " + (sent - start)
|
||||
+ " ms, server elapsed: " + (nowTimestamp - sent)
|
||||
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
|
||||
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
|
||||
+ " -> " + channel.getRemoteAddress();
|
||||
}
|
||||
|
||||
private static class TimeoutCheckTask implements TimerTask {
|
||||
|
|
@ -249,105 +240,4 @@ public class DefaultFuture implements ResponseFuture {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeCallback(ResponseCallback c) {
|
||||
ResponseCallback callbackCopy = c;
|
||||
if (callbackCopy == null) {
|
||||
throw new NullPointerException("callback cannot be null.");
|
||||
}
|
||||
Response res = response;
|
||||
if (res == null) {
|
||||
throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
|
||||
}
|
||||
|
||||
if (res.getStatus() == Response.OK) {
|
||||
try {
|
||||
callbackCopy.done(res.getResult());
|
||||
} catch (Exception e) {
|
||||
logger.error("callback invoke error .result:" + res.getResult() + ",url:" + channel.getUrl(), e);
|
||||
}
|
||||
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
|
||||
try {
|
||||
TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
|
||||
callbackCopy.caught(te);
|
||||
} catch (Exception e) {
|
||||
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
RuntimeException re = new RuntimeException(res.getErrorMessage());
|
||||
callbackCopy.caught(re);
|
||||
} catch (Exception e) {
|
||||
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object returnFromResponse() throws RemotingException {
|
||||
Response res = response;
|
||||
if (res == null) {
|
||||
throw new IllegalStateException("response cannot be null");
|
||||
}
|
||||
if (res.getStatus() == Response.OK) {
|
||||
return res.getResult();
|
||||
}
|
||||
if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
|
||||
throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
|
||||
}
|
||||
throw new RemotingException(channel, res.getErrorMessage());
|
||||
}
|
||||
|
||||
private long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
private boolean isSent() {
|
||||
return sent > 0;
|
||||
}
|
||||
|
||||
public Request getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
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")
|
||||
+ (scan ? " by scan timer" : "") + ". start time: "
|
||||
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
|
||||
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
|
||||
+ (sent > 0 ? " client elapsed: " + (sent - start)
|
||||
+ " ms, server elapsed: " + (nowTimestamp - sent)
|
||||
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
|
||||
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
|
||||
+ " -> " + channel.getRemoteAddress();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,10 +28,10 @@ import org.apache.dubbo.remoting.exchange.ExchangeChannel;
|
|||
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
|
||||
import org.apache.dubbo.remoting.exchange.Request;
|
||||
import org.apache.dubbo.remoting.exchange.Response;
|
||||
import org.apache.dubbo.remoting.exchange.ResponseFuture;
|
||||
import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* ExchangeReceiver
|
||||
|
|
@ -97,12 +97,12 @@ final class HeaderExchangeChannel implements ExchangeChannel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResponseFuture request(Object request) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object request) throws RemotingException {
|
||||
return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseFuture request(Object request, int timeout) throws RemotingException {
|
||||
public CompletableFuture<Object> 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!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ import org.apache.dubbo.remoting.RemotingException;
|
|||
import org.apache.dubbo.remoting.exchange.ExchangeChannel;
|
||||
import org.apache.dubbo.remoting.exchange.ExchangeClient;
|
||||
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
|
||||
import org.apache.dubbo.remoting.exchange.ResponseFuture;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.utils.UrlUtils.getHeartbeat;
|
||||
|
|
@ -62,7 +62,7 @@ public class HeaderExchangeClient implements ExchangeClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResponseFuture request(Object request) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object request) throws RemotingException {
|
||||
return channel.request(request);
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ public class HeaderExchangeClient implements ExchangeClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResponseFuture request(Object request, int timeout) throws RemotingException {
|
||||
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
|
||||
return channel.request(request, timeout);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
|
|||
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 @@ public class HeaderExchangeHandler implements ChannelHandlerDelegate {
|
|||
// find handler by message class.
|
||||
Object msg = req.getData();
|
||||
try {
|
||||
// handle data.
|
||||
CompletableFuture<Object> 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<Object> 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));
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ package org.apache.dubbo.remoting;
|
|||
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 @@ public class Main {
|
|||
|
||||
System.out.println("=====test invoke=====");
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ResponseFuture future = client.request(new Main.Data());
|
||||
CompletableFuture<Object> future = client.request(new Main.Data());
|
||||
System.out.println("invoke and get");
|
||||
System.out.println("invoke result:" + future.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import org.apache.dubbo.remoting.exchange.Exchangers;
|
|||
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
|
||||
import org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class DefaultFutureTest {
|
|||
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 class DefaultFutureTest {
|
|||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ public class HeaderExchangeHandlerTest {
|
|||
HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler() {
|
||||
|
||||
@Override
|
||||
public CompletableFuture reply(ExchangeChannel channel, Object request) throws RemotingException {
|
||||
public CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException {
|
||||
Assertions.fail();
|
||||
throw new RemotingException(channel, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package org.apache.remoting.transport.mina;
|
|||
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.Assertions;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* ClientToServer
|
||||
*/
|
||||
|
|
@ -64,7 +65,7 @@ public abstract class ClientToServerTest {
|
|||
|
||||
@Test
|
||||
public void testFuture() throws Exception {
|
||||
ResponseFuture future = client.request(new World("world"));
|
||||
CompletableFuture<Object> future = client.request(new World("world"));
|
||||
Hello result = (Hello) future.get();
|
||||
Assertions.assertEquals("hello,world", result.getName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler;
|
|||
import org.apache.dubbo.remoting.exchange.ExchangeServer;
|
||||
import org.apache.dubbo.remoting.exchange.Exchangers;
|
||||
import org.apache.dubbo.remoting.transport.dispatcher.FakeChannelHandlers;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ package org.apache.dubbo.remoting.transport.netty;
|
|||
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 @@ public abstract class ClientToServerTest {
|
|||
|
||||
@Test
|
||||
public void testFuture() throws Exception {
|
||||
ResponseFuture future = client.request(new World("world"));
|
||||
CompletableFuture<Object> future = client.request(new World("world"));
|
||||
Hello result = (Hello) future.get();
|
||||
Assertions.assertEquals("hello,world", result.getName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package org.apache.dubbo.remoting.transport.netty4;
|
|||
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.Assertions;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* ClientToServer
|
||||
*/
|
||||
|
|
@ -64,7 +65,7 @@ public abstract class ClientToServerTest {
|
|||
|
||||
@Test
|
||||
public void testFuture() throws Exception {
|
||||
ResponseFuture future = client.request(new World("world"));
|
||||
CompletableFuture<Object> future = client.request(new World("world"));
|
||||
Hello result = (Hello) future.get();
|
||||
Assertions.assertEquals("hello,world", result.getName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc;
|
||||
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractResult implements Result {
|
||||
protected Map<String, String> attachments = new HashMap<String, String>();
|
||||
|
||||
protected Object result;
|
||||
|
||||
protected Throwable exception;
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttachments(Map<String, String> map) {
|
||||
this.attachments = map == null ? new HashMap<String, String>() : map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttachments(Map<String, String> map) {
|
||||
if (map == null) {
|
||||
return;
|
||||
}
|
||||
if (this.attachments == null) {
|
||||
this.attachments = new HashMap<String, String>();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}:
|
||||
* <ul>
|
||||
* <li>AsyncRpcResult is the object that is actually passed in the call chain</li>
|
||||
* <li>AppResponse only simply represents the business result</li>
|
||||
* </ul>
|
||||
*
|
||||
* The relationship between them can be reflected in the definition of AsyncRpcResult:
|
||||
* <pre>
|
||||
* {@code
|
||||
* Public class AsyncRpcResult implements Result {
|
||||
* private CompletableFuture <AppResponse> resultFuture;
|
||||
* ......
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* In theory, AppResponse does not need to implement the {@link Result} interface, this is done mainly for compatibility purpose.
|
||||
*
|
||||
* @serial Do not change the class name and properties.
|
||||
*/
|
||||
public class AppResponse implements Result, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6925924956850004727L;
|
||||
|
||||
private Object result;
|
||||
|
||||
private Throwable exception;
|
||||
|
||||
private Map<String, String> attachments = new HashMap<String, String>();
|
||||
|
||||
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<String, String> 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<String, String> map) {
|
||||
this.attachments = map == null ? new HashMap<String, String>() : map;
|
||||
}
|
||||
|
||||
public void addAttachments(Map<String, String> map) {
|
||||
if (map == null) {
|
||||
return;
|
||||
}
|
||||
if (this.attachments == null) {
|
||||
this.attachments = new HashMap<String, String>();
|
||||
}
|
||||
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<AppResponse, AppResponse> fn) {
|
||||
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> thenApply(Function<Result, ? extends U> fn) {
|
||||
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result get() throws InterruptedException, ExecutionException {
|
||||
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AppResponse [value=" + result + ", exception=" + exception + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -22,96 +22,27 @@ import org.apache.dubbo.common.logger.LoggerFactory;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* <b>NOTICE!!</b>
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
* <pre>
|
||||
* {@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);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* </p>
|
||||
* TODO RpcResult can be an instance of {@link java.util.concurrent.CompletionStage} instead of composing CompletionStage inside.
|
||||
*/
|
||||
public class AsyncRpcResult extends AbstractResult {
|
||||
public class AsyncRpcResult implements Result {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AsyncRpcResult.class);
|
||||
|
||||
/**
|
||||
* RpcContext can be changed, because thread may have been used by other thread. It should be cloned before store.
|
||||
* So we use Invocation instead, Invocation will create for every invoke, but invocation only support attachments of string type.
|
||||
* RpcContext may already have been changed when callback happens, it happens when the same thread is used to execute another RPC call.
|
||||
* So we should keep the reference of current RpcContext instance and restore it before callback being executed.
|
||||
*/
|
||||
private RpcContext storedContext;
|
||||
private RpcContext storedServerContext;
|
||||
|
||||
protected CompletableFuture<Object> valueFuture;
|
||||
private CompletableFuture<AppResponse> responseFuture;
|
||||
private Invocation invocation;
|
||||
|
||||
protected CompletableFuture<Result> resultFuture;
|
||||
|
||||
public AsyncRpcResult(CompletableFuture<Object> future) {
|
||||
this(future, true);
|
||||
}
|
||||
|
||||
public AsyncRpcResult(CompletableFuture<Object> future, boolean registerCallback) {
|
||||
this(future, new CompletableFuture<>(), registerCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param future
|
||||
* @param rFuture
|
||||
* @param registerCallback
|
||||
*/
|
||||
public AsyncRpcResult(CompletableFuture<Object> future, final CompletableFuture<Result> rFuture, boolean registerCallback) {
|
||||
if (rFuture == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
resultFuture = rFuture;
|
||||
if (registerCallback) {
|
||||
/**
|
||||
* We do not know whether future already completed or not, it's a future exposed or even created by end user.
|
||||
* 1. future complete before whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread subscribing, in our case, it's Dubbo thread.
|
||||
* 2. future complete after whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread calling complete, normally its User thread.
|
||||
*/
|
||||
future.whenComplete((v, t) -> {
|
||||
RpcResult rpcResult;
|
||||
if (t != null) {
|
||||
if (t instanceof CompletionException) {
|
||||
rpcResult = new RpcResult(t.getCause());
|
||||
} else {
|
||||
rpcResult = new RpcResult(t);
|
||||
}
|
||||
} else {
|
||||
rpcResult = new RpcResult(v);
|
||||
}
|
||||
// instead of resultFuture we must use rFuture here, resultFuture may being changed before complete when building filter chain, but rFuture was guaranteed never changed by closure.
|
||||
rFuture.complete(rpcResult);
|
||||
});
|
||||
}
|
||||
this.valueFuture = future;
|
||||
// employ copy of context avoid the other call may modify the context content
|
||||
this.storedContext = RpcContext.getContext().copyOf();
|
||||
this.storedServerContext = RpcContext.getServerContext().copyOf();
|
||||
public AsyncRpcResult(CompletableFuture<AppResponse> future, Invocation invocation) {
|
||||
this.responseFuture = future;
|
||||
this.invocation = invocation;
|
||||
this.storedContext = RpcContext.getContext();
|
||||
this.storedServerContext = RpcContext.getServerContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -119,52 +50,87 @@ public class AsyncRpcResult extends AbstractResult {
|
|||
return getRpcResult().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable getException() {
|
||||
return getRpcResult().getException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setException(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasException() {
|
||||
return getRpcResult().hasException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult() {
|
||||
return getRpcResult().getResult();
|
||||
public CompletableFuture<AppResponse> getResponseFuture() {
|
||||
return responseFuture;
|
||||
}
|
||||
|
||||
public CompletableFuture getValueFuture() {
|
||||
return valueFuture;
|
||||
}
|
||||
|
||||
public CompletableFuture<Result> getResultFuture() {
|
||||
return resultFuture;
|
||||
}
|
||||
|
||||
public void setResultFuture(CompletableFuture<Result> resultFuture) {
|
||||
this.resultFuture = resultFuture;
|
||||
public void setResponseFuture(CompletableFuture<AppResponse> responseFuture) {
|
||||
this.responseFuture = responseFuture;
|
||||
}
|
||||
|
||||
public Result getRpcResult() {
|
||||
try {
|
||||
if (resultFuture.isDone()) {
|
||||
return resultFuture.get();
|
||||
if (responseFuture.isDone()) {
|
||||
return responseFuture.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// This should never happen;
|
||||
logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e);
|
||||
}
|
||||
return new RpcResult();
|
||||
return new AppResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object recreate() throws Throwable {
|
||||
return valueFuture;
|
||||
RpcInvocation rpcInvocation = (RpcInvocation) invocation;
|
||||
if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) {
|
||||
AppResponse rpcResult = new AppResponse();
|
||||
CompletableFuture<Object> future = new CompletableFuture<>();
|
||||
rpcResult.setValue(future);
|
||||
responseFuture.whenComplete((result, t) -> {
|
||||
if (t != null) {
|
||||
if (t instanceof CompletionException) {
|
||||
t = t.getCause();
|
||||
}
|
||||
future.completeExceptionally(t);
|
||||
} else {
|
||||
if (result.hasException()) {
|
||||
future.completeExceptionally(result.getException());
|
||||
} else {
|
||||
future.complete(result.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
return rpcResult.recreate();
|
||||
} else if (responseFuture.isDone()) {
|
||||
return responseFuture.get().recreate();
|
||||
}
|
||||
return (new AppResponse()).recreate();
|
||||
}
|
||||
|
||||
public void thenApplyWithContext(Function<Result, Result> fn) {
|
||||
this.resultFuture = resultFuture.thenApply(fn.compose(beforeContext).andThen(afterContext));
|
||||
public Result get() throws InterruptedException, ExecutionException {
|
||||
return responseFuture.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result thenApplyWithContext(Function<AppResponse, AppResponse> fn) {
|
||||
this.responseFuture = responseFuture.thenApply(fn.compose(beforeContext).andThen(afterContext));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> thenApply(Function<Result,? extends U> fn) {
|
||||
return this.responseFuture.thenApply(fn);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -203,18 +169,49 @@ public class AsyncRpcResult extends AbstractResult {
|
|||
private RpcContext tmpContext;
|
||||
private RpcContext tmpServerContext;
|
||||
|
||||
private Function<Result, Result> beforeContext = (result) -> {
|
||||
private Function<AppResponse, AppResponse> beforeContext = (appResponse) -> {
|
||||
tmpContext = RpcContext.getContext();
|
||||
tmpServerContext = RpcContext.getServerContext();
|
||||
RpcContext.restoreContext(storedContext);
|
||||
RpcContext.restoreServerContext(storedServerContext);
|
||||
return result;
|
||||
return appResponse;
|
||||
};
|
||||
|
||||
private Function<Result, Result> afterContext = (result) -> {
|
||||
private Function<AppResponse, AppResponse> afterContext = (appResponse) -> {
|
||||
RpcContext.restoreContext(tmpContext);
|
||||
RpcContext.restoreServerContext(tmpServerContext);
|
||||
return result;
|
||||
return appResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Some utility methods used to quickly generate default AsyncRpcResult instance.
|
||||
*/
|
||||
public static AsyncRpcResult newDefaultAsyncResult(AppResponse result, Invocation invocation) {
|
||||
return new AsyncRpcResult(CompletableFuture.completedFuture(result), invocation);
|
||||
}
|
||||
|
||||
public static AsyncRpcResult newDefaultAsyncResult(Invocation invocation) {
|
||||
return newDefaultAsyncResult(null, null, invocation);
|
||||
}
|
||||
|
||||
public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) {
|
||||
return newDefaultAsyncResult(value, null, invocation);
|
||||
}
|
||||
|
||||
public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invocation) {
|
||||
return newDefaultAsyncResult(null, t, invocation);
|
||||
}
|
||||
|
||||
public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
|
||||
CompletableFuture<AppResponse> future = new CompletableFuture<>();
|
||||
AppResponse result = new AppResponse();
|
||||
if (t != null) {
|
||||
result.setException(t);
|
||||
} else {
|
||||
result.setValue(value);
|
||||
}
|
||||
future.complete(result);
|
||||
return new AsyncRpcResult(future, invocation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,27 +42,16 @@ import org.apache.dubbo.common.extension.SPI;
|
|||
*/
|
||||
@SPI
|
||||
public interface Filter {
|
||||
|
||||
/**
|
||||
* do invoke filter.
|
||||
* <p>
|
||||
* <code>
|
||||
* // before filter
|
||||
* Result result = invoker.invoke(invocation);
|
||||
* // after filter
|
||||
* return result;
|
||||
* </code>
|
||||
*
|
||||
* @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;
|
||||
|
||||
default Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
return result;
|
||||
interface Listener {
|
||||
|
||||
void onResponse(Result result, Invoker<?> invoker, Invocation invocation);
|
||||
|
||||
void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Used for async call scenario. But if the method you are calling has a {@link CompletableFuture<?>} signature
|
||||
* you do not need to use this class since you will get a Future response directly.
|
||||
* <p>
|
||||
* Remember to save the Future reference before making another call using the same thread, otherwise,
|
||||
* the current Future will be override by the new one, which means you will lose the chance get the return value.
|
||||
*/
|
||||
public class FutureContext {
|
||||
|
||||
public static ThreadLocal<CompletableFuture<?>> futureTL = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* get future.
|
||||
*
|
||||
* @param <T>
|
||||
* @return future
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> CompletableFuture<T> getCompletableFuture() {
|
||||
return (CompletableFuture<T>) futureTL.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* set future.
|
||||
*
|
||||
* @param future
|
||||
*/
|
||||
public static void setFuture(CompletableFuture<?> future) {
|
||||
futureTL.set(future);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -59,6 +59,10 @@ public interface Invocation {
|
|||
*/
|
||||
Map<String, String> getAttachments();
|
||||
|
||||
void setAttachment(String key, String value);
|
||||
|
||||
void setAttachmentIfAbsent(String key, String value);
|
||||
|
||||
/**
|
||||
* get attachment by key.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc;
|
||||
|
||||
public enum InvokeMode {
|
||||
|
||||
SYNC, ASYNC, FUTURE;
|
||||
|
||||
}
|
||||
|
|
@ -1,38 +1,29 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.remoting.exchange;
|
||||
|
||||
/**
|
||||
* Callback
|
||||
*/
|
||||
public interface ResponseCallback {
|
||||
|
||||
/**
|
||||
* done.
|
||||
*
|
||||
* @param response
|
||||
*/
|
||||
void done(Object response);
|
||||
|
||||
/**
|
||||
* caught exception.
|
||||
*
|
||||
* @param exception
|
||||
*/
|
||||
void caught(Throwable exception);
|
||||
|
||||
}
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract class ListenableFilter implements Filter {
|
||||
|
||||
protected Listener listener = null;
|
||||
|
||||
public Listener listener() {
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@ package org.apache.dubbo.rpc;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -25,7 +28,7 @@ import java.util.Map;
|
|||
*
|
||||
* @serial Don't change the class name and package name.
|
||||
* @see org.apache.dubbo.rpc.Invoker#invoke(Invocation)
|
||||
* @see org.apache.dubbo.rpc.RpcResult
|
||||
* @see AppResponse
|
||||
*/
|
||||
public interface Result extends Serializable {
|
||||
|
||||
|
|
@ -36,6 +39,8 @@ public interface Result extends Serializable {
|
|||
*/
|
||||
Object getValue();
|
||||
|
||||
void setValue(Object value);
|
||||
|
||||
/**
|
||||
* Get exception.
|
||||
*
|
||||
|
|
@ -43,6 +48,8 @@ public interface Result extends Serializable {
|
|||
*/
|
||||
Throwable getException();
|
||||
|
||||
void setException(Throwable t);
|
||||
|
||||
/**
|
||||
* Has exception.
|
||||
*
|
||||
|
|
@ -66,14 +73,6 @@ public interface Result extends Serializable {
|
|||
*/
|
||||
Object recreate() throws Throwable;
|
||||
|
||||
/**
|
||||
* @see org.apache.dubbo.rpc.Result#getValue()
|
||||
* @deprecated Replace to getValue()
|
||||
*/
|
||||
@Deprecated
|
||||
Object getResult();
|
||||
|
||||
|
||||
/**
|
||||
* get attachments.
|
||||
*
|
||||
|
|
@ -111,4 +110,10 @@ public interface Result extends Serializable {
|
|||
|
||||
void setAttachment(String key, String value);
|
||||
|
||||
Result thenApplyWithContext(Function<AppResponse, AppResponse> fn);
|
||||
|
||||
<U> CompletableFuture<U> thenApply(Function<Result, ? extends U> fn);
|
||||
|
||||
Result get() throws InterruptedException, ExecutionException;
|
||||
|
||||
}
|
||||
|
|
@ -70,7 +70,6 @@ public class RpcContext {
|
|||
|
||||
private final Map<String, String> attachments = new HashMap<String, String>();
|
||||
private final Map<String, Object> values = new HashMap<String, Object>();
|
||||
private Future<?> future;
|
||||
|
||||
private List<URL> urls;
|
||||
|
||||
|
|
@ -136,31 +135,6 @@ public class RpcContext {
|
|||
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.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.
|
||||
*
|
||||
|
|
@ -242,7 +216,7 @@ public class RpcContext {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> CompletableFuture<T> getCompletableFuture() {
|
||||
return (CompletableFuture<T>) future;
|
||||
return FutureContext.getCompletableFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -253,7 +227,7 @@ public class RpcContext {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Future<T> getFuture() {
|
||||
return (Future<T>) future;
|
||||
return FutureContext.getCompletableFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -261,8 +235,8 @@ public class RpcContext {
|
|||
*
|
||||
* @param future
|
||||
*/
|
||||
public void setFuture(Future<?> future) {
|
||||
this.future = future;
|
||||
public void setFuture(CompletableFuture<?> future) {
|
||||
FutureContext.setFuture(future);
|
||||
}
|
||||
|
||||
public List<URL> getUrls() {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ public class RpcInvocation implements Invocation, Serializable {
|
|||
|
||||
private transient Invoker<?> invoker;
|
||||
|
||||
private transient Class<?> returnType;
|
||||
|
||||
private transient InvokeMode invokeMode;
|
||||
|
||||
public RpcInvocation() {
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +93,7 @@ public class RpcInvocation implements Invocation, Serializable {
|
|||
|
||||
public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
|
||||
this(method.getName(), method.getParameterTypes(), arguments, attachment, null);
|
||||
this.returnType = method.getReturnType();
|
||||
}
|
||||
|
||||
public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
|
||||
|
|
@ -220,6 +225,22 @@ public class RpcInvocation implements Invocation, Serializable {
|
|||
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="
|
||||
|
|
|
|||
|
|
@ -1,112 +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 = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object recreate() throws Throwable {
|
||||
if (exception != null) {
|
||||
// fix issue#619
|
||||
try {
|
||||
// get Throwable class
|
||||
Class clazz = exception.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(exception);
|
||||
if (stackTrace == null) {
|
||||
exception.setStackTrace(new StackTraceElement[0]);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
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 = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasException() {
|
||||
return exception != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RpcResult [result=" + result + ", exception=" + exception + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
* <b>NOTICE!!</b>
|
||||
*
|
||||
* <p>
|
||||
* 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}.
|
||||
* </p>
|
||||
*
|
||||
* Check {@link AsyncRpcResult} for more details.
|
||||
*
|
||||
* TODO AsyncRpcResult, AsyncNormalRpcResult should not be a parent-child hierarchy.
|
||||
*/
|
||||
public class SimpleAsyncRpcResult extends AsyncRpcResult {
|
||||
public SimpleAsyncRpcResult(CompletableFuture<Object> future, boolean registerCallback) {
|
||||
super(future, registerCallback);
|
||||
}
|
||||
|
||||
public SimpleAsyncRpcResult(CompletableFuture<Object> future, CompletableFuture<Result> rFuture, boolean registerCallback) {
|
||||
super(future, rFuture, registerCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object recreate() throws Throwable {
|
||||
// TODO should we check the status of valueFuture here?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,14 +19,17 @@ package org.apache.dubbo.rpc.filter;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* ActiveLimitFilter restrict the concurrent client invocation for a service or service's method from client side.
|
||||
* To use active limit filter, configured url with <b>actives</b> and provide valid >0 integer value.
|
||||
* <pre>
|
||||
|
|
@ -39,50 +42,75 @@ import org.apache.dubbo.rpc.RpcStatus;
|
|||
* @see Filter
|
||||
*/
|
||||
@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)
|
||||
public class ActiveLimitFilter implements Filter {
|
||||
public class ActiveLimitFilter extends ListenableFilter {
|
||||
|
||||
private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
|
||||
|
||||
public ActiveLimitFilter() {
|
||||
super.listener = new ActiveLimitListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
URL url = invoker.getUrl();
|
||||
String methodName = invocation.getMethodName();
|
||||
int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
|
||||
RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
|
||||
if (!count.beginCount(url, methodName, max)) {
|
||||
RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
|
||||
if (!RpcStatus.beginCount(url, methodName, max)) {
|
||||
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
|
||||
long start = System.currentTimeMillis();
|
||||
long remain = timeout;
|
||||
synchronized (count) {
|
||||
while (!count.beginCount(url, methodName, max)) {
|
||||
synchronized (rpcStatus) {
|
||||
while (!RpcStatus.beginCount(url, methodName, max)) {
|
||||
try {
|
||||
count.wait(remain);
|
||||
rpcStatus.wait(remain);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
remain = timeout - elapsed;
|
||||
if (remain <= 0) {
|
||||
throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
|
||||
+ invoker.getInterface().getName() + ", method: "
|
||||
+ invocation.getMethodName() + ", elapsed: " + elapsed
|
||||
+ ", timeout: " + timeout + ". concurrent invokes: " + count.getActive()
|
||||
+ ". max concurrent invoke limit: " + max);
|
||||
throw new RpcException("Waiting concurrent invoke timeout in client-side for service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", elapsed: " + elapsed + ", timeout: " + timeout + ". concurrent invokes: " + rpcStatus.getActive() + ". max concurrent invoke limit: " + max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSuccess = true;
|
||||
long begin = System.currentTimeMillis();
|
||||
try {
|
||||
return invoker.invoke(invocation);
|
||||
} catch (RuntimeException t) {
|
||||
isSuccess = false;
|
||||
throw t;
|
||||
} finally {
|
||||
count.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
|
||||
invocation.setAttachment(ACTIVELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
|
||||
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
|
||||
static class ActiveLimitListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
String methodName = invocation.getMethodName();
|
||||
URL url = invoker.getUrl();
|
||||
int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
|
||||
|
||||
RpcStatus.endCount(url, methodName, getElapsed(invocation), true);
|
||||
notifyFinish(RpcStatus.getStatus(url, methodName), max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
String methodName = invocation.getMethodName();
|
||||
URL url = invoker.getUrl();
|
||||
int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
|
||||
|
||||
RpcStatus.endCount(url, methodName, getElapsed(invocation), false);
|
||||
notifyFinish(RpcStatus.getStatus(url, methodName), max);
|
||||
}
|
||||
|
||||
private long getElapsed(Invocation invocation) {
|
||||
String beginTime = invocation.getAttachment(ACTIVELIMIT_FILTER_START_TIME);
|
||||
return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
|
||||
}
|
||||
|
||||
private void notifyFinish(RpcStatus rpcStatus, int max) {
|
||||
if (max > 0) {
|
||||
synchronized (count) {
|
||||
count.notifyAll();
|
||||
synchronized (rpcStatus) {
|
||||
rpcStatus.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ import org.apache.dubbo.common.utils.PojoUtils;
|
|||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
|
@ -45,44 +45,54 @@ import java.lang.reflect.Type;
|
|||
* @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;
|
||||
static class CompatibleListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
if (!invocation.getMethodName().startsWith("$") && !result.hasException()) {
|
||||
Object value = result.getValue();
|
||||
if (value != null) {
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
|
||||
Class<?> type = method.getReturnType();
|
||||
Object newValue;
|
||||
String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
|
||||
if ("json".equals(serialization) || "fastjson".equals(serialization)) {
|
||||
// If the serialization key is json or fastjson
|
||||
Type gtype = method.getGenericReturnType();
|
||||
newValue = PojoUtils.realize(value, type, gtype);
|
||||
} else if (!type.isInstance(value)) {
|
||||
//if local service interface's method's return type is not instance of return value
|
||||
newValue = PojoUtils.isPojo(type) ? PojoUtils.realize(value, type) : CompatibleTypeUtils.compatibleTypeConvert(value, type);
|
||||
|
||||
} else {
|
||||
newValue = value;
|
||||
}
|
||||
if (newValue != value) {
|
||||
result.setValue(newValue);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.warn(t.getMessage(), t);
|
||||
}
|
||||
if (newValue != value) {
|
||||
result = new RpcResult(newValue);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.warn(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package org.apache.dubbo.rpc.filter;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
|
@ -35,7 +35,11 @@ import org.apache.dubbo.rpc.RpcInvocation;
|
|||
* @see RpcContext
|
||||
*/
|
||||
@Activate(group = Constants.CONSUMER, order = -10000)
|
||||
public class ConsumerContextFilter implements Filter {
|
||||
public class ConsumerContextFilter extends ListenableFilter {
|
||||
|
||||
public ConsumerContextFilter() {
|
||||
super.listener = new ConsumerContextListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
|
|
@ -49,18 +53,22 @@ public class ConsumerContextFilter implements Filter {
|
|||
((RpcInvocation) invocation).setInvoker(invoker);
|
||||
}
|
||||
try {
|
||||
// TODO should we clear server context?
|
||||
RpcContext.removeServerContext();
|
||||
return invoker.invoke(invocation);
|
||||
} finally {
|
||||
// TODO removeContext? but we need to save future for RpcContext.getFuture() API. If clear attachments here, attachments will not available when postProcessResult is invoked.
|
||||
RpcContext.getContext().clearAttachments();
|
||||
RpcContext.removeContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
RpcContext.getServerContext().setAttachments(result.getAttachments());
|
||||
return result;
|
||||
static class ConsumerContextListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
RpcContext.getServerContext().setAttachments(result.getAttachments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ package org.apache.dubbo.rpc.filter;
|
|||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
|
@ -36,7 +36,11 @@ import java.util.Map;
|
|||
* @see RpcContext
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER, order = -10000)
|
||||
public class ContextFilter implements Filter {
|
||||
public class ContextFilter extends ListenableFilter {
|
||||
|
||||
public ContextFilter() {
|
||||
super.listener = new ContextListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
|
|
@ -84,10 +88,16 @@ public class ContextFilter implements Filter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
// pass attachments to result
|
||||
result.addAttachments(RpcContext.getServerContext().getAttachments());
|
||||
return result;
|
||||
static class ContextListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
// pass attachments to result
|
||||
result.addAttachments(RpcContext.getServerContext().getAttachments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ package org.apache.dubbo.rpc.filter;
|
|||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
|
||||
/**
|
||||
* Dubbo provided default Echo echo service, which is available for all dubbo provider service interface.
|
||||
|
|
@ -34,7 +34,7 @@ public class EchoFilter implements Filter {
|
|||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
|
||||
if (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
|
||||
return new RpcResult(inv.getArguments()[0]);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
|
||||
}
|
||||
return invoker.invoke(inv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,12 @@ import org.apache.dubbo.common.logger.Logger;
|
|||
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 @@ import java.lang.reflect.Method;
|
|||
* </ol>
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER)
|
||||
public class ExceptionFilter implements Filter {
|
||||
|
||||
private final Logger logger;
|
||||
public class ExceptionFilter extends ListenableFilter {
|
||||
|
||||
public ExceptionFilter() {
|
||||
this(LoggerFactory.getLogger(ExceptionFilter.class));
|
||||
}
|
||||
|
||||
public ExceptionFilter(Logger logger) {
|
||||
this.logger = logger;
|
||||
super.listener = new ExceptionListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
try {
|
||||
return invoker.invoke(invocation);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
if (result.hasException() && GenericService.class != invoker.getInterface()) {
|
||||
try {
|
||||
Throwable exception = result.getException();
|
||||
static class ExceptionListener implements Listener {
|
||||
|
||||
// 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
|
||||
private Logger logger = LoggerFactory.getLogger(ExceptionListener.class);
|
||||
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
if (result.hasException() && GenericService.class != invoker.getInterface()) {
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
|
||||
Class<?>[] exceptionClassses = method.getExceptionTypes();
|
||||
for (Class<?> exceptionClass : exceptionClassses) {
|
||||
if (exception.getClass().equals(exceptionClass)) {
|
||||
return result;
|
||||
}
|
||||
Throwable exception = result.getException();
|
||||
|
||||
// directly throw if it's checked exception
|
||||
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
|
||||
return;
|
||||
}
|
||||
// directly throw if the exception appears in the signature
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
|
||||
Class<?>[] exceptionClassses = method.getExceptionTypes();
|
||||
for (Class<?> exceptionClass : exceptionClassses) {
|
||||
if (exception.getClass().equals(exceptionClass)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// for the exception not found in method's signature, print ERROR message in server's log.
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
|
||||
// for the exception not found in method's signature, print ERROR message in server's log.
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
|
||||
|
||||
// directly throw if exception class and interface class are in the same jar file.
|
||||
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
|
||||
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
|
||||
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
|
||||
return result;
|
||||
}
|
||||
// directly throw if it's JDK exception
|
||||
String className = exception.getClass().getName();
|
||||
if (className.startsWith("java.") || className.startsWith("javax.")) {
|
||||
return result;
|
||||
}
|
||||
// directly throw if it's dubbo exception
|
||||
if (exception instanceof RpcException) {
|
||||
return result;
|
||||
}
|
||||
// directly throw if exception class and interface class are in the same jar file.
|
||||
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
|
||||
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
|
||||
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
|
||||
return;
|
||||
}
|
||||
// directly throw if it's JDK exception
|
||||
String className = exception.getClass().getName();
|
||||
if (className.startsWith("java.") || className.startsWith("javax.")) {
|
||||
return;
|
||||
}
|
||||
// directly throw if it's dubbo exception
|
||||
if (exception instanceof RpcException) {
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, wrap with RuntimeException and throw back to the client
|
||||
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
|
||||
} catch (Throwable e) {
|
||||
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
|
||||
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
|
||||
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
return result;
|
||||
// otherwise, wrap with RuntimeException and throw back to the client
|
||||
result.setException(new RuntimeException(StringUtils.toString(exception)));
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
|
||||
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// For test purpose
|
||||
public void setLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,21 +19,29 @@ package org.apache.dubbo.rpc.filter;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* The maximum parallel execution request count per method per service for the provider.If the max configured
|
||||
* <b>executes</b> is set to 10 and if invoke request where it is already 10 then it will throws exception. It
|
||||
* continue the same behaviour un till it is <10.
|
||||
*
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
|
||||
public class ExecuteLimitFilter implements Filter {
|
||||
public class ExecuteLimitFilter extends ListenableFilter {
|
||||
|
||||
private static final String EXECUTELIMIT_FILTER_START_TIME = "execugtelimit_filter_start_time";
|
||||
|
||||
public ExecuteLimitFilter() {
|
||||
super.listener = new ExecuteLimitListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
|
|
@ -46,20 +54,32 @@ public class ExecuteLimitFilter implements Filter {
|
|||
"\" /> limited.");
|
||||
}
|
||||
|
||||
long begin = System.currentTimeMillis();
|
||||
boolean isSuccess = true;
|
||||
invocation.setAttachment(EXECUTELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
|
||||
try {
|
||||
return invoker.invoke(invocation);
|
||||
} catch (Throwable t) {
|
||||
isSuccess = false;
|
||||
if (t instanceof RuntimeException) {
|
||||
throw (RuntimeException) t;
|
||||
} else {
|
||||
throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
|
||||
}
|
||||
} finally {
|
||||
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
static class ExecuteLimitListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), false);
|
||||
}
|
||||
|
||||
private long getElapsed(Invocation invocation) {
|
||||
String beginTime = invocation.getAttachment(EXECUTELIMIT_FILTER_START_TIME);
|
||||
return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,13 @@ import org.apache.dubbo.common.serialize.Serialization;
|
|||
import org.apache.dubbo.common.utils.PojoUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.service.GenericException;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
|
|
@ -47,11 +46,15 @@ import java.lang.reflect.Method;
|
|||
* GenericInvokerFilter.
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER, order = -20000)
|
||||
public class GenericFilter implements Filter {
|
||||
public class GenericFilter extends ListenableFilter {
|
||||
|
||||
public GenericFilter() {
|
||||
super.listener = new GenericListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
|
||||
if (inv.getMethodName().equals(Constants.$INVOKE)
|
||||
if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
|
||||
&& inv.getArguments() != null
|
||||
&& inv.getArguments().length == 3
|
||||
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
|
||||
|
|
@ -108,26 +111,7 @@ public class GenericFilter implements Filter {
|
|||
}
|
||||
}
|
||||
}
|
||||
Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
|
||||
if (result.hasException()
|
||||
&& !(result.getException() instanceof GenericException)) {
|
||||
return new RpcResult(new GenericException(result.getException()));
|
||||
}
|
||||
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
|
||||
try {
|
||||
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
|
||||
ExtensionLoader.getExtensionLoader(Serialization.class)
|
||||
.getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
|
||||
.serialize(null, os).writeObject(result.getValue());
|
||||
return new RpcResult(os.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RpcException("Serialize result failed.", e);
|
||||
}
|
||||
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
|
||||
return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
|
||||
} else {
|
||||
return new RpcResult(PojoUtils.generalize(result.getValue()));
|
||||
}
|
||||
return invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RpcException(e.getMessage(), e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
|
|
@ -137,4 +121,42 @@ public class GenericFilter implements Filter {
|
|||
return invoker.invoke(inv);
|
||||
}
|
||||
|
||||
static class GenericListener implements Listener {
|
||||
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation inv) {
|
||||
if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
|
||||
&& inv.getArguments() != null
|
||||
&& inv.getArguments().length == 3
|
||||
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
|
||||
|
||||
String generic = inv.getAttachment(Constants.GENERIC_KEY);
|
||||
if (StringUtils.isBlank(generic)) {
|
||||
generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY);
|
||||
}
|
||||
|
||||
if (result.hasException() && !(result.getException() instanceof GenericException)) {
|
||||
result.setException(new GenericException(result.getException()));
|
||||
}
|
||||
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
|
||||
try {
|
||||
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
|
||||
ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(result.getValue());
|
||||
result.setValue(os.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RpcException("Serialize result failed.", e);
|
||||
}
|
||||
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
|
||||
result.setValue(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
|
||||
} else {
|
||||
result.setValue(PojoUtils.generalize(result.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,37 +25,42 @@ import org.apache.dubbo.common.logger.Logger;
|
|||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.PojoUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.service.GenericException;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* GenericImplInvokerFilter
|
||||
*/
|
||||
@Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000)
|
||||
public class GenericImplFilter implements Filter {
|
||||
public class GenericImplFilter extends ListenableFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
|
||||
|
||||
private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class};
|
||||
|
||||
public GenericImplFilter() {
|
||||
super.listener = new GenericImplListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY);
|
||||
if (ProtocolUtils.isGeneric(generic)
|
||||
&& !Constants.$INVOKE.equals(invocation.getMethodName())
|
||||
&& (!Constants.$INVOKE.equals(invocation.getMethodName()) && !Constants.$INVOKE_ASYNC.equals(invocation.getMethodName()))
|
||||
&& invocation instanceof RpcInvocation) {
|
||||
RpcInvocation invocation2 = (RpcInvocation) invocation;
|
||||
RpcInvocation invocation2 = new RpcInvocation(invocation);
|
||||
String methodName = invocation2.getMethodName();
|
||||
Class<?>[] parameterTypes = invocation2.getParameterTypes();
|
||||
Object[] arguments = invocation2.getArguments();
|
||||
|
|
@ -75,77 +80,15 @@ public class GenericImplFilter implements Filter {
|
|||
args = PojoUtils.generalize(arguments);
|
||||
}
|
||||
|
||||
invocation2.setMethodName(Constants.$INVOKE);
|
||||
if (RpcUtils.isReturnTypeFuture(invocation)) {
|
||||
invocation2.setMethodName(Constants.$INVOKE_ASYNC);
|
||||
} else {
|
||||
invocation2.setMethodName(Constants.$INVOKE);
|
||||
}
|
||||
invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
|
||||
invocation2.setArguments(new Object[]{methodName, types, args});
|
||||
Result result = invoker.invoke(invocation2);
|
||||
|
||||
if (!result.hasException()) {
|
||||
Object value = result.getValue();
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
|
||||
if (ProtocolUtils.isBeanGenericSerialization(generic)) {
|
||||
if (value == null) {
|
||||
return new RpcResult(value);
|
||||
} else if (value instanceof JavaBeanDescriptor) {
|
||||
return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
|
||||
} else {
|
||||
throw new RpcException(
|
||||
"The type of result value is " +
|
||||
value.getClass().getName() +
|
||||
" other than " +
|
||||
JavaBeanDescriptor.class.getName() +
|
||||
", and the result is " +
|
||||
value);
|
||||
}
|
||||
} else {
|
||||
return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RpcException(e.getMessage(), e);
|
||||
}
|
||||
} else if (result.getException() instanceof GenericException) {
|
||||
GenericException exception = (GenericException) result.getException();
|
||||
try {
|
||||
String className = exception.getExceptionClass();
|
||||
Class<?> clazz = ReflectUtils.forName(className);
|
||||
Throwable targetException = null;
|
||||
Throwable lastException = null;
|
||||
try {
|
||||
targetException = (Throwable) clazz.newInstance();
|
||||
} catch (Throwable e) {
|
||||
lastException = e;
|
||||
for (Constructor<?> constructor : clazz.getConstructors()) {
|
||||
try {
|
||||
targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
|
||||
break;
|
||||
} catch (Throwable e1) {
|
||||
lastException = e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetException != null) {
|
||||
try {
|
||||
Field field = Throwable.class.getDeclaredField("detailMessage");
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
field.set(targetException, exception.getExceptionMessage());
|
||||
} catch (Throwable e) {
|
||||
logger.warn(e.getMessage(), e);
|
||||
}
|
||||
result = new RpcResult(targetException);
|
||||
} else if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (invocation.getMethodName().equals(Constants.$INVOKE)
|
||||
return invoker.invoke(invocation2);
|
||||
} else if ((invocation.getMethodName().equals(Constants.$INVOKE) || invocation.getMethodName().equals(Constants.$INVOKE_ASYNC))
|
||||
&& invocation.getArguments() != null
|
||||
&& invocation.getArguments().length == 3
|
||||
&& ProtocolUtils.isGeneric(generic)) {
|
||||
|
|
@ -166,20 +109,89 @@ public class GenericImplFilter implements Filter {
|
|||
}
|
||||
}
|
||||
|
||||
((RpcInvocation) invocation).setAttachment(
|
||||
invocation.setAttachment(
|
||||
Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY));
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
|
||||
private void error(String generic, String expected, String actual) throws RpcException {
|
||||
throw new RpcException(
|
||||
"Generic serialization [" +
|
||||
generic +
|
||||
"] only support message type " +
|
||||
expected +
|
||||
" and your message type is " +
|
||||
actual);
|
||||
throw new RpcException("Generic serialization [" + generic + "] only support message type " + expected + " and your message type is " + actual);
|
||||
}
|
||||
|
||||
static class GenericImplListener implements Listener {
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY);
|
||||
String methodName = invocation.getMethodName();
|
||||
Class<?>[] parameterTypes = invocation.getParameterTypes();
|
||||
if (ProtocolUtils.isGeneric(generic)
|
||||
&& (!Constants.$INVOKE.equals(invocation.getMethodName()) && !Constants.$INVOKE_ASYNC.equals(invocation.getMethodName()))
|
||||
&& invocation instanceof RpcInvocation) {
|
||||
if (!result.hasException()) {
|
||||
Object value = result.getValue();
|
||||
try {
|
||||
Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
|
||||
if (ProtocolUtils.isBeanGenericSerialization(generic)) {
|
||||
if (value == null) {
|
||||
result.setValue(value);
|
||||
} else if (value instanceof JavaBeanDescriptor) {
|
||||
result.setValue(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
|
||||
} else {
|
||||
throw new RpcException("The type of result value is " + value.getClass().getName() + " other than " + JavaBeanDescriptor.class.getName() + ", and the result is " + value);
|
||||
}
|
||||
} else {
|
||||
Type[] types = ReflectUtils.getReturnTypes(method);
|
||||
result.setValue(PojoUtils.realize(value, (Class<?>) types[0], types[1]));
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RpcException(e.getMessage(), e);
|
||||
}
|
||||
} else if (result.getException() instanceof GenericException) {
|
||||
GenericException exception = (GenericException) result.getException();
|
||||
try {
|
||||
String className = exception.getExceptionClass();
|
||||
Class<?> clazz = ReflectUtils.forName(className);
|
||||
Throwable targetException = null;
|
||||
Throwable lastException = null;
|
||||
try {
|
||||
targetException = (Throwable) clazz.newInstance();
|
||||
} catch (Throwable e) {
|
||||
lastException = e;
|
||||
for (Constructor<?> constructor : clazz.getConstructors()) {
|
||||
try {
|
||||
targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
|
||||
break;
|
||||
} catch (Throwable e1) {
|
||||
lastException = e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetException != null) {
|
||||
try {
|
||||
Field field = Throwable.class.getDeclaredField("detailMessage");
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
field.set(targetException, exception.getExceptionMessage());
|
||||
} catch (Throwable e) {
|
||||
logger.warn(e.getMessage(), e);
|
||||
}
|
||||
result.setException(targetException);
|
||||
} else if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,11 @@ import org.apache.dubbo.common.Constants;
|
|||
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 @@ import java.util.Arrays;
|
|||
* Log any invocation timeout, but don't stop server from running
|
||||
*/
|
||||
@Activate(group = Constants.PROVIDER)
|
||||
public class TimeoutFilter implements Filter {
|
||||
public class TimeoutFilter extends ListenableFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
|
||||
|
||||
private static final String TIMEOUT_FILTER_START_TIME = "timeout_filter_start_time";
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
public TimeoutFilter() {
|
||||
super.listener = new TimeoutListener();
|
||||
}
|
||||
|
||||
@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.");
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
invocation.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
|
||||
static class TimeoutListener implements Listener {
|
||||
|
||||
@Override
|
||||
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
|
||||
String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
|
||||
if (startAttach != null) {
|
||||
long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
|
||||
if (invoker.getUrl() != null && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("invoke time out. method: " + invocation.getMethodName() + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is " + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
package org.apache.dubbo.rpc.protocol;
|
||||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.Version;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
|
|
@ -24,13 +23,13 @@ import org.apache.dubbo.common.logger.LoggerFactory;
|
|||
import org.apache.dubbo.common.utils.ArrayUtils;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
|
@ -148,9 +147,8 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
|
|||
*/
|
||||
invocation.addAttachments(contextAttachments);
|
||||
}
|
||||
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
|
||||
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
|
||||
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
|
||||
|
||||
try {
|
||||
|
|
@ -158,21 +156,21 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
|
|||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
|||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -82,4 +83,11 @@ public abstract class AbstractProtocol implements Protocol {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
|
||||
return new AsyncToSyncInvoker<>(doRefer(type, url));
|
||||
}
|
||||
|
||||
protected abstract <T> Invoker<T> doRefer(Class<T> type, URL url) throws RpcException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,13 +92,14 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> refer(final Class<T> type, final URL url) throws RpcException {
|
||||
final Invoker<T> target = proxyFactory.getInvoker(doRefer(type, url), type, url);
|
||||
public <T> Invoker<T> doRefer(final Class<T> type, final URL url) throws RpcException {
|
||||
final Invoker<T> target = proxyFactory.getInvoker(getFrameworkProxy(type, url), type, url);
|
||||
Invoker<T> invoker = new AbstractInvoker<T>(type, url) {
|
||||
@Override
|
||||
protected Result doInvoke(Invocation invocation) throws Throwable {
|
||||
try {
|
||||
Result result = target.invoke(invocation);
|
||||
// FIXME result is an AsyncRpcResult instance.
|
||||
Throwable e = result.getException();
|
||||
if (e != null) {
|
||||
for (Class<?> rpcException : rpcExceptions) {
|
||||
|
|
@ -143,6 +144,6 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
|
|||
|
||||
protected abstract <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException;
|
||||
|
||||
protected abstract <T> T doRefer(Class<T> type, URL url) throws RpcException;
|
||||
protected abstract <T> T getFrameworkProxy(Class<T> type, URL url) throws RpcException;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <T>
|
||||
*/
|
||||
public class AsyncToSyncInvoker<T> implements Invoker<T> {
|
||||
|
||||
private Invoker<T> invoker;
|
||||
|
||||
public AsyncToSyncInvoker(Invoker<T> invoker) {
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> 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<T> getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,11 +19,11 @@ package org.apache.dubbo.rpc.protocol;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ListenableFilter;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
|
@ -70,14 +70,29 @@ public class ProtocolFilterWrapper implements Protocol {
|
|||
|
||||
@Override
|
||||
public Result invoke(Invocation invocation) throws RpcException {
|
||||
Result result = filter.invoke(next, invocation);
|
||||
if (result instanceof AsyncRpcResult) {
|
||||
AsyncRpcResult asyncResult = (AsyncRpcResult) result;
|
||||
asyncResult.thenApplyWithContext(r -> filter.onResponse(r, invoker, invocation));
|
||||
return asyncResult;
|
||||
} else {
|
||||
return filter.onResponse(result, invoker, invocation);
|
||||
Result asyncResult;
|
||||
try {
|
||||
asyncResult = filter.invoke(next, invocation);
|
||||
} catch (Exception e) {
|
||||
// onError callback
|
||||
if (filter instanceof ListenableFilter) {
|
||||
Filter.Listener listener = ((ListenableFilter) filter).listener();
|
||||
if (listener != null) {
|
||||
listener.onError(e, invoker, invocation);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return asyncResult.thenApplyWithContext(r -> {
|
||||
// onResponse callback
|
||||
if (filter instanceof ListenableFilter) {
|
||||
Filter.Listener listener = ((ListenableFilter) filter).listener();
|
||||
if (listener != null) {
|
||||
listener.onResponse(r, invoker, invocation);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.proxy;
|
|||
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.Invoker;
|
|||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
/**
|
||||
* InvokerWrapper
|
||||
|
|
@ -78,30 +79,47 @@ public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
|
|||
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<Object>) 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<Object> future = wrapWithFuture(value, invocation);
|
||||
CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
|
||||
AppResponse result = new AppResponse();
|
||||
if (t != null) {
|
||||
if (t instanceof CompletionException) {
|
||||
result.setException(t.getCause());
|
||||
} else {
|
||||
result.setException(t);
|
||||
}
|
||||
} else {
|
||||
result.setValue(obj);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return new AsyncRpcResult(appResponseFuture, invocation);
|
||||
} catch (InvocationTargetException e) {
|
||||
// TODO async throw exception before async thread write back, should stop asyncContext
|
||||
if (rpcContext.isAsyncStarted() && !rpcContext.stopAsync()) {
|
||||
if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
|
||||
logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
|
||||
}
|
||||
return new RpcResult(e.getTargetException());
|
||||
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
|
||||
} catch (Throwable e) {
|
||||
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> 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<Object>) value;
|
||||
}
|
||||
return CompletableFuture.completedFuture(value);
|
||||
}
|
||||
|
||||
protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,12 +16,10 @@
|
|||
*/
|
||||
package org.apache.dubbo.rpc.proxy;
|
||||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -54,16 +52,6 @@ public class InvokerInvocationHandler implements InvocationHandler {
|
|||
return invoker.equals(args[0]);
|
||||
}
|
||||
|
||||
return invoker.invoke(createInvocation(method, args)).recreate();
|
||||
return invoker.invoke(new RpcInvocation(method, args)).recreate();
|
||||
}
|
||||
|
||||
private RpcInvocation createInvocation(Method method, Object[] args) {
|
||||
RpcInvocation invocation = new RpcInvocation(method, args);
|
||||
if (RpcUtils.hasFutureReturnType(method)) {
|
||||
invocation.setAttachment(Constants.FUTURE_RETURNTYPE_KEY, "true");
|
||||
invocation.setAttachment(Constants.ASYNC_KEY, "true");
|
||||
}
|
||||
return invocation;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object> $invokeAsync(String method, String[] parameterTypes, Object[] args) throws GenericException {
|
||||
Object object = $invoke(method, parameterTypes, args);
|
||||
if (object instanceof CompletableFuture) {
|
||||
return (CompletableFuture<Object>) object;
|
||||
}
|
||||
return CompletableFuture.completedFuture(object);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,18 +19,18 @@ package org.apache.dubbo.rpc.support;
|
|||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.utils.ArrayUtils;
|
||||
import org.apache.dubbo.common.utils.ConfigUtils;
|
||||
import org.apache.dubbo.common.utils.PojoUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.common.utils.ArrayUtils;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.RpcResult;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ final public class MockInvoker<T> implements Invoker<T> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ final public class MockProtocol extends AbstractProtocol {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
|
||||
protected <T> Invoker<T> doRefer(Class<T> type, URL url) throws RpcException {
|
||||
return new MockInvoker<T>(url);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,15 +23,14 @@ 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.Invocation;
|
||||
import org.apache.dubbo.rpc.InvokeMode;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
|
|
@ -65,7 +64,6 @@ public class RpcUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
// TODO why not get return type when initialize Invocation?
|
||||
public static Type[] getReturnTypes(Invocation invocation) {
|
||||
try {
|
||||
if (invocation != null && invocation.getInvoker() != null
|
||||
|
|
@ -80,24 +78,7 @@ public class RpcUtils {
|
|||
if (method.getReturnType() == void.class) {
|
||||
return null;
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
Type genericReturnType = method.getGenericReturnType();
|
||||
if (Future.class.isAssignableFrom(returnType)) {
|
||||
if (genericReturnType instanceof ParameterizedType) {
|
||||
Type actualArgType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
|
||||
if (actualArgType instanceof ParameterizedType) {
|
||||
returnType = (Class<?>) ((ParameterizedType) actualArgType).getRawType();
|
||||
genericReturnType = actualArgType;
|
||||
} else {
|
||||
returnType = (Class<?>) actualArgType;
|
||||
genericReturnType = returnType;
|
||||
}
|
||||
} else {
|
||||
returnType = null;
|
||||
genericReturnType = null;
|
||||
}
|
||||
}
|
||||
return new Type[]{returnType, genericReturnType};
|
||||
return ReflectUtils.getReturnTypes(method);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
|
|
@ -184,11 +165,22 @@ public class RpcUtils {
|
|||
}
|
||||
|
||||
public static boolean isReturnTypeFuture(Invocation inv) {
|
||||
return Boolean.TRUE.toString().equals(inv.getAttachment(Constants.FUTURE_RETURNTYPE_KEY));
|
||||
Class<?> clazz = getReturnType(inv);
|
||||
return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv);
|
||||
}
|
||||
|
||||
public static boolean hasFutureReturnType(Method method) {
|
||||
return CompletableFuture.class.isAssignableFrom(method.getReturnType());
|
||||
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 isGenericAsync(Invocation inv) {
|
||||
return Constants.$INVOKE_ASYNC.equals(inv.getMethodName());
|
||||
}
|
||||
|
||||
public static boolean isOneway(URL url, Invocation inv) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class RpcResultTest {
|
|||
@Test
|
||||
public void testRecreateWithNormalException() {
|
||||
NullPointerException npe = new NullPointerException();
|
||||
RpcResult rpcResult = new RpcResult(npe);
|
||||
AppResponse rpcResult = new AppResponse(npe);
|
||||
try {
|
||||
rpcResult.recreate();
|
||||
fail();
|
||||
|
|
@ -64,7 +64,7 @@ public class RpcResultTest {
|
|||
}
|
||||
// end construct a NullPointerException with empty stackTrace
|
||||
|
||||
RpcResult rpcResult = new RpcResult(throwable);
|
||||
AppResponse rpcResult = new AppResponse(throwable);
|
||||
try {
|
||||
rpcResult.recreate();
|
||||
fail();
|
||||
|
|
|
|||
|
|
@ -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 java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
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 class ActiveLimitFilterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeTimeOut() {
|
||||
public void testInvokeTimeOut() throws Exception {
|
||||
int totalThread = 100;
|
||||
int maxActives = 10;
|
||||
long timeout = 1;
|
||||
|
|
@ -120,9 +121,14 @@ public class ActiveLimitFilterTest {
|
|||
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 class ActiveLimitFilterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeNotTimeOut() {
|
||||
public void testInvokeNotTimeOut() throws Exception {
|
||||
int totalThread = 100;
|
||||
int maxActives = 10;
|
||||
long timeout = 1000;
|
||||
|
|
@ -163,9 +169,14 @@ public class ActiveLimitFilterTest {
|
|||
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 class ActiveLimitFilterTest {
|
|||
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ import static org.mockito.Mockito.mock;
|
|||
* 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 class CompatibleFilterFilterTest {
|
|||
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 class CompatibleFilterFilterTest {
|
|||
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 class CompatibleFilterFilterTest {
|
|||
}
|
||||
|
||||
@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 class CompatibleFilterFilterTest {
|
|||
invoker = mock(Invoker.class);
|
||||
given(invoker.isAvailable()).willReturn(true);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
RpcResult result = new RpcResult();
|
||||
AppResponse result = new AppResponse();
|
||||
result.setValue("High");
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(result, invocation));
|
||||
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&serialization=json");
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
|
||||
Result filterResult = compatibleFilter.invoke(invoker, invocation);
|
||||
assertEquals(Type.High, filterResult.getValue());
|
||||
Result asyncResult = compatibleFilter.invoke(invoker, invocation);
|
||||
Result rpcResult = asyncResult.get();
|
||||
compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
|
||||
assertEquals(Type.High, rpcResult.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokerNonJsonEnumSerialization() {
|
||||
public void testInvokerNonJsonEnumSerialization() throws Exception {
|
||||
invocation = mock(Invocation.class);
|
||||
given(invocation.getMethodName()).willReturn("enumlength");
|
||||
given(invocation.getParameterTypes()).willReturn(new Class<?>[]{Type[].class});
|
||||
|
|
@ -117,14 +120,16 @@ public class CompatibleFilterFilterTest {
|
|||
invoker = mock(Invoker.class);
|
||||
given(invoker.isAvailable()).willReturn(true);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
RpcResult result = new RpcResult();
|
||||
AppResponse result = new AppResponse();
|
||||
result.setValue("High");
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(result, invocation));
|
||||
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
|
||||
Result filterResult = compatibleFilter.invoke(invoker, invocation);
|
||||
assertEquals(Type.High, filterResult.getValue());
|
||||
Result asyncResult = compatibleFilter.invoke(invoker, invocation);
|
||||
Result rpcResult = asyncResult.get();
|
||||
compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
|
||||
assertEquals(Type.High, rpcResult.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -137,7 +142,7 @@ public class CompatibleFilterFilterTest {
|
|||
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 class CompatibleFilterFilterTest {
|
|||
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");
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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.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 class ConsumerContextFilterTest {
|
|||
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
|
||||
Invoker<DemoService> invoker = new MyInvoker<DemoService>(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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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 class ContextFilterTest {
|
|||
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");
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue