merge async changes in 3.x to 2.7 (#3997)

This commit is contained in:
ken.lj 2019-05-23 10:19:04 +08:00 committed by cvictory
parent df084addc8
commit ebafc18c4d
130 changed files with 1823 additions and 1665 deletions

View File

@ -23,11 +23,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;
@ -103,7 +103,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
}
}

View File

@ -18,18 +18,18 @@ 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;
import java.util.List;
/**
* When invoke fails, log the error message and ignore this error by returning an empty RpcResult.
* When invoke fails, log the error message and ignore this error by returning an empty Result.
* Usually used to write audit logs and other operations
*
* <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
@ -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
}
}
}

View File

@ -40,6 +40,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
/**
* NOTICE! This implementation does not work well with async call.
*
* Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources.
*
* <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
@ -70,7 +72,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.

View File

@ -22,12 +22,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;
@ -40,17 +40,16 @@ 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;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
import static org.apache.dubbo.rpc.Constants.MERGER_KEY;
/**
* @param <T>
*/
@SuppressWarnings("unchecked")
public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
@ -90,26 +89,21 @@ 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);
RpcInvocation subInvocation = new RpcInvocation(invocation, invoker);
subInvocation.setAttachment(ASYNC_KEY, "true");
results.put(invoker.getUrl().getServiceKey(), invoker.invoke(subInvocation));
}
Object result = null;
List<Result> resultList = new ArrayList<Result>(results.size());
int timeout = getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 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(),
@ -123,13 +117,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(".")) {
@ -177,7 +171,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);
}

View File

@ -21,12 +21,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;
@ -115,7 +115,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());
}

View File

@ -19,12 +19,12 @@ package org.apache.dubbo.rpc.cluster;
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"

View File

@ -57,6 +57,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;
}

View File

@ -18,12 +18,12 @@ package org.apache.dubbo.rpc.cluster.router.file;
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -17,10 +17,11 @@
package org.apache.dubbo.rpc.cluster.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.junit.jupiter.api.Assertions;
@ -120,7 +121,7 @@ public class MergeableClusterInvokerTest {
return MenuService.class;
}
if ("invoke".equals(method.getName())) {
return new RpcResult(firstMenu);
return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation);
}
return null;
}
@ -136,7 +137,7 @@ public class MergeableClusterInvokerTest {
return MenuService.class;
}
if ("invoke".equals(method.getName())) {
return new RpcResult(secondMenu);
return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation);
}
return null;
}
@ -196,14 +197,14 @@ public class MergeableClusterInvokerTest {
given(firstInvoker.getUrl()).willReturn(
url.addParameter(GROUP_KEY, "first"));
given(firstInvoker.getInterface()).willReturn(MenuService.class);
given(firstInvoker.invoke(invocation)).willReturn(new RpcResult())
given(firstInvoker.invoke(invocation)).willReturn(new AppResponse())
;
given(firstInvoker.isAvailable()).willReturn(true);
given(secondInvoker.getUrl()).willReturn(
url.addParameter(GROUP_KEY, "second"));
given(secondInvoker.getInterface()).willReturn(MenuService.class);
given(secondInvoker.invoke(invocation)).willReturn(new RpcResult())
given(secondInvoker.invoke(invocation)).willReturn(new AppResponse())
;
given(secondInvoker.isAvailable()).willReturn(true);

View File

@ -42,6 +42,62 @@ public interface RpcConstants {
String $INVOKE = "$invoke";
String $INVOKE_ASYNC = "$invokeAsync";
String $ECHO = "$echo";
String RETURN_PREFIX = "return ";
String THROW_PREFIX = "throw";
String FAIL_PREFIX = "fail:";
String FORCE_PREFIX = "force:";
String MERGER_KEY = "merger";
String IS_SERVER_KEY = "isserver";
String FORCE_USE_TAG = "dubbo.force.tag";
String GENERIC_SERIALIZATION_NATIVE_JAVA = "nativejava";
String GENERIC_SERIALIZATION_DEFAULT = "true";
String GENERIC_SERIALIZATION_BEAN = "bean";
String GENERIC_SERIALIZATION_PROTOBUF = "protobuf-json";
String TPS_LIMIT_RATE_KEY = "tps";
String TPS_LIMIT_INTERVAL_KEY = "tps.interval";
long DEFAULT_TPS_LIMIT_INTERVAL = 60 * 1000;
String AUTO_ATTACH_INVOCATIONID_KEY = "invocationid.autoattach";
String STUB_EVENT_KEY = "dubbo.stub.event";
boolean DEFAULT_STUB_EVENT = false;
String STUB_EVENT_METHODS_KEY = "dubbo.stub.event.methods";
String PROXY_KEY = "proxy";
String EXECUTES_KEY = "executes";
String REFERENCE_FILTER_KEY = "reference.filter";
String INVOKER_LISTENER_KEY = "invoker.listener";
String SERVICE_FILTER_KEY = "service.filter";
String EXPORTER_LISTENER_KEY = "exporter.listener";
String ACCESS_LOG_KEY = "accesslog";
String ACTIVES_KEY = "actives";
String CONNECTIONS_KEY = "connections";
}

View File

@ -142,7 +142,7 @@ public interface Logger {
/**
* Is debug logging currently enabled?
*
* 
* @return true if debug is enabled
*/
boolean isDebugEnabled();

View File

@ -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};
}
}

View File

@ -22,12 +22,14 @@ import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@ -401,6 +403,32 @@ public class ReflectUtilsTest {
});
}
@Test
public void testGetReturnTypes () throws Exception{
Class clazz = TypeClass.class;
Type[] types = ReflectUtils.getReturnTypes(clazz.getMethod("getFuture"));
Assertions.assertEquals("java.lang.String", types[0].getTypeName());
Assertions.assertEquals("java.lang.String", types[1].getTypeName());
Type[] types1 = ReflectUtils.getReturnTypes(clazz.getMethod("getString"));
Assertions.assertEquals("java.lang.String", types1[0].getTypeName());
Assertions.assertEquals("java.lang.String", types1[1].getTypeName());
Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture"));
Assertions.assertEquals("java.util.List", types2[0].getTypeName());
Assertions.assertEquals("java.util.List<java.lang.String>", types2[1].getTypeName());
}
public static interface TypeClass {
CompletableFuture<String> getFuture();
String getString();
CompletableFuture<List<String>> getListFuture();
}
public static class EmptyClass {
private EmptyProperty property;
public boolean set;

View File

@ -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, org.apache.dubbo.rpc.Invocation {
private org.apache.dubbo.rpc.Invocation delegate;

View File

@ -18,11 +18,30 @@
package com.alibaba.dubbo.rpc;
import java.util.Map;
import java.util.function.Function;
@Deprecated
public interface Result extends org.apache.dubbo.rpc.Result {
class CompatibleResult implements Result {
@Override
default void setValue(Object value) {
}
@Override
default void setException(Throwable t) {
}
abstract class AbstractResult extends org.apache.dubbo.rpc.AbstractResult implements Result {
@Override
public org.apache.dubbo.rpc.Result thenApplyWithContext(Function<org.apache.dubbo.rpc.Result, org.apache.dubbo.rpc.Result> fn) {
return null;
}
}
class CompatibleResult extends AbstractResult {
private org.apache.dubbo.rpc.Result delegate;
public CompatibleResult(org.apache.dubbo.rpc.Result result) {
@ -38,11 +57,21 @@ public interface Result extends org.apache.dubbo.rpc.Result {
return delegate.getValue();
}
@Override
public void setValue(Object value) {
delegate.setValue(value);
}
@Override
public Throwable getException() {
return delegate.getException();
}
@Override
public void setException(Throwable t) {
delegate.setException(t);
}
@Override
public boolean hasException() {
return delegate.hasException();
@ -53,11 +82,6 @@ public 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();

View File

@ -29,7 +29,7 @@ public class RpcContext extends org.apache.dubbo.rpc.RpcContext {
RpcContext copy = new RpcContext();
copy.getAttachments().putAll(rpcContext.getAttachments());
copy.get().putAll(rpcContext.get());
copy.setFuture(rpcContext.getFuture());
copy.setFuture(rpcContext.getCompletableFuture());
copy.setUrls(rpcContext.getUrls());
copy.setUrl(rpcContext.getUrl());
copy.setMethodName(rpcContext.getMethodName());

View File

@ -20,7 +20,6 @@ package com.alibaba.dubbo.rpc.support;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invocation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
@ -74,10 +73,6 @@ public class RpcUtils extends org.apache.dubbo.rpc.support.RpcUtils {
return org.apache.dubbo.rpc.support.RpcUtils.isReturnTypeFuture(inv);
}
public static boolean hasFutureReturnType(Method method) {
return org.apache.dubbo.rpc.support.RpcUtils.hasFutureReturnType(method);
}
public static boolean isOneway(URL url, Invocation inv) {
return org.apache.dubbo.rpc.support.RpcUtils.isOneway(url.getOriginalURL(), inv);
}

View File

@ -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 {

View File

@ -63,6 +63,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;
}

View File

@ -21,12 +21,12 @@ import org.apache.dubbo.cache.CacheFactory;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import java.io.Serializable;
@ -48,10 +48,10 @@ import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY;
* 3)&lt;dubbo:provider cache="expiring" /&gt;
* 4)&lt;dubbo:consumer cache="jcache" /&gt;
*
* If cache type is defined in method level then method level type will get precedence. According to above provided
* example, if service has two method, method1 and method2, method2 will have cache type as <b>threadlocal</b> where others will
* be backed by <b>lru</b>
* </pre>
*If cache type is defined in method level then method level type will get precedence. According to above provided
*example, if service has two method, method1 and method2, method2 will have cache type as <b>threadlocal</b> where others will
*be backed by <b>lru</b>
*</pre>
*
* @see org.apache.dubbo.rpc.Filter
* @see org.apache.dubbo.cache.support.lru.LruCacheFactory
@ -62,6 +62,7 @@ import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY;
* @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
* @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
* @see org.apache.dubbo.cache.support.expiring.ExpiringCache
*
*/
@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
public class CacheFilter implements Filter {
@ -83,7 +84,6 @@ public class CacheFilter implements Filter {
* If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store
* then it will return otherwise call the remote method and return value. If remote method's return valeu has error
* then it will not cache the value.
*
* @param invoker service
* @param invocation invocation.
* @return Cache returned value if found by the underlying cache store. If cache miss it will call target method.
@ -98,9 +98,9 @@ public 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);
@ -122,7 +122,7 @@ public class CacheFilter implements Filter {
private final Object value;
public ValueWrapper(Object value) {
public ValueWrapper (Object value) {
this.value = value;
}

View File

@ -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);
}
}

View File

@ -18,12 +18,12 @@ package org.apache.dubbo.validation.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
@ -90,7 +90,7 @@ public 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);

View File

@ -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});

View File

@ -88,4 +88,5 @@ public interface MonitorService {
*/
List<URL> lookup(URL query);
}

View File

@ -24,9 +24,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;
@ -52,10 +52,14 @@ import static org.apache.dubbo.common.constants.RpcConstants.OUTPUT_KEY;
* MonitorFilter. (SPI, Singleton, ThreadSafe)
*/
@Activate(group = {PROVIDER, CONSUMER})
public class MonitorFilter implements Filter {
public class MonitorFilter extends ListenableFilter {
private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time";
public MonitorFilter() {
super.listener = new MonitorListener();
}
/**
* The Concurrent counter
*/
@ -70,6 +74,7 @@ public 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
*
@ -81,105 +86,10 @@ public class MonitorFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
RpcContext context = RpcContext.getContext(); // provider must fetch context before invoke() gets called
String remoteHost = context.getRemoteHost();
long start = System.currentTimeMillis(); // record start timestamp
invocation.setAttachment(MONITOR_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
getConcurrent(invoker, invocation).incrementAndGet(); // count up
try {
Result result = invoker.invoke(invocation); // proceed invocation chain
collect(invoker, invocation, result, remoteHost, start, false);
return result;
} catch (RpcException e) {
collect(invoker, invocation, null, remoteHost, start, true);
throw e;
} finally {
getConcurrent(invoker, invocation).decrementAndGet(); // count down
}
} else {
return invoker.invoke(invocation);
}
}
/**
* The collector logic, it will be handled by the default monitor
*
* @param invoker
* @param invocation
* @param result the invoke result
* @param remoteHost the remote host address
* @param start the timestamp the invoke begin
* @param error if there is an error on the invoke
*/
private void collect(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
try {
URL monitorUrl = invoker.getUrl().getUrlParameter(MONITOR_KEY);
Monitor monitor = monitorFactory.getMonitor(monitorUrl);
if (monitor == null) {
return;
}
URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error);
monitor.collect(statisticsURL);
} catch (Throwable t) {
logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
}
}
/**
* Create statistics url
*
* @param invoker
* @param invocation
* @param result
* @param remoteHost
* @param start
* @param error
* @return
*/
private URL createStatisticsUrl(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
// ---- service statistics ----
long elapsed = System.currentTimeMillis() - start; // invocation cost
int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count
String application = invoker.getUrl().getParameter(APPLICATION_KEY);
String service = invoker.getInterface().getName(); // service name
String method = RpcUtils.getMethodName(invocation); // method name
String group = invoker.getUrl().getParameter(GROUP_KEY);
String version = invoker.getUrl().getParameter(VERSION_KEY);
int localPort;
String remoteKey, remoteValue;
if (CONSUMER_SIDE.equals(invoker.getUrl().getParameter(SIDE_KEY))) {
// ---- for service consumer ----
localPort = 0;
remoteKey = MonitorService.PROVIDER;
remoteValue = invoker.getUrl().getAddress();
} else {
// ---- for service provider ----
localPort = invoker.getUrl().getPort();
remoteKey = MonitorService.CONSUMER;
remoteValue = remoteHost;
}
String input = "", output = "";
if (invocation.getAttachment(INPUT_KEY) != null) {
input = invocation.getAttachment(INPUT_KEY);
}
if (result != null && result.getAttachment(OUTPUT_KEY) != null) {
output = result.getAttachment(OUTPUT_KEY);
}
return new URL(COUNT_PROTOCOL,
NetUtils.getLocalHost(), localPort,
service + PATH_SEPARATOR + method,
MonitorService.APPLICATION, application,
MonitorService.INTERFACE, service,
MonitorService.METHOD, method,
remoteKey, remoteValue,
error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1",
MonitorService.ELAPSED, String.valueOf(elapsed),
MonitorService.CONCURRENT, String.valueOf(concurrent),
INPUT_KEY, input,
OUTPUT_KEY, output,
GROUP_KEY, group,
VERSION_KEY, version);
return invoker.invoke(invocation); // proceed invocation chain
}
// concurrent counter
@ -193,4 +103,93 @@ 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(MONITOR_KEY)) {
collect(invoker, invocation, result, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), false);
getConcurrent(invoker, invocation).decrementAndGet(); // count down
}
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
collect(invoker, invocation, null, RpcContext.getContext().getRemoteHost(), Long.valueOf(invocation.getAttachment(MONITOR_FILTER_START_TIME)), true);
getConcurrent(invoker, invocation).decrementAndGet(); // count down
}
}
/**
* The collector logic, it will be handled by the default monitor
*
* @param invoker
* @param invocation
* @param result the invoke result
* @param remoteHost the remote host address
* @param start the timestamp the invoke begin
* @param error if there is an error on the invoke
*/
private void collect(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
try {
URL monitorUrl = invoker.getUrl().getUrlParameter(MONITOR_KEY);
Monitor monitor = monitorFactory.getMonitor(monitorUrl);
if (monitor == null) {
return;
}
URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error);
monitor.collect(statisticsURL);
} catch (Throwable t) {
logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
}
}
/**
* Create statistics url
*
* @param invoker
* @param invocation
* @param result
* @param remoteHost
* @param start
* @param error
* @return
*/
private URL createStatisticsUrl(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
// ---- service statistics ----
long elapsed = System.currentTimeMillis() - start; // invocation cost
int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count
String application = invoker.getUrl().getParameter(APPLICATION_KEY);
String service = invoker.getInterface().getName(); // service name
String method = RpcUtils.getMethodName(invocation); // method name
String group = invoker.getUrl().getParameter(GROUP_KEY);
String version = invoker.getUrl().getParameter(VERSION_KEY);
int localPort;
String remoteKey, remoteValue;
if (CONSUMER_SIDE.equals(invoker.getUrl().getParameter(SIDE_KEY))) {
// ---- for service consumer ----
localPort = 0;
remoteKey = MonitorService.PROVIDER;
remoteValue = invoker.getUrl().getAddress();
} else {
// ---- for service provider ----
localPort = invoker.getUrl().getPort();
remoteKey = MonitorService.CONSUMER;
remoteValue = remoteHost;
}
String input = "", output = "";
if (invocation.getAttachment(INPUT_KEY) != null) {
input = invocation.getAttachment(INPUT_KEY);
}
if (result != null && result.getAttachment(OUTPUT_KEY) != null) {
output = result.getAttachment(OUTPUT_KEY);
}
return new URL(COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + PATH_SEPARATOR + method, MonitorService.APPLICATION, application, MonitorService.INTERFACE, service, MonitorService.METHOD, method, remoteKey, remoteValue, error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1", MonitorService.ELAPSED, String.valueOf(elapsed), MonitorService.CONCURRENT, String.valueOf(concurrent), INPUT_KEY, input, OUTPUT_KEY, output, GROUP_KEY, group, VERSION_KEY, version);
}
}
}

View File

@ -21,6 +21,7 @@ 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;
@ -77,7 +78,7 @@ public class MonitorFilterTest {
public Result invoke(Invocation invocation) throws RpcException {
lastInvocation = invocation;
return null;
return AsyncRpcResult.newDefaultAsyncResult(invocation);
}
@Override
@ -119,7 +120,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);
}
@ -155,7 +160,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);
}

View File

@ -23,7 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.store.DataStore;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.monitor.MetricsService;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
@ -31,7 +31,6 @@ import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.support.RpcUtils;
import com.alibaba.fastjson.JSON;
@ -66,6 +65,7 @@ import static org.apache.dubbo.monitor.Constants.METHOD;
import static org.apache.dubbo.monitor.Constants.METRICS_PORT;
import static org.apache.dubbo.monitor.Constants.METRICS_PROTOCOL;
import static org.apache.dubbo.monitor.Constants.SERVICE;
import static org.apache.dubbo.remoting.Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
public class MetricsFilter implements Filter {
@ -178,7 +178,7 @@ public class MetricsFilter implements Filter {
private List<MetricObject> getThreadPoolMessage() {
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
Map<String, Object> executors = dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY);
Map<String, Object> executors = dataStore.get(EXECUTOR_SERVICE_COMPONENT_KEY);
List<MetricObject> threadPoolMtricList = new ArrayList<>();
for (Map.Entry<String, Object> entry : executors.entrySet()) {
@ -235,12 +235,9 @@ public class MetricsFilter implements Filter {
collector.collect(entry.getKey(), entry.getValue(), timestamp);
}
RpcResult result = new RpcResult();
List res = collector.build();
res.addAll(getThreadPoolMessage());
result.setValue(JSON.toJSONString(res));
return result;
return AsyncRpcResult.newDefaultAsyncResult(JSON.toJSONString(res), invocation);
}
@Override

View File

@ -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;
}

View File

@ -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) {
}
};
}

View File

@ -32,7 +32,7 @@
<javax.ws.rs.version>2.1</javax.ws.rs.version>
<argline>-Dnetwork_interface_denylist=docker0</argline>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>

View File

@ -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)

View File

@ -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 {

View File

@ -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.

View File

@ -1,38 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.remoting.exchange;
/**
* Callback
*/
public interface ResponseCallback {
/**
* done.
*
* @param response
*/
void done(Object response);
/**
* caught exception.
*
* @param exception
*/
void caught(Throwable exception);
}

View File

@ -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();
}

View File

@ -28,17 +28,13 @@ 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;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
@ -46,7 +42,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
/**
* DefaultFuture.
*/
public class DefaultFuture implements ResponseFuture {
public class DefaultFuture extends CompletableFuture<Object> {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
@ -66,12 +62,8 @@ public class DefaultFuture implements ResponseFuture {
private final Channel channel;
private final Request request;
private final int timeout;
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private final long start = System.currentTimeMillis();
private volatile long sent;
private volatile Response response;
private volatile ResponseCallback callback;
private DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
@ -170,142 +162,32 @@ 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 = 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 static class TimeoutCheckTask implements TimerTask {
private DefaultFuture future;
TimeoutCheckTask(DefaultFuture future) {
this.future = future;
}
@Override
public void run(Timeout timeout) {
// remove from pending task
PENDING_TASKS.remove(future.getId());
if (future.isDone()) {
return;
}
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
// handle response.
DefaultFuture.received(future.getChannel(), timeoutResponse);
}
}
private void invokeCallback(ResponseCallback c) {
if (c == null) {
throw new NullPointerException("callback cannot be null.");
}
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
}
if (res.getStatus() == Response.OK) {
try {
c.done(res.getResult());
} catch (Exception e) {
logger.error("callback invoke error .result:" + res.getResult() + ",url:" + channel.getUrl(), e);
}
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
try {
TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
c.caught(te);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
} else {
try {
RuntimeException re = new RuntimeException(res.getErrorMessage());
c.caught(re);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
}
}
private Object returnFromResponse() throws RemotingException {
Response res = response;
private void doReceived(Response res) {
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
return res.getResult();
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()));
}
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() {
@ -328,27 +210,10 @@ public class DefaultFuture implements ResponseFuture {
return timeout;
}
private long getStartTimestamp() {
return start;
}
private void doSent() {
sent = System.currentTimeMillis();
}
private void doReceived(Response res) {
lock.lock();
try {
response = res;
done.signalAll();
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
private String getTimeoutMessage(boolean scan) {
long nowTimestamp = System.currentTimeMillis();
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
@ -361,4 +226,28 @@ public class DefaultFuture implements ResponseFuture {
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress();
}
private static class TimeoutCheckTask implements TimerTask {
private DefaultFuture future;
TimeoutCheckTask(DefaultFuture future) {
this.future = future;
}
@Override
public void run(Timeout timeout) {
if (future == null || future.isDone()) {
return;
}
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
// handle response.
DefaultFuture.received(future.getChannel(), timeoutResponse);
}
}
}

View File

@ -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;
}
}

View File

@ -27,10 +27,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;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
@ -99,12 +99,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(TIMEOUT_KEY, 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!");
}

View File

@ -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;
@ -65,7 +65,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);
}
@ -80,7 +80,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);
}

View File

@ -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));

View File

@ -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());
}

View File

@ -19,7 +19,7 @@ package org.apache.dubbo.remoting;
import java.io.Serializable;
/**
* RpcResult.
* AppResponse.
*/
public class MockResult implements Serializable {

View File

@ -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());
}
}

View File

@ -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, "");
}

View File

@ -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());
}

View File

@ -30,6 +30,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;

View File

@ -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());
}

View File

@ -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());
}

View File

@ -16,59 +16,10 @@
*/
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
*
*/
public abstract class AbstractResult implements Result {
protected Map<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);
}
public abstract class AbstractResult extends CompletableFuture<Result> implements Result {
}

View File

@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}:
* <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 described as follow, an abstraction of the definition of AsyncRpcResult:
* <pre>
* {@code
* Public class AsyncRpcResult implements CompletionStage<AppResponse> {
* ......
* }
* </pre>
* AsyncRpcResult is a future representing an unfinished RPC call, while AppResponse is the actual return type of this call.
* In theory, AppResponse does'n have to implement the {@link Result} interface, this is done mainly for compatibility purpose.
*
* @serial Do not change the class name and properties.
*/
public class AppResponse extends AbstractResult implements Serializable {
private static final long serialVersionUID = -6925924956850004727L;
private Object result;
private Throwable exception;
private Map<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<Result, Result> fn) {
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
}
@Override
public String toString() {
return "AppResponse [value=" + result + ", exception=" + exception + "]";
}
}

View File

@ -25,176 +25,174 @@ import java.util.concurrent.CompletionException;
import java.util.function.Function;
/**
* <b>NOTICE!!</b>
*
* This class represents an unfinished RPC call, it will hold some context information for this call, for example RpcContext and Invocation,
* so that when the call finishes and the result returns, it can guarantee all the contexts being recovered as the same as when the call was made
* before any callback is invoked.
* <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.
* TODO if it's reasonable or even right to keep a reference to Invocation?
* <p>
* As {@link Result} implements CompletionStage, {@link AsyncRpcResult} allows you to easily build a async filter chain whose status will be
* driven entirely by the state of the underlying RPC call.
* <p>
* AsyncRpcResult does not contain any concrete value (except the underlying value bring by CompletableFuture), consider it as a status transfer node.
* {@link #getValue()} and {@link #getException()} are all inherited from {@link Result} interface, implementing them are mainly
* for compatibility consideration. Because many legacy {@link Filter} implementation are most possibly to call getValue directly.
*/
public class AsyncRpcResult extends AbstractResult {
private static final Logger logger = LoggerFactory.getLogger(AsyncRpcResult.class);
/**
* RpcContext can be changed, because thread may have been used by other thread. It should be cloned before store.
* So we use Invocation instead, Invocation will create for every invoke, but invocation only support attachments of string type.
* RpcContext may already have been changed when callback happens, it happens when the same thread is used to execute another RPC call.
* So we should keep the reference of current RpcContext instance and restore it before callback being executed.
*/
private RpcContext storedContext;
private RpcContext storedServerContext;
protected CompletableFuture<Object> valueFuture;
private Invocation invocation;
protected CompletableFuture<Result> resultFuture;
public AsyncRpcResult(CompletableFuture<Object> future) {
this(future, true);
public AsyncRpcResult(Invocation invocation) {
this.invocation = invocation;
this.storedContext = RpcContext.getContext();
this.storedServerContext = RpcContext.getServerContext();
}
public AsyncRpcResult(CompletableFuture<Object> future, boolean registerCallback) {
this(future, new CompletableFuture<>(), registerCallback);
public AsyncRpcResult(AsyncRpcResult asyncRpcResult) {
this.invocation = asyncRpcResult.getInvocation();
this.storedContext = asyncRpcResult.getStoredContext();
this.storedServerContext = asyncRpcResult.getStoredServerContext();
}
/**
* @param future
* @param rFuture
* @param registerCallback
* Notice the return type of {@link #getValue} is the actual type of the RPC method, not {@link AppResponse}
*
* @return
*/
public AsyncRpcResult(CompletableFuture<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();
@Override
public Object getValue() {
return getAppResponse().getValue();
}
@Override
public Object getValue() {
return getRpcResult().getValue();
public void setValue(Object value) {
AppResponse appResponse = new AppResponse();
appResponse.setValue(value);
this.complete(appResponse);
}
@Override
public Throwable getException() {
return getRpcResult().getException();
return getAppResponse().getException();
}
@Override
public void setException(Throwable t) {
AppResponse appResponse = new AppResponse();
appResponse.setException(t);
this.complete(appResponse);
}
@Override
public boolean hasException() {
return getRpcResult().hasException();
return getAppResponse().hasException();
}
@Override
public Object getResult() {
return getRpcResult().getResult();
}
public CompletableFuture getValueFuture() {
return valueFuture;
}
public CompletableFuture<Result> getResultFuture() {
return resultFuture;
}
public void setResultFuture(CompletableFuture<Result> resultFuture) {
this.resultFuture = resultFuture;
}
public Result getRpcResult() {
public Result getAppResponse() {
try {
if (resultFuture.isDone()) {
return resultFuture.get();
if (this.isDone()) {
return this.get();
}
} catch (Exception e) {
// This should never happen;
logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e);
}
return new RpcResult();
return new AppResponse();
}
@Override
public Object recreate() throws Throwable {
return valueFuture;
RpcInvocation rpcInvocation = (RpcInvocation) invocation;
if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) {
AppResponse appResponse = new AppResponse();
CompletableFuture<Object> future = new CompletableFuture<>();
appResponse.setValue(future);
this.whenComplete((result, t) -> {
if (t != null) {
if (t instanceof CompletionException) {
t = t.getCause();
}
future.completeExceptionally(t);
} else {
if (result.hasException()) {
future.completeExceptionally(result.getException());
} else {
future.complete(result.getValue());
}
}
});
return appResponse.recreate();
} else if (this.isDone()) {
return this.get().recreate();
}
return (new AppResponse()).recreate();
}
public void thenApplyWithContext(Function<Result, Result> fn) {
this.resultFuture = resultFuture.thenApply(fn.compose(beforeContext).andThen(afterContext));
public Result thenApplyWithContext(Function<Result, Result> fn) {
CompletableFuture<Result> future = this.thenApply(fn.compose(beforeContext).andThen(afterContext));
AsyncRpcResult nextAsyncRpcResult = new AsyncRpcResult(this);
nextAsyncRpcResult.subscribeTo(future);
return nextAsyncRpcResult;
}
public void subscribeTo(CompletableFuture<?> future) {
future.whenComplete((obj, t) -> {
if (t != null) {
this.completeExceptionally(t);
} else {
this.complete((Result) obj);
}
});
}
@Override
public Map<String, String> getAttachments() {
return getRpcResult().getAttachments();
return getAppResponse().getAttachments();
}
@Override
public void setAttachments(Map<String, String> map) {
getRpcResult().setAttachments(map);
getAppResponse().setAttachments(map);
}
@Override
public void addAttachments(Map<String, String> map) {
getRpcResult().addAttachments(map);
getAppResponse().addAttachments(map);
}
@Override
public String getAttachment(String key) {
return getRpcResult().getAttachment(key);
return getAppResponse().getAttachment(key);
}
@Override
public String getAttachment(String key, String defaultValue) {
return getRpcResult().getAttachment(key, defaultValue);
return getAppResponse().getAttachment(key, defaultValue);
}
@Override
public void setAttachment(String key, String value) {
getRpcResult().setAttachment(key, value);
getAppResponse().setAttachment(key, value);
}
public RpcContext getStoredContext() {
return storedContext;
}
public RpcContext getStoredServerContext() {
return storedServerContext;
}
public Invocation getInvocation() {
return invocation;
}
/**
@ -203,18 +201,51 @@ public class AsyncRpcResult extends AbstractResult {
private RpcContext tmpContext;
private RpcContext tmpServerContext;
private Function<Result, Result> beforeContext = (result) -> {
private Function<Result, Result> 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<Result, Result> afterContext = (appResponse) -> {
RpcContext.restoreContext(tmpContext);
RpcContext.restoreServerContext(tmpServerContext);
return result;
return appResponse;
};
/**
* Some utility methods used to quickly generate default AsyncRpcResult instance.
*/
public static AsyncRpcResult newDefaultAsyncResult(AppResponse appResponse, Invocation invocation) {
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);
asyncRpcResult.complete(appResponse);
return asyncRpcResult;
}
public static AsyncRpcResult newDefaultAsyncResult(Invocation invocation) {
return newDefaultAsyncResult(null, null, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) {
return newDefaultAsyncResult(value, null, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invocation) {
return newDefaultAsyncResult(null, t, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);
AppResponse appResponse = new AppResponse();
if (t != null) {
appResponse.setException(t);
} else {
appResponse.setValue(value);
}
asyncRpcResult.complete(appResponse);
return asyncRpcResult;
}
}

View File

@ -42,35 +42,29 @@ 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;
/**
* Return processing result
* Filter itself should only be response for passing invocation, all callbacks has been placed into {@link Listener}
*
* @param result result
* @param invoker invoker
* @param invocation invocation
* @return Return {@link Result}
* @param appResponse
* @param invoker
* @param invocation
* @return
*/
default Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
return result;
@Deprecated
default Result onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
return appResponse;
}
interface Listener {
void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.threadlocal.InternalThreadLocal;
import java.util.concurrent.CompletableFuture;
/**
* Used for async call scenario. But if the method you are calling has a {@link CompletableFuture<?>} signature
* you do not need to use this class since you will get a Future response directly.
* <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 InternalThreadLocal<CompletableFuture<?>> futureTL = new InternalThreadLocal<>();
/**
* 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);
}
}

View File

@ -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.
*

View File

@ -14,12 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
package com.alibaba.dubbo.remoting.exchange;
public enum InvokeMode {
SYNC, ASYNC, FUTURE;
/**
* 2019-04-18
*/
@Deprecated
public interface ResponseCallback extends org.apache.dubbo.remoting.exchange.ResponseCallback {
}

View File

@ -14,16 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.rpc.protocol.dubbo;
import org.apache.dubbo.remoting.exchange.ResponseFuture;
package org.apache.dubbo.rpc;
/**
* 2019-04-18
*
*/
public class FutureAdapter<V> extends org.apache.dubbo.rpc.protocol.dubbo.FutureAdapter<V> {
public FutureAdapter(ResponseFuture future) {
super(future);
public abstract class ListenableFilter implements Filter {
protected Listener listener = null;
public Listener listener() {
return listener;
}
}

View File

@ -18,16 +18,27 @@ package org.apache.dubbo.rpc;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.Function;
/**
* RPC invoke result. (API, Prototype, NonThreadSafe)
* (API, Prototype, NonThreadSafe)
*
* An RPC {@link Result}.
*
* Known implementations are:
* 1. {@link AsyncRpcResult}, it's a {@link CompletionStage} whose underlying value signifies the return value of an RPC call.
* 2. {@link AppResponse}, it inevitably inherits {@link CompletionStage} and {@link Future}, but you should never treat AppResponse as a type of Future,
* instead, it is a normal concrete type.
*
* @serial Don't change the class name and package name.
* @see org.apache.dubbo.rpc.Invoker#invoke(Invocation)
* @see org.apache.dubbo.rpc.RpcResult
* @see AppResponse
*/
public interface Result extends Serializable {
public interface Result extends CompletionStage<Result>, Future<Result>, Serializable {
/**
* Get invoke result.
@ -36,6 +47,8 @@ public interface Result extends Serializable {
*/
Object getValue();
void setValue(Object value);
/**
* Get exception.
*
@ -43,6 +56,8 @@ public interface Result extends Serializable {
*/
Throwable getException();
void setException(Throwable t);
/**
* Has exception.
*
@ -66,14 +81,6 @@ public interface Result extends Serializable {
*/
Object recreate() throws Throwable;
/**
* @see org.apache.dubbo.rpc.Result#getValue()
* @deprecated Replace to getValue()
*/
@Deprecated
Object getResult();
/**
* get attachments.
*
@ -111,4 +118,26 @@ public interface Result extends Serializable {
void setAttachment(String key, String value);
/**
* Returns the specified {@code valueIfAbsent} when not complete, or
* returns the result value or throws an exception when complete.
*
* @see CompletableFuture#getNow(Object)
*/
Result getNow(Result valueIfAbsent);
/**
* Add a callback which can be triggered when the RPC call finishes.
* <p>
* Just as the method name implies, this method will guarantee the callback being triggered under the same context as when the call was started,
* see implementation in {@link AsyncRpcResult#thenApplyWithContext(Function)}
*
* @param fn
* @return
*/
Result thenApplyWithContext(Function<Result, Result> fn);
default CompletableFuture<Result> completionFuture() {
return toCompletableFuture();
}
}

View File

@ -75,7 +75,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;
@ -144,32 +143,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.remoteApplicationName = this.remoteApplicationName;
copy.invokers = this.invokers;
copy.invoker = this.invoker;
copy.invocation = this.invocation;
copy.request = this.request;
copy.response = this.response;
copy.asyncContext = this.asyncContext;
return copy;
}
/**
* remove context.
*
@ -251,7 +224,7 @@ public class RpcContext {
*/
@SuppressWarnings("unchecked")
public <T> CompletableFuture<T> getCompletableFuture() {
return (CompletableFuture<T>) future;
return FutureContext.getCompletableFuture();
}
/**
@ -262,7 +235,7 @@ public class RpcContext {
*/
@SuppressWarnings("unchecked")
public <T> Future<T> getFuture() {
return (Future<T>) future;
return FutureContext.getCompletableFuture();
}
/**
@ -270,8 +243,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() {

View File

@ -52,6 +52,10 @@ public class RpcInvocation implements Invocation, Serializable {
private transient Invoker<?> invoker;
private transient Class<?> returnType;
private transient InvokeMode invokeMode;
public RpcInvocation() {
}
@ -94,6 +98,7 @@ public 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) {
@ -212,6 +217,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="

View File

@ -1,128 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import java.lang.reflect.Field;
/**
* RPC Result.
*
* @serial Don't change the class name and properties.
*/
public class RpcResult extends AbstractResult {
private static final long serialVersionUID = -6925924956850004727L;
public RpcResult() {
}
public RpcResult(Object result) {
this.result = result;
}
public RpcResult(Throwable exception) {
this.exception = handleStackTraceNull(exception);
}
@Override
public Object recreate() throws Throwable {
if (exception != null) {
throw exception;
}
return result;
}
/**
* @see org.apache.dubbo.rpc.RpcResult#getValue()
* @deprecated Replace to getValue()
*/
@Override
@Deprecated
public Object getResult() {
return getValue();
}
/**
* @see org.apache.dubbo.rpc.RpcResult#setValue(Object)
* @deprecated Replace to setValue()
*/
@Deprecated
public void setResult(Object result) {
setValue(result);
}
@Override
public Object getValue() {
return result;
}
public void setValue(Object value) {
this.result = value;
}
@Override
public Throwable getException() {
return exception;
}
public void setException(Throwable e) {
this.exception = handleStackTraceNull(e);
}
@Override
public boolean hasException() {
return exception != null;
}
@Override
public String toString() {
return "RpcResult [result=" + result + ", exception=" + exception + "]";
}
/**
* we need to deal the exception whose stack trace is null.
* <p>
* see https://github.com/apache/incubator-dubbo/pull/2956
* and https://github.com/apache/incubator-dubbo/pull/3634
* and https://github.com/apache/incubator-dubbo/issues/619
*
* @param e exception
* @return exception after deal with stack trace
*/
private Throwable handleStackTraceNull(Throwable e) {
if (e != null) {
try {
// get Throwable class
Class clazz = e.getClass();
while (!clazz.getName().equals(Throwable.class.getName())) {
clazz = clazz.getSuperclass();
}
// get stackTrace value
Field stackTraceField = clazz.getDeclaredField("stackTrace");
stackTraceField.setAccessible(true);
Object stackTrace = stackTraceField.get(e);
if (stackTrace == null) {
e.setStackTrace(new StackTraceElement[0]);
}
} catch (Throwable t) {
// ignore
}
}
return e;
}
}

View File

@ -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;
}
}

View File

@ -18,9 +18,11 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcStatus;
@ -42,50 +44,75 @@ import static org.apache.dubbo.rpc.Constants.ACTIVES_KEY;
* @see Filter
*/
@Activate(group = CONSUMER, value = ACTIVES_KEY)
public class ActiveLimitFilter implements Filter {
public class ActiveLimitFilter extends ListenableFilter {
private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
public ActiveLimitFilter() {
super.listener = new ActiveLimitListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
if (!RpcStatus.beginCount(url, methodName, max)) {
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 0);
long start = System.currentTimeMillis();
long remain = timeout;
synchronized (count) {
synchronized (rpcStatus) {
while (!RpcStatus.beginCount(url, methodName, max)) {
try {
count.wait(remain);
rpcStatus.wait(remain);
} catch (InterruptedException e) {
// ignore
}
long elapsed = System.currentTimeMillis() - start;
remain = timeout - elapsed;
if (remain <= 0) {
throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
+ invoker.getInterface().getName() + ", method: "
+ invocation.getMethodName() + ", elapsed: " + elapsed
+ ", timeout: " + timeout + ". concurrent invokes: " + count.getActive()
+ ". max concurrent invoke limit: " + max);
throw new RpcException("Waiting concurrent invoke timeout in client-side for service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", elapsed: " + elapsed + ", timeout: " + timeout + ". concurrent invokes: " + rpcStatus.getActive() + ". max concurrent invoke limit: " + max);
}
}
}
}
boolean isSuccess = true;
long begin = System.currentTimeMillis();
try {
return invoker.invoke(invocation);
} catch (RuntimeException t) {
isSuccess = false;
throw t;
} finally {
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
invocation.setAttachment(ACTIVELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
return invoker.invoke(invocation);
}
static class ActiveLimitListener implements Listener {
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
String methodName = invocation.getMethodName();
URL url = invoker.getUrl();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
RpcStatus.endCount(url, methodName, getElapsed(invocation), true);
notifyFinish(RpcStatus.getStatus(url, methodName), max);
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
String methodName = invocation.getMethodName();
URL url = invoker.getUrl();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
RpcStatus.endCount(url, methodName, getElapsed(invocation), false);
notifyFinish(RpcStatus.getStatus(url, methodName), max);
}
private long getElapsed(Invocation invocation) {
String beginTime = invocation.getAttachment(ACTIVELIMIT_FILTER_START_TIME);
return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
}
private void notifyFinish(RpcStatus rpcStatus, int max) {
if (max > 0) {
synchronized (count) {
count.notifyAll();
synchronized (rpcStatus) {
rpcStatus.notifyAll();
}
}
}

View File

@ -20,17 +20,18 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CompatibleTypeUtils;
import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY;
/**
* CompatibleFilter make the remote method's return value compatible to invoker's version of object.
* To make return object compatible it does
@ -45,44 +46,54 @@ 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 appResponse, Invoker<?> invoker, Invocation invocation) {
if (!invocation.getMethodName().startsWith("$") && !appResponse.hasException()) {
Object value = appResponse.getValue();
if (value != null) {
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?> type = method.getReturnType();
Object newValue;
String serialization = invoker.getUrl().getParameter(SERIALIZATION_KEY);
if ("json".equals(serialization) || "fastjson".equals(serialization)) {
// If the serialization key is json or fastjson
Type gtype = method.getGenericReturnType();
newValue = PojoUtils.realize(value, type, gtype);
} else if (!type.isInstance(value)) {
//if local service interface's method's return type is not instance of return value
newValue = PojoUtils.isPojo(type) ? PojoUtils.realize(value, type) : CompatibleTypeUtils.compatibleTypeConvert(value, type);
} else {
newValue = value;
}
if (newValue != value) {
appResponse.setValue(newValue);
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
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) {
}
}
}

View File

@ -16,17 +16,18 @@
*/
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
/**
* ConsumerContextFilter set current RpcContext with invoker,invocation, local host, remote host and port
* for consumer invoker.It does it to make the requires info available to execution thread's RpcContext.
@ -34,8 +35,12 @@ import org.apache.dubbo.rpc.RpcInvocation;
* @see org.apache.dubbo.rpc.Filter
* @see RpcContext
*/
@Activate(group = CommonConstants.CONSUMER, order = -10000)
public class ConsumerContextFilter implements Filter {
@Activate(group = CONSUMER, order = -10000)
public class ConsumerContextFilter extends ListenableFilter {
public ConsumerContextFilter() {
super.listener = new ConsumerContextListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@ -49,18 +54,22 @@ public 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 appResponse, Invoker<?> invoker, Invocation invocation) {
RpcContext.getServerContext().setAttachments(appResponse.getAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -17,9 +17,9 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
@ -48,9 +48,13 @@ import static org.apache.dubbo.rpc.Constants.TOKEN_KEY;
* @see RpcContext
*/
@Activate(group = PROVIDER, order = -10000)
public class ContextFilter implements Filter {
public class ContextFilter extends ListenableFilter {
private static final String TAG_KEY = "dubbo.tag";
public ContextFilter() {
super.listener = new ContextListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Map<String, String> attachments = invocation.getAttachments();
@ -97,10 +101,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 appResponse, Invoker<?> invoker, Invocation invocation) {
// pass attachments to result
appResponse.addAttachments(RpcContext.getServerContext().getAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -18,12 +18,12 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import static org.apache.dubbo.rpc.Constants.$ECHO;
@ -36,7 +36,7 @@ public class EchoFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
if (inv.getMethodName().equals($ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
return new RpcResult(inv.getArguments()[0]);
return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
}
return invoker.invoke(inv);
}

View File

@ -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 = CommonConstants.PROVIDER)
public class ExceptionFilter implements Filter {
private final Logger logger;
public class ExceptionFilter extends ListenableFilter {
public ExceptionFilter() {
this(LoggerFactory.getLogger(ExceptionFilter.class));
}
public ExceptionFilter(Logger logger) {
this.logger = logger;
super.listener = new ExceptionListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
try {
return invoker.invoke(invocation);
} catch (RuntimeException e) {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
throw e;
}
return invoker.invoke(invocation);
}
@Override
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
if (result.hasException() && GenericService.class != invoker.getInterface()) {
try {
Throwable exception = result.getException();
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 appResponse, Invoker<?> invoker, Invocation invocation) {
if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
for (Class<?> exceptionClass : exceptionClassses) {
if (exception.getClass().equals(exceptionClass)) {
return result;
}
Throwable exception = appResponse.getException();
// directly throw if it's checked exception
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
return;
}
// directly throw if the exception appears in the signature
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
for (Class<?> exceptionClass : exceptionClassses) {
if (exception.getClass().equals(exceptionClass)) {
return;
}
}
} catch (NoSuchMethodException e) {
return;
}
} catch (NoSuchMethodException e) {
return result;
}
// for the exception not found in method's signature, print ERROR message in server's log.
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
// for the exception not found in method's signature, print ERROR message in server's log.
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
// directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
return result;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return result;
}
// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
return result;
}
// directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
return;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return;
}
// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
return;
}
// otherwise, wrap with RuntimeException and throw back to the client
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
} catch (Throwable e) {
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
return result;
// otherwise, wrap with RuntimeException and throw back to the client
appResponse.setException(new RuntimeException(StringUtils.toString(exception)));
return;
} catch (Throwable e) {
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
return;
}
}
}
return result;
}
@Override
public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
}
// For test purpose
public void setLogger(Logger logger) {
this.logger = logger;
}
}
}

View File

@ -19,9 +19,10 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcStatus;
@ -30,12 +31,20 @@ import static org.apache.dubbo.rpc.Constants.EXECUTES_KEY;
/**
*
* 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 = CommonConstants.PROVIDER, value = EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter {
public class ExecuteLimitFilter extends ListenableFilter {
private static final String EXECUTELIMIT_FILTER_START_TIME = "execugtelimit_filter_start_time";
public ExecuteLimitFilter() {
super.listener = new ExecuteLimitListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@ -48,20 +57,32 @@ public 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 appResponse, Invoker<?> invoker, Invocation invocation) {
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), true);
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), false);
}
private long getElapsed(Invocation invocation) {
String beginTime = invocation.getAttachment(EXECUTELIMIT_FILTER_START_TIME);
return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
}
}
}

View File

@ -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;
@ -44,20 +43,25 @@ import java.io.IOException;
import java.lang.reflect.Method;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_NATIVE_JAVA;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_BEAN;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_PROTOBUF;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC;
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_BEAN;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_NATIVE_JAVA;
import static org.apache.dubbo.rpc.Constants.GENERIC_SERIALIZATION_PROTOBUF;
/**
* GenericInvokerFilter.
*/
@Activate(group = CommonConstants.PROVIDER, order = -20000)
public class GenericFilter implements Filter {
public class GenericFilter extends ListenableFilter {
public GenericFilter() {
super.listener = new GenericListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
if (inv.getMethodName().equals($INVOKE)
if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
@ -134,41 +138,7 @@ public class GenericFilter implements Filter {
args[0].getClass().getName());
}
}
Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
if (result.hasException()
&& !(result.getException() instanceof GenericException)) {
return new RpcResult(new GenericException(result.getException()));
}
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class)
.getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA)
.serialize(null, os).writeObject(result.getValue());
return new RpcResult(os.toByteArray());
} catch (IOException e) {
throw new RpcException(
"Generic serialization [" +
GENERIC_SERIALIZATION_NATIVE_JAVA +
"] serialize result failed.", e);
}
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
} else if (ProtocolUtils.isProtobufGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class)
.getExtension(GENERIC_SERIALIZATION_PROTOBUF)
.serialize(null, os).writeObject(result.getValue());
return new RpcResult(os.toString());
} catch (IOException e) {
throw new RpcException("Generic serialization [" +
GENERIC_SERIALIZATION_PROTOBUF +
"] serialize result failed.", e);
}
} 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) {
@ -178,4 +148,57 @@ public class GenericFilter implements Filter {
return invoker.invoke(inv);
}
static class GenericListener implements Listener {
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation inv) {
if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
String generic = inv.getAttachment(GENERIC_KEY);
if (StringUtils.isBlank(generic)) {
generic = RpcContext.getContext().getAttachment(GENERIC_KEY);
}
if (appResponse.hasException() && !(appResponse.getException() instanceof GenericException)) {
appResponse.setException(new GenericException(appResponse.getException()));
}
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(appResponse.getValue());
appResponse.setValue(os.toByteArray());
} catch (IOException e) {
throw new RpcException(
"Generic serialization [" +
GENERIC_SERIALIZATION_NATIVE_JAVA +
"] serialize result failed.", e);
}
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
appResponse.setValue(JavaBeanSerializeUtil.serialize(appResponse.getValue(), JavaBeanAccessor.METHOD));
} else if (ProtocolUtils.isProtobufGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class)
.getExtension(GENERIC_SERIALIZATION_PROTOBUF)
.serialize(null, os).writeObject(appResponse.getValue());
appResponse.setValue(os.toString());
} catch (IOException e) {
throw new RpcException("Generic serialization [" +
GENERIC_SERIALIZATION_PROTOBUF +
"] serialize result failed.", e);
}
} else {
appResponse.setValue(PojoUtils.generalize(appResponse.getValue()));
}
}
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -25,40 +25,46 @@ 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;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC;
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
/**
* GenericImplInvokerFilter
*/
@Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000)
public class GenericImplFilter implements Filter {
public class GenericImplFilter extends ListenableFilter {
private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class};
public GenericImplFilter() {
super.listener = new GenericImplListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String generic = invoker.getUrl().getParameter(GENERIC_KEY);
if (ProtocolUtils.isGeneric(generic)
&& !$INVOKE.equals(invocation.getMethodName())
&& (!$INVOKE.equals(invocation.getMethodName()) && !$INVOKE_ASYNC.equals(invocation.getMethodName()))
&& invocation instanceof RpcInvocation) {
RpcInvocation invocation2 = (RpcInvocation) invocation;
RpcInvocation invocation2 = new RpcInvocation(invocation);
String methodName = invocation2.getMethodName();
Class<?>[] parameterTypes = invocation2.getParameterTypes();
Object[] arguments = invocation2.getArguments();
@ -78,77 +84,15 @@ public class GenericImplFilter implements Filter {
args = PojoUtils.generalize(arguments);
}
invocation2.setMethodName($INVOKE);
if (RpcUtils.isReturnTypeFuture(invocation)) {
invocation2.setMethodName($INVOKE_ASYNC);
} else {
invocation2.setMethodName($INVOKE);
}
invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
invocation2.setArguments(new Object[]{methodName, types, args});
Result result = invoker.invoke(invocation2);
if (!result.hasException()) {
Object value = result.getValue();
try {
Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
if (ProtocolUtils.isBeanGenericSerialization(generic)) {
if (value == null) {
return new RpcResult(value);
} else if (value instanceof JavaBeanDescriptor) {
return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
} else {
throw new RpcException(
"The type of result value is " +
value.getClass().getName() +
" other than " +
JavaBeanDescriptor.class.getName() +
", and the result is " +
value);
}
} else {
return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));
}
} catch (NoSuchMethodException e) {
throw new RpcException(e.getMessage(), e);
}
} else if (result.getException() instanceof GenericException) {
GenericException exception = (GenericException) result.getException();
try {
String className = exception.getExceptionClass();
Class<?> clazz = ReflectUtils.forName(className);
Throwable targetException = null;
Throwable lastException = null;
try {
targetException = (Throwable) clazz.newInstance();
} catch (Throwable e) {
lastException = e;
for (Constructor<?> constructor : clazz.getConstructors()) {
try {
targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
break;
} catch (Throwable e1) {
lastException = e1;
}
}
}
if (targetException != null) {
try {
Field field = Throwable.class.getDeclaredField("detailMessage");
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(targetException, exception.getExceptionMessage());
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
result = new RpcResult(targetException);
} else if (lastException != null) {
throw lastException;
}
} catch (Throwable e) {
throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
}
}
return result;
}
if (invocation.getMethodName().equals($INVOKE)
return invoker.invoke(invocation2);
} else if ((invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC))
&& invocation.getArguments() != null
&& invocation.getArguments().length == 3
&& ProtocolUtils.isGeneric(generic)) {
@ -169,19 +113,89 @@ public class GenericImplFilter implements Filter {
}
}
((RpcInvocation) invocation).setAttachment(GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY));
invocation.setAttachment(
GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY));
}
return invoker.invoke(invocation);
}
private void error(String generic, String expected, String actual) throws RpcException {
throw new RpcException(
"Generic serialization [" +
generic +
"] only support message type " +
expected +
" and your message type is " +
actual);
throw new RpcException("Generic serialization [" + generic + "] only support message type " + expected + " and your message type is " + actual);
}
static class GenericImplListener implements Listener {
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
String generic = invoker.getUrl().getParameter(GENERIC_KEY);
String methodName = invocation.getMethodName();
Class<?>[] parameterTypes = invocation.getParameterTypes();
if (ProtocolUtils.isGeneric(generic)
&& (!$INVOKE.equals(invocation.getMethodName()) && !$INVOKE_ASYNC.equals(invocation.getMethodName()))
&& invocation instanceof RpcInvocation) {
if (!appResponse.hasException()) {
Object value = appResponse.getValue();
try {
Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
if (ProtocolUtils.isBeanGenericSerialization(generic)) {
if (value == null) {
appResponse.setValue(value);
} else if (value instanceof JavaBeanDescriptor) {
appResponse.setValue(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
} else {
throw new RpcException("The type of result value is " + value.getClass().getName() + " other than " + JavaBeanDescriptor.class.getName() + ", and the result is " + value);
}
} else {
Type[] types = ReflectUtils.getReturnTypes(method);
appResponse.setValue(PojoUtils.realize(value, (Class<?>) types[0], types[1]));
}
} catch (NoSuchMethodException e) {
throw new RpcException(e.getMessage(), e);
}
} else if (appResponse.getException() instanceof GenericException) {
GenericException exception = (GenericException) appResponse.getException();
try {
String className = exception.getExceptionClass();
Class<?> clazz = ReflectUtils.forName(className);
Throwable targetException = null;
Throwable lastException = null;
try {
targetException = (Throwable) clazz.newInstance();
} catch (Throwable e) {
lastException = e;
for (Constructor<?> constructor : clazz.getConstructors()) {
try {
targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
break;
} catch (Throwable e1) {
lastException = e1;
}
}
}
if (targetException != null) {
try {
Field field = Throwable.class.getDeclaredField("detailMessage");
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(targetException, exception.getExceptionMessage());
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
appResponse.setException(targetException);
} else if (lastException != null) {
throw lastException;
}
} catch (Throwable e) {
throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
}
}
}
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -20,12 +20,11 @@ import org.apache.dubbo.common.constants.CommonConstants;
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 = CommonConstants.PROVIDER)
public class TimeoutFilter implements Filter {
public class TimeoutFilter extends ListenableFilter {
private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
private static final String TIMEOUT_FILTER_START_TIME = "timeout_filter_start_time";
@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 appResponse, Invoker<?> invoker, Invocation invocation) {
String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
if (startAttach != null) {
long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
if (invoker.getUrl() != null && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE)) {
if (logger.isWarnEnabled()) {
logger.warn("invoke time out. method: " + invocation.getMethodName() + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is " + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
}
}
}
}
return result;
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -23,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;
@ -38,8 +38,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
/**
* AbstractInvoker.
*/
@ -149,9 +147,8 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
*/
invocation.addAttachments(contextAttachments);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), ASYNC_KEY, false)) {
invocation.setAttachment(ASYNC_KEY, Boolean.TRUE.toString());
}
invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
@ -159,21 +156,21 @@ public 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);
}
}

View File

@ -24,6 +24,7 @@ import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.support.ProtocolUtils;
import java.util.ArrayList;
@ -84,4 +85,11 @@ public abstract class AbstractProtocol implements Protocol {
}
}
}
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
}
protected abstract <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException;
}

View File

@ -95,13 +95,14 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
}
@Override
public <T> Invoker<T> refer(final Class<T> type, final URL url) throws RpcException {
protected <T> Invoker<T> protocolBindingRefer(final Class<T> type, final URL url) throws RpcException {
final Invoker<T> target = proxyFactory.getInvoker(doRefer(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) {

View File

@ -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;
}
}

View File

@ -19,11 +19,11 @@ package org.apache.dubbo.rpc.protocol;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
@ -75,14 +75,31 @@ public 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);
}
} else {
filter.onResponse(r, invoker, invocation);
}
return r;
});
}
@Override

View File

@ -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,48 @@ 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);
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);
future.whenComplete((obj, t) -> {
AppResponse result = new AppResponse();
if (t != null) {
if (t instanceof CompletionException) {
result.setException(t.getCause());
} else {
result.setException(t);
}
} else {
result.setValue(obj);
}
asyncRpcResult.complete(result);
});
return asyncRpcResult;
} catch (InvocationTargetException e) {
// TODO async throw exception before async thread write back, should stop asyncContext
if (rpcContext.isAsyncStarted() && !rpcContext.stopAsync()) {
if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
}
return new RpcResult(e.getTargetException());
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
private CompletableFuture<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

View File

@ -20,14 +20,10 @@ 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;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
import static org.apache.dubbo.rpc.Constants.FUTURE_RETURNTYPE_KEY;
/**
* InvokerHandler
*/
@ -56,16 +52,6 @@ public 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(FUTURE_RETURNTYPE_KEY, "true");
invocation.setAttachment(ASYNC_KEY, "true");
}
return invocation;
}
}

View File

@ -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);
}
}

View File

@ -18,18 +18,18 @@ package org.apache.dubbo.rpc.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import com.alibaba.fastjson.JSON;
@ -112,7 +112,7 @@ 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);

View File

@ -38,7 +38,7 @@ final public class MockProtocol extends AbstractProtocol {
}
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
public <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException {
return new MockInvoker<>(url, type);
}
}

View File

@ -22,23 +22,22 @@ 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;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE;
import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY;
import static org.apache.dubbo.rpc.Constants.ID_KEY;
import static org.apache.dubbo.common.constants.RpcConstants.$INVOKE_ASYNC;
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY;
import static org.apache.dubbo.rpc.Constants.FUTURE_GENERATED_KEY;
import static org.apache.dubbo.rpc.Constants.FUTURE_RETURNTYPE_KEY;
import static org.apache.dubbo.rpc.Constants.ID_KEY;
import static org.apache.dubbo.rpc.Constants.RETURN_KEY;
/**
* RpcUtils
@ -71,7 +70,6 @@ public 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
@ -86,24 +84,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) {
@ -190,11 +171,22 @@ public class RpcUtils {
}
public static boolean isReturnTypeFuture(Invocation inv) {
return Boolean.TRUE.toString().equals(inv.getAttachment(FUTURE_RETURNTYPE_KEY));
Class<?> clazz = getReturnType(inv);
return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv);
}
public static 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 $INVOKE_ASYNC.equals(inv.getMethodName());
}
public static boolean isOneway(URL url, Invocation inv) {

View File

@ -20,13 +20,13 @@ package org.apache.dubbo.rpc;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class RpcResultTest {
public class AppResponseTest {
@Test
public void testRpcResultWithNormalException() {
public void testAppResponseWithNormalException() {
NullPointerException npe = new NullPointerException();
RpcResult rpcResult = new RpcResult(npe);
AppResponse appResponse = new AppResponse(npe);
StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace();
StackTraceElement[] stackTrace = appResponse.getException().getStackTrace();
Assertions.assertNotNull(stackTrace);
Assertions.assertTrue(stackTrace.length > 1);
}
@ -35,14 +35,14 @@ public class RpcResultTest {
* please run this test in Run mode
*/
@Test
public void testRpcResultWithEmptyStackTraceException() {
public void testAppResponseWithEmptyStackTraceException() {
Throwable throwable = buildEmptyStackTraceException();
if (throwable == null) {
return;
}
RpcResult rpcResult = new RpcResult(throwable);
AppResponse appResponse = new AppResponse(throwable);
StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace();
StackTraceElement[] stackTrace = appResponse.getException().getStackTrace();
Assertions.assertNotNull(stackTrace);
Assertions.assertTrue(stackTrace.length == 0);
}
@ -50,10 +50,10 @@ public class RpcResultTest {
@Test
public void testSetExceptionWithNormalException() {
NullPointerException npe = new NullPointerException();
RpcResult rpcResult = new RpcResult();
rpcResult.setException(npe);
AppResponse appResponse = new AppResponse();
appResponse.setException(npe);
StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace();
StackTraceElement[] stackTrace = appResponse.getException().getStackTrace();
Assertions.assertNotNull(stackTrace);
Assertions.assertTrue(stackTrace.length > 1);
}
@ -67,10 +67,10 @@ public class RpcResultTest {
if (throwable == null) {
return;
}
RpcResult rpcResult = new RpcResult();
rpcResult.setException(throwable);
AppResponse appResponse = new AppResponse();
appResponse.setException(throwable);
StackTraceElement[] stackTrace = rpcResult.getException().getStackTrace();
StackTraceElement[] stackTrace = appResponse.getException().getStackTrace();
Assertions.assertNotNull(stackTrace);
Assertions.assertTrue(stackTrace.length == 0);
}

View File

@ -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");
}

View File

@ -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);
AppResponse appResponse = (AppResponse) asyncResult.get();
compatibleFilter.listener().onResponse(appResponse, invoker, invocation);
assertEquals(Type.High, appResponse.getValue());
}
@Test
public void testInvokerNonJsonEnumSerialization() {
public void testInvokerNonJsonEnumSerialization() throws Exception {
invocation = mock(Invocation.class);
given(invocation.getMethodName()).willReturn("enumlength");
given(invocation.getParameterTypes()).willReturn(new Class<?>[]{Type[].class});
@ -117,14 +120,16 @@ public 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);
AppResponse appResponse = (AppResponse) asyncResult.get();
compatibleFilter.listener().onResponse(appResponse, invoker, invocation);
assertEquals(Type.High, appResponse.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");

View File

@ -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;
});
}
}

View File

@ -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");

View File

@ -17,11 +17,11 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.support.DemoService;
import org.junit.jupiter.api.Test;
@ -46,7 +46,7 @@ public class EchoFilterTest {
Invoker<DemoService> invoker = mock(Invoker.class);
given(invoker.isAvailable()).willReturn(true);
given(invoker.getInterface()).willReturn(DemoService.class);
RpcResult result = new RpcResult();
AppResponse result = new AppResponse();
result.setValue("High");
given(invoker.invoke(invocation)).willReturn(result);
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@ -68,7 +68,7 @@ public class EchoFilterTest {
Invoker<DemoService> 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");

View File

@ -17,13 +17,13 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.support.DemoService;
import org.apache.dubbo.rpc.support.LocalException;
@ -51,18 +51,20 @@ public class ExceptionFilterTest {
RpcContext.getContext().setRemoteAddress("127.0.0.1", 1234);
RpcException exception = new RpcException("TestRpcException");
ExceptionFilter exceptionFilter = new ExceptionFilter(logger);
ExceptionFilter exceptionFilter = new ExceptionFilter();
RpcInvocation invocation = new RpcInvocation("sayHello", new Class<?>[]{String.class}, new Object[]{"world"});
Invoker<DemoService> invoker = mock(Invoker.class);
given(invoker.getInterface()).willReturn(DemoService.class);
given(invoker.invoke(eq(invocation))).willThrow(exception);
try {
exceptionFilter.invoke(invoker, invocation);
} catch (RpcException e) {
assertEquals("TestRpcException", e.getMessage());
((ExceptionFilter.ExceptionListener) exceptionFilter.listener()).setLogger(logger);
exceptionFilter.listener().onError(e, invoker, invocation);
}
Mockito.verify(logger).error(eq("Got unchecked and undeclared exception which called by 127.0.0.1. service: "
+ DemoService.class.getName() + ", method: sayHello, exception: "
+ RpcException.class.getName() + ": TestRpcException"), eq(exception));
@ -76,16 +78,16 @@ public class ExceptionFilterTest {
ExceptionFilter exceptionFilter = new ExceptionFilter();
RpcInvocation invocation = new RpcInvocation("sayHello", new Class<?>[]{String.class}, new Object[]{"world"});
RpcResult rpcResult = new RpcResult();
rpcResult.setException(new IllegalArgumentException("java"));
AppResponse appResponse = new AppResponse();
appResponse.setException(new IllegalArgumentException("java"));
Invoker<DemoService> invoker = mock(Invoker.class);
when(invoker.invoke(invocation)).thenReturn(rpcResult);
when(invoker.invoke(invocation)).thenReturn(appResponse);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result newResult = exceptionFilter.invoke(invoker, invocation);
Assertions.assertEquals(rpcResult.getException(), newResult.getException());
Assertions.assertEquals(appResponse.getException(), newResult.getException());
}
@ -96,42 +98,43 @@ public class ExceptionFilterTest {
ExceptionFilter exceptionFilter = new ExceptionFilter();
RpcInvocation invocation = new RpcInvocation("sayHello", new Class<?>[]{String.class}, new Object[]{"world"});
RpcResult rpcResult = new RpcResult();
rpcResult.setException(new LocalException("localException"));
AppResponse appResponse = new AppResponse();
appResponse.setException(new LocalException("localException"));
Invoker<DemoService> invoker = mock(Invoker.class);
when(invoker.invoke(invocation)).thenReturn(rpcResult);
when(invoker.invoke(invocation)).thenReturn(appResponse);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result newResult = exceptionFilter.invoke(invoker, invocation);
Assertions.assertEquals(rpcResult.getException(), newResult.getException());
Assertions.assertEquals(appResponse.getException(), newResult.getException());
}
@SuppressWarnings("unchecked")
@Test
public void testConvertToRunTimeException() {
public void testConvertToRunTimeException() throws Exception {
ExceptionFilter exceptionFilter = new ExceptionFilter();
RpcInvocation invocation = new RpcInvocation("sayHello", new Class<?>[]{String.class}, new Object[]{"world"});
RpcResult rpcResult = new RpcResult();
rpcResult.setException(new HessianException("hessian"));
AppResponse mockRpcResult = new AppResponse();
mockRpcResult.setException(new HessianException("hessian"));
Result mockAsyncResult = AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation);
Invoker<DemoService> invoker = mock(Invoker.class);
when(invoker.invoke(invocation)).thenReturn(rpcResult);
when(invoker.invoke(invocation)).thenReturn(mockAsyncResult);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result newResult = exceptionFilter.invoke(invoker, invocation);
Result asyncResult = exceptionFilter.invoke(invoker, invocation);
newResult = exceptionFilter.onResponse(newResult, invoker, invocation);
AppResponse appResponse = (AppResponse) asyncResult.get();
exceptionFilter.listener().onResponse(appResponse, invoker, invocation);
Assertions.assertFalse(newResult.getException() instanceof HessianException);
Assertions.assertEquals(newResult.getException().getClass(), RuntimeException.class);
Assertions.assertEquals(newResult.getException().getMessage(), StringUtils.toString(rpcResult.getException()));
Assertions.assertFalse(appResponse.getException() instanceof HessianException);
Assertions.assertEquals(appResponse.getException().getClass(), RuntimeException.class);
}
}

View File

@ -17,11 +17,11 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.RpcStatus;
import org.apache.dubbo.rpc.support.BlockMyInvoker;
@ -43,7 +43,7 @@ public class ExecuteLimitFilterTest {
@Test
public void testNoExecuteLimitInvoke() throws Exception {
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1"));
Invocation invocation = Mockito.mock(Invocation.class);
@ -56,7 +56,7 @@ public class ExecuteLimitFilterTest {
@Test
public void testExecuteLimitInvoke() throws Exception {
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&executes=10"));
Invocation invocation = Mockito.mock(Invocation.class);
@ -82,6 +82,7 @@ public class ExecuteLimitFilterTest {
executeLimitFilter.invoke(invoker, invocation);
} catch (Exception e) {
Assertions.assertTrue(e instanceof RpcException);
executeLimitFilter.listener().onError(e, invoker, invocation);
}
Assertions.assertEquals(1, RpcStatus.getStatus(url, invocation.getMethodName()).getFailed());
}

View File

@ -17,7 +17,13 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.service.GenericService;
import org.apache.dubbo.rpc.support.DemoService;
import org.apache.dubbo.rpc.support.Person;
@ -54,14 +60,16 @@ public class GenericFilterTest {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
"accesslog=true&group=dubbo&version=1.1");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
when(invoker.invoke(any(Invocation.class))).thenReturn(AsyncRpcResult.newDefaultAsyncResult(new Person("person", 10), invocation));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result result = genericFilter.invoke(invoker, invocation);
Result asyncResult = genericFilter.invoke(invoker, invocation);
Assertions.assertEquals(HashMap.class, result.getValue().getClass());
Assertions.assertEquals(10, ((HashMap) result.getValue()).get("age"));
AppResponse appResponse = (AppResponse) asyncResult.get();
genericFilter.listener().onResponse(appResponse, invoker, invocation);
Assertions.assertEquals(HashMap.class, appResponse.getValue().getClass());
Assertions.assertEquals(10, ((HashMap) appResponse.getValue()).get("age"));
}
@ -81,7 +89,7 @@ public class GenericFilterTest {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
"accesslog=true&group=dubbo&version=1.1");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);
@ -104,7 +112,7 @@ public class GenericFilterTest {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
"accesslog=true&group=dubbo&version=1.1");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);
@ -128,7 +136,7 @@ public class GenericFilterTest {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
"accesslog=true&group=dubbo&version=1.1");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);

View File

@ -17,11 +17,12 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;
import org.apache.dubbo.rpc.support.DemoService;
@ -60,11 +61,14 @@ public class GenericImplFilterTest {
person.put("name", "dubbo");
person.put("age", 10);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(person));
AppResponse mockRpcResult = new AppResponse(person);
when(invoker.invoke(any(Invocation.class))).thenReturn(AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result result = genericImplFilter.invoke(invoker, invocation);
Result asyncResult = genericImplFilter.invoke(invoker, invocation);
Result result = asyncResult.get();
genericImplFilter.listener().onResponse(result, invoker, invocation);
Assertions.assertEquals(Person.class, result.getValue().getClass());
Assertions.assertEquals(10, ((Person) result.getValue()).getAge());
@ -80,12 +84,14 @@ public class GenericImplFilterTest {
"accesslog=true&group=dubbo&version=1.1&generic=true");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(
new RpcResult(new GenericException(new RuntimeException("failed"))));
AppResponse mockRpcResult = new AppResponse(new GenericException(new RuntimeException("failed")));
when(invoker.invoke(any(Invocation.class))).thenReturn(AsyncRpcResult.newDefaultAsyncResult(mockRpcResult, invocation));
when(invoker.getUrl()).thenReturn(url);
when(invoker.getInterface()).thenReturn(DemoService.class);
Result result = genericImplFilter.invoke(invoker, invocation);
Result asyncResult = genericImplFilter.invoke(invoker, invocation);
Result result = asyncResult.get();
genericImplFilter.listener().onResponse(result, invoker, invocation);
Assertions.assertEquals(RuntimeException.class, result.getException().getClass());
}
@ -105,7 +111,7 @@ public class GenericImplFilterTest {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
"accesslog=true&group=dubbo&version=1.1&generic=true");
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult(new Person("person", 10)));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse(new Person("person", 10)));
when(invoker.getUrl()).thenReturn(url);
genericImplFilter.invoke(invoker, invocation);

View File

@ -17,10 +17,10 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.support.BlockMyInvoker;
import org.junit.jupiter.api.Assertions;
@ -39,7 +39,7 @@ public class TimeoutFilterTest {
int timeout = 3000;
Invoker invoker = Mockito.mock(Invoker.class);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
when(invoker.getUrl()).thenReturn(URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&timeout=" + timeout));
Invocation invocation = Mockito.mock(Invocation.class);
@ -60,7 +60,7 @@ public class TimeoutFilterTest {
when(invocation.getMethodName()).thenReturn("testInvokeWithTimeout");
Result result = timeoutFilter.invoke(invoker, invocation);
Assertions.assertEquals("alibaba", result.getValue());
Assertions.assertEquals("Dubbo", result.getValue());
}
}

View File

@ -17,11 +17,11 @@
package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -46,7 +46,7 @@ public class TokenFilterTest {
Invoker invoker = Mockito.mock(Invoker.class);
URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
when(invoker.getUrl()).thenReturn(url);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
Map<String, String> attachments = new HashMap<String, String>();
attachments.put(TOKEN_KEY, token);
@ -65,7 +65,7 @@ public class TokenFilterTest {
Invoker invoker = Mockito.mock(Invoker.class);
URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
when(invoker.getUrl()).thenReturn(url);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
Map<String, String> attachments = new HashMap<String, String>();
attachments.put(TOKEN_KEY, "wrongToken");
@ -84,7 +84,7 @@ public class TokenFilterTest {
Invoker invoker = Mockito.mock(Invoker.class);
URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&token=" + token);
when(invoker.getUrl()).thenReturn(url);
when(invoker.invoke(any(Invocation.class))).thenReturn(new RpcResult("result"));
when(invoker.invoke(any(Invocation.class))).thenReturn(new AppResponse("result"));
Invocation invocation = Mockito.mock(Invocation.class);

View File

@ -17,10 +17,11 @@
package org.apache.dubbo.rpc.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
public class BlockMyInvoker<T> extends MyInvoker<T> {
@ -38,19 +39,18 @@ public class BlockMyInvoker<T> extends MyInvoker<T> {
@Override
public Result invoke(Invocation invocation) throws RpcException {
RpcResult result = new RpcResult();
AppResponse result = new AppResponse();
if (hasException == false) {
try {
Thread.sleep(blockTime);
} catch (InterruptedException e) {
}
result.setValue("alibaba");
return result;
result.setValue("Dubbo");
} else {
result.setException(new RuntimeException("mocked exception"));
return result;
}
return AsyncRpcResult.newDefaultAsyncResult(result, invocation);
}
public long getBlockTime() {

Some files were not shown because too many files have changed in this diff Show More