Async optimization (#3738)

* Result implement CF

* Result implement CF

* Result implement CF

* Add AsyncRpcResult

* Fix bugs and refactor Filter

* Try to add onSend onError for Filter

* invoke different filter method according to result status.

*  make generic work with async call, including add $invokeAsync

* refactor legacy Filter implementation to work with onResponse.

* demo changes

* Fixes #3620, provider attachment lose on consumer side, fix this by reverting RpcContext copy

* AsyncRpcResult should always holds an Invocation instance

* refactor filter signature

* reimplement embedded Filters

* use ProviderModel modification in 3.x

* Fix address notification processing workflow after merging 3.x branch

* Fix UT

* Fix UT

* Unit test of JValidator; Clean code of JValidator (#3723)

* Fixes #3625 (#3730)

use constant to replace magic number

* Fix conflict when merging master and 3.x

* Fix conflict when merging master and 3.x

* Result interface itself has Future status.

* Fix DefaultFuture UT

* Wrap all protocol Invoker with AsyncToSyncInvoker & Fix UT

* Add license

* fix UT

* Fix ut in MonitorFilterTest

* avoid duplicate async to sync wrapper

* return async result in CacheFilter.

* fix UT in CacheFilterTest

* Add generic condition check to GenericFilter callback.

* Fix UT

* Get generic from RpcContext if the value in Invocation is empty.

* Fix RSocketProtocol to meet AbstractProtocol adjustment

* rename RpcResult to AppResponse to help avoid confusion with AsyncRpcResult.

* RSocket module switch to AsyncRpcResult
This commit is contained in:
ken.lj 2019-04-12 10:29:30 +08:00 committed by jefflv
parent 91554bc84b
commit 003e400b6f
141 changed files with 1825 additions and 1613 deletions

View File

@ -24,11 +24,11 @@ import org.apache.dubbo.common.timer.Timeout;
import org.apache.dubbo.common.timer.Timer;
import org.apache.dubbo.common.timer.TimerTask;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
@ -99,7 +99,7 @@ public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
+ e.getMessage() + ", ", e);
addFailed(loadbalance, invocation, invokers, invoker);
return new RpcResult(); // ignore
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
}
}

View File

@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.cluster.support;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
@ -50,7 +50,7 @@ public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
return invoker.invoke(invocation);
} catch (Throwable e) {
logger.error("Failsafe ignore exception: " + e.getMessage(), e);
return new RpcResult(); // ignore
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
}
}
}

View File

@ -36,6 +36,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* NOTICE! This implementation does not work well with async call.
*
* Invoke a specific number of invokers concurrently, usually used for demanding real-time operations, but need to waste more service resources.
*
* <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a>
@ -66,7 +68,6 @@ public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T> {
} else {
selected = new ArrayList<>();
for (int i = 0; i < forks; i++) {
// TODO. Add some comment here, refer chinese version for more details.
Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
if (!selected.contains(invoker)) {
//Avoid add the same invoker several times.

View File

@ -23,12 +23,12 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.cluster.Merger;
@ -41,12 +41,13 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* NOTICE! Does not work with async call.
* @param <T>
*/
@SuppressWarnings("unchecked")
public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
@ -86,26 +87,19 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
returnType = null;
}
Map<String, Future<Result>> results = new HashMap<String, Future<Result>>();
Map<String, Result> results = new HashMap<>();
for (final Invoker<T> invoker : invokers) {
Future<Result> future = executor.submit(new Callable<Result>() {
@Override
public Result call() throws Exception {
return invoker.invoke(new RpcInvocation(invocation, invoker));
}
});
results.put(invoker.getUrl().getServiceKey(), future);
results.put(invoker.getUrl().getServiceKey(), invoker.invoke(new RpcInvocation(invocation, invoker)));
}
Object result = null;
List<Result> resultList = new ArrayList<Result>(results.size());
int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
for (Map.Entry<String, Future<Result>> entry : results.entrySet()) {
Future<Result> future = entry.getValue();
for (Map.Entry<String, Result> entry : results.entrySet()) {
Result asyncResult = entry.getValue();
try {
Result r = future.get(timeout, TimeUnit.MILLISECONDS);
Result r = asyncResult.get();
if (r.hasException()) {
log.error("Invoke " + getGroupDescFromServiceKey(entry.getKey()) +
" failed: " + r.getException().getMessage(),
@ -119,13 +113,13 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
}
if (resultList.isEmpty()) {
return new RpcResult((Object) null);
return AsyncRpcResult.newDefaultAsyncResult(invocation);
} else if (resultList.size() == 1) {
return resultList.iterator().next();
}
if (returnType == void.class) {
return new RpcResult((Object) null);
return AsyncRpcResult.newDefaultAsyncResult(invocation);
}
if (merger.startsWith(".")) {
@ -173,7 +167,7 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
throw new RpcException("There is no merger to merge result.");
}
}
return new RpcResult(result);
return AsyncRpcResult.newDefaultAsyncResult(result, invocation);
}

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.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.support.MockInvoker;
@ -113,7 +113,7 @@ public class MockClusterInvoker<T> implements Invoker<T> {
result = minvoker.invoke(invocation);
} catch (RpcException me) {
if (me.isBiz()) {
result = new RpcResult(me.getCause());
result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation);
} else {
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
}

View File

@ -20,12 +20,12 @@ package org.apache.dubbo.rpc.cluster;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
import org.junit.jupiter.api.Assertions;
@ -48,7 +48,7 @@ public class StickyTest {
private Invoker<StickyTest> invoker2 = mock(Invoker.class);
private RpcInvocation invocation;
private Directory<StickyTest> dic;
private Result result = new RpcResult();
private Result result = new AppResponse();
private StickyClusterInvoker<StickyTest> clusterinvoker = null;
private URL url = URL.valueOf("test://test:11/test?"
+ "&loadbalance=roundrobin"

View File

@ -51,6 +51,16 @@ public class MockDirInvocation implements Invocation {
return attachments;
}
@Override
public void setAttachment(String key, String value) {
}
@Override
public void setAttachmentIfAbsent(String key, String value) {
}
public Invoker<?> getInvoker() {
return null;
}

View File

@ -19,12 +19,12 @@ package org.apache.dubbo.rpc.cluster.router.file;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.cluster.RouterFactory;
@ -52,7 +52,7 @@ public class FileRouterEngineTest {
Invoker<FileRouterEngineTest> invoker2 = mock(Invoker.class);
Invocation invocation;
StaticDirectory<FileRouterEngineTest> dic;
Result result = new RpcResult();
Result result = new AppResponse();
private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
@BeforeAll

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

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

View File

@ -91,6 +91,8 @@ public class Constants {
public static final String $INVOKE = "$invoke";
public static final String $INVOKE_ASYNC = "$invokeAsync";
public static final String $ECHO = "$echo";
public static final int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32);
@ -827,16 +829,16 @@ public class Constants {
*/
public static final String DEVELOPMENT_ENVIRONMENT = "develop";
/**
* Production environment key.
*/
public static final String PRODUCTION_ENVIRONMENT = "product";
/**
* Consumer side 's proxy class
*/
public static final String PROXY_CLASS_REF = "refClass";
/**
* Production environment key.
*/
public static final String PRODUCTION_ENVIRONMENT = "product";
public static final String ETCD3_NOTIFY_MAXTHREADS_KEYS = "etcd3.notify.maxthreads";
public static final int DEFAULT_ETCD3_NOTIFY_THREADS = DEFAULT_IO_THREADS;

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

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.common.config;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

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

View File

@ -17,11 +17,42 @@
package com.alibaba.dubbo.rpc;
import org.apache.dubbo.rpc.AppResponse;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@Deprecated
public interface Result extends org.apache.dubbo.rpc.Result {
@Override
default void setValue(Object value) {
}
@Override
default void setException(Throwable t) {
}
@Override
default org.apache.dubbo.rpc.Result thenApplyWithContext(Function<AppResponse, AppResponse> fn) {
return this;
}
@Override
default <U> CompletableFuture<U> thenApply(Function<org.apache.dubbo.rpc.Result, ? extends U> fn) {
return null;
}
@Override
default org.apache.dubbo.rpc.Result get() throws InterruptedException, ExecutionException {
return this;
}
class CompatibleResult implements Result {
private org.apache.dubbo.rpc.Result delegate;
@ -53,11 +84,6 @@ public interface Result extends org.apache.dubbo.rpc.Result {
return delegate.recreate();
}
@Override
public Object getResult() {
return delegate.getResult();
}
@Override
public Map<String, String> getAttachments() {
return delegate.getAttachments();

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

@ -57,6 +57,16 @@ public class MockInvocation implements Invocation {
return attachments;
}
@Override
public void setAttachment(String key, String value) {
}
@Override
public void setAttachmentIfAbsent(String key, String value) {
}
public Invoker<?> getInvoker() {
return null;
}

View File

@ -265,7 +265,7 @@ public class ReferenceConfig<T> extends AbstractReferenceConfig {
checkStubAndLocal(interfaceClass);
checkMock(interfaceClass);
ConsumerModel consumerModel = new ConsumerModel(interfaceName, group, version, interfaceClass);
ConsumerModel consumerModel = new ConsumerModel(interfaceName, group, version, getActualInterface());
ApplicationModel.initConsumerModel(URL.buildKey(interfaceName, group, version), consumerModel);
Map<String, String> map = new HashMap<String, String>();
@ -321,6 +321,17 @@ public class ReferenceConfig<T> extends AbstractReferenceConfig {
consumerModel.getServiceMetadata().addAttribute(Constants.PROXY_CLASS_REF, ref);
}
private Class<?> getActualInterface() {
Class actualInterface = interfaceClass;
if (interfaceClass == GenericService.class) {
try {
actualInterface = Class.forName(interfaceName);
} catch (ClassNotFoundException e) {
// ignore
}
}
return actualInterface;
}
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) {

View File

@ -35,6 +35,8 @@ import java.util.concurrent.CountDownLatch;
*/
public class CacheListener implements DataListener {
private static final int MIN_PATH_DEPTH = 5;
private Map<String, Set<ConfigurationListener>> keyListeners = new ConcurrentHashMap<>();
private CountDownLatch initializedLatch;
private String rootPath;
@ -89,7 +91,7 @@ public class CacheListener implements DataListener {
// TODO We limit the notification of config changes to a specific path level, for example
// /dubbo/config/service/configurators, other config changes not in this level will not get notified,
// say /dubbo/config/dubbo.properties
if (path.split("/").length >= 5) {
if (path.split("/").length >= MIN_PATH_DEPTH) {
String key = pathToKey(path);
ConfigChangeType changeType;
switch (eventType) {

View File

@ -24,6 +24,8 @@ import org.apache.dubbo.demo.DemoService;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
@Reference
@ -33,4 +35,9 @@ public class DemoServiceComponent implements DemoService {
public String sayHello(String name) {
return demoService.sayHello(name);
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
}

View File

@ -25,6 +25,8 @@ import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
@Service
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@ -35,4 +37,9 @@ public class DemoServiceImpl implements DemoService {
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
}

View File

@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@ -31,4 +33,9 @@ public class DemoServiceImpl implements DemoService {
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
}

View File

@ -16,8 +16,11 @@
*/
package org.apache.dubbo.demo;
import java.util.concurrent.CompletableFuture;
public interface DemoService {
String sayHello(String name);
CompletableFuture<String> sayHelloAsync(String name);
}

View File

@ -20,16 +20,21 @@ import org.apache.dubbo.demo.DemoService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.CompletableFuture;
public class Application {
/**
* In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
* launch the application
*/
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
context.start();
DemoService demoService = context.getBean("demoService", DemoService.class);
String hello = demoService.sayHello("world");
System.out.println("result: " + hello);
// String hello = demoService.sayHello("world");
CompletableFuture<String> helloFuture = demoService.sayHelloAsync("world");
// System.out.println("result: " + hello);
System.out.println("result: " + helloFuture.get());
System.in.read();
}
}

View File

@ -27,6 +27,6 @@
<!-- generate proxy for the remote service, then demoService can be used in the same way as the
local regular interface -->
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
<dubbo:reference timeout="600000" id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
</beans>

View File

@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@ -31,4 +33,14 @@ public class DemoServiceImpl implements DemoService {
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture("future return value!");
}
}

View File

@ -22,12 +22,12 @@ import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import java.io.Serializable;
@ -95,9 +95,9 @@ public class CacheFilter implements Filter {
Object value = cache.get(key);
if (value != null) {
if (value instanceof ValueWrapper) {
return new RpcResult(((ValueWrapper)value).get());
return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation);
} else {
return new RpcResult(value);
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
}
}
Result result = invoker.invoke(invocation);

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

@ -19,12 +19,12 @@ package org.apache.dubbo.validation.filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
@ -87,7 +87,7 @@ public class ValidationFilter implements Filter {
} catch (RpcException e) {
throw e;
} catch (Throwable t) {
return new RpcResult(t);
return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
}
}
return invoker.invoke(invocation);

View File

@ -94,7 +94,7 @@ public class JValidator implements Validator {
factory = Validation.buildDefaultValidatorFactory();
}
this.validator = factory.getValidator();
this.methodClassMap = new ConcurrentHashMap<String, Class>();
this.methodClassMap = new ConcurrentHashMap<>();
}
private static boolean isPrimitives(Class<?> cls) {
@ -117,7 +117,7 @@ public class JValidator implements Validator {
String parameterClassName = generateMethodParameterClassName(clazz, method);
Class<?> parameterClass;
try {
parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
CtClass ctClass = pool.makeClass(parameterClassName);
@ -243,14 +243,14 @@ public class JValidator implements Validator {
@Override
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
List<Class<?>> groups = new ArrayList<Class<?>>();
List<Class<?>> groups = new ArrayList<>();
Class<?> methodClass = methodClass(methodName);
if (methodClass != null) {
groups.add(methodClass);
}
Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
Set<ConstraintViolation<?>> violations = new HashSet<>();
Method method = clazz.getMethod(methodName, parameterTypes);
Class<?>[] methodClasses = null;
Class<?>[] methodClasses;
if (method.isAnnotationPresent(MethodValidated.class)){
methodClasses = method.getAnnotation(MethodValidated.class).value();
groups.addAll(Arrays.asList(methodClasses));
@ -260,7 +260,7 @@ public class JValidator implements Validator {
groups.add(1, clazz);
// convert list to array
Class<?>[] classgroups = groups.toArray(new Class[0]);
Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);
Object parameterBean = getMethodParameterBean(clazz, method, arguments);
if (parameterBean != null) {

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

@ -23,6 +23,10 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import javax.validation.ConstraintViolationException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JValidatorTest {
@Test
@ -56,4 +60,27 @@ public class JValidatorTest {
JValidator jValidator = new JValidator(url);
jValidator.validate("someMethod2", new Class<?>[]{ValidationParameter.class}, new Object[]{new ValidationParameter("NotBeNull")});
}
@Test
public void testItWithArrayArg() throws Exception {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
JValidator jValidator = new JValidator(url);
jValidator.validate("someMethod3", new Class<?>[]{ValidationParameter[].class}, new Object[]{new ValidationParameter[]{new ValidationParameter("parameter")}});
}
@Test
public void testItWithCollectionArg() throws Exception {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
JValidator jValidator = new JValidator(url);
jValidator.validate("someMethod4", new Class<?>[]{List.class}, new Object[]{Arrays.asList("parameter")});
}
@Test
public void testItWithMapArg() throws Exception {
URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
JValidator jValidator = new JValidator(url);
Map<String, String> map = new HashMap<>();
map.put("key", "value");
jValidator.validate("someMethod5", new Class<?>[]{Map.class}, new Object[]{map});
}
}

View File

@ -19,6 +19,8 @@ package org.apache.dubbo.validation.support.jvalidation.mock;
import org.apache.dubbo.validation.MethodValidated;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
public interface JValidatorTestTarget {
@MethodValidated
@ -27,6 +29,12 @@ public interface JValidatorTestTarget {
@MethodValidated(Test2.class)
public void someMethod2(@NotNull ValidationParameter validationParameter);
public void someMethod3(ValidationParameter[] parameters);
public void someMethod4(List<String> strings);
public void someMethod5(Map<String, String> map);
@interface Test2 {
}

View File

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

View File

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

View File

@ -22,12 +22,14 @@ import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.monitor.Monitor;
import org.apache.dubbo.monitor.MonitorFactory;
import org.apache.dubbo.monitor.MonitorService;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@ -73,7 +75,7 @@ public class MonitorFilterTest {
public Result invoke(Invocation invocation) throws RpcException {
lastInvocation = invocation;
return null;
return AsyncRpcResult.newDefaultAsyncResult(invocation);
}
@Override
@ -115,7 +117,11 @@ public class MonitorFilterTest {
monitorFilter.setMonitorFactory(monitorFactory);
Invocation invocation = new RpcInvocation("aaa", new Class<?>[0], new Object[0]);
RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
monitorFilter.invoke(serviceInvoker, invocation);
Result result = monitorFilter.invoke(serviceInvoker, invocation);
result.thenApplyWithContext((r) -> {
monitorFilter.listener().onResponse(r, serviceInvoker, invocation);
return r;
});
while (lastStatistics == null) {
Thread.sleep(10);
}
@ -151,7 +157,11 @@ public class MonitorFilterTest {
monitorFilter.setMonitorFactory(monitorFactory);
Invocation invocation = new RpcInvocation("$invoke", new Class<?>[]{String.class, String[].class, Object[].class}, new Object[]{"xxx", new String[]{}, new Object[]{}});
RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
monitorFilter.invoke(serviceInvoker, invocation);
Result result = monitorFilter.invoke(serviceInvoker, invocation);
result.thenApplyWithContext((r) -> {
monitorFilter.listener().onResponse(r, serviceInvoker, invocation);
return r;
});
while (lastStatistics == null) {
Thread.sleep(10);
}

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

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

@ -16,6 +16,16 @@
*/
package org.apache.dubbo.remoting.exchange;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.Codec;
import org.apache.dubbo.remoting.Decodeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Response
*/
@ -173,4 +183,55 @@ public class Response {
return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent
+ ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]";
}
public static class AppResult {
protected Map<String, String> attachments = new HashMap<String, String>();
protected Object result;
protected Throwable exception;
public Map<String, String> getAttachments() {
return attachments;
}
public void setAttachments(Map<String, String> attachments) {
this.attachments = attachments;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public Throwable getException() {
return exception;
}
public void setException(Throwable exception) {
this.exception = exception;
}
}
public static class DecodeableAppResult implements Codec, Decodeable {
@Override
public void decode() throws Exception {
}
@Override
public void encode(Channel channel, OutputStream output, Object message) throws IOException {
}
@Override
public Object decode(Channel channel, InputStream input) throws IOException {
return null;
}
}
}

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

@ -29,22 +29,18 @@ import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.TimeoutException;
import org.apache.dubbo.remoting.exchange.Request;
import org.apache.dubbo.remoting.exchange.Response;
import org.apache.dubbo.remoting.exchange.ResponseCallback;
import org.apache.dubbo.remoting.exchange.ResponseFuture;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* DefaultFuture.
*/
public class DefaultFuture implements ResponseFuture {
public class DefaultFuture extends CompletableFuture<Object> {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
@ -62,12 +58,8 @@ public class DefaultFuture implements ResponseFuture {
private final Channel channel;
private final Request request;
private final int timeout;
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private final long start = System.currentTimeMillis();
private volatile long sent;
private volatile Response response;
private volatile ResponseCallback callback;
private DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
@ -79,14 +71,6 @@ public class DefaultFuture implements ResponseFuture {
CHANNELS.put(id, channel);
}
/**
* check time out of the future
*/
private static void timeoutCheck(DefaultFuture future) {
TimeoutCheckTask task = new TimeoutCheckTask(future);
TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
}
/**
* init a DefaultFuture
* 1.init a DefaultFuture
@ -160,70 +144,77 @@ public class DefaultFuture implements ResponseFuture {
}
@Override
public Object get() throws RemotingException {
return get(timeout);
}
@Override
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = Constants.DEFAULT_TIMEOUT;
}
if (!isDone()) {
long start = System.currentTimeMillis();
lock.lock();
try {
while (!isDone()) {
done.await(timeout, TimeUnit.MILLISECONDS);
if (isDone() || System.currentTimeMillis() - start > timeout) {
break;
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
if (!isDone()) {
throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
}
}
return returnFromResponse();
public boolean cancel(boolean mayInterruptIfRunning) {
Response errorResult = new Response(id);
errorResult.setStatus(Response.CLIENT_ERROR);
errorResult.setErrorMessage("request future has been canceled.");
this.doReceived(errorResult);
FUTURES.remove(id);
CHANNELS.remove(id);
return true;
}
public void cancel() {
Response errorResult = new Response(id);
errorResult.setErrorMessage("request future has been canceled.");
response = errorResult;
FUTURES.remove(id);
CHANNELS.remove(id);
this.cancel(true);
}
@Override
public boolean isDone() {
return response != null;
}
@Override
public void setCallback(ResponseCallback callback) {
if (isDone()) {
invokeCallback(callback);
} else {
boolean isdone = false;
lock.lock();
try {
if (!isDone()) {
this.callback = callback;
} else {
isdone = true;
}
} finally {
lock.unlock();
}
if (isdone) {
invokeCallback(callback);
}
private void doReceived(Response res) {
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
this.complete(res.getResult());
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
this.completeExceptionally(new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()));
} else {
this.completeExceptionally(new RemotingException(channel, res.getErrorMessage()));
}
}
private long getId() {
return id;
}
private Channel getChannel() {
return channel;
}
private boolean isSent() {
return sent > 0;
}
public Request getRequest() {
return request;
}
private int getTimeout() {
return timeout;
}
private void doSent() {
sent = System.currentTimeMillis();
}
/**
* check time out of the future
*/
private static void timeoutCheck(DefaultFuture future) {
TimeoutCheckTask task = new TimeoutCheckTask(future);
TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
}
private String getTimeoutMessage(boolean scan) {
long nowTimestamp = System.currentTimeMillis();
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
+ (scan ? " by scan timer" : "") + ". start time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
+ (sent > 0 ? " client elapsed: " + (sent - start)
+ " ms, server elapsed: " + (nowTimestamp - sent)
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress();
}
private static class TimeoutCheckTask implements TimerTask {
@ -249,105 +240,4 @@ public class DefaultFuture implements ResponseFuture {
}
}
private void invokeCallback(ResponseCallback c) {
ResponseCallback callbackCopy = c;
if (callbackCopy == null) {
throw new NullPointerException("callback cannot be null.");
}
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
}
if (res.getStatus() == Response.OK) {
try {
callbackCopy.done(res.getResult());
} catch (Exception e) {
logger.error("callback invoke error .result:" + res.getResult() + ",url:" + channel.getUrl(), e);
}
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
try {
TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
callbackCopy.caught(te);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
} else {
try {
RuntimeException re = new RuntimeException(res.getErrorMessage());
callbackCopy.caught(re);
} catch (Exception e) {
logger.error("callback invoke error ,url:" + channel.getUrl(), e);
}
}
}
private Object returnFromResponse() throws RemotingException {
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
return res.getResult();
}
if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
}
throw new RemotingException(channel, res.getErrorMessage());
}
private long getId() {
return id;
}
private Channel getChannel() {
return channel;
}
private boolean isSent() {
return sent > 0;
}
public Request getRequest() {
return request;
}
private int getTimeout() {
return timeout;
}
private long getStartTimestamp() {
return start;
}
private void doSent() {
sent = System.currentTimeMillis();
}
private void doReceived(Response res) {
lock.lock();
try {
response = res;
done.signalAll();
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
private String getTimeoutMessage(boolean scan) {
long nowTimestamp = System.currentTimeMillis();
return (sent > 0 ? "Waiting server-side response timeout" : "Sending request timeout in client-side")
+ (scan ? " by scan timer" : "") + ". start time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(start))) + ", end time: "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ","
+ (sent > 0 ? " client elapsed: " + (sent - start)
+ " ms, server elapsed: " + (nowTimestamp - sent)
: " elapsed: " + (nowTimestamp - start)) + " ms, timeout: "
+ timeout + " ms, request: " + request + ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress();
}
}

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

@ -28,10 +28,10 @@ import org.apache.dubbo.remoting.exchange.ExchangeChannel;
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import org.apache.dubbo.remoting.exchange.Request;
import org.apache.dubbo.remoting.exchange.Response;
import org.apache.dubbo.remoting.exchange.ResponseFuture;
import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
/**
* ExchangeReceiver
@ -97,12 +97,12 @@ final class HeaderExchangeChannel implements ExchangeChannel {
}
@Override
public ResponseFuture request(Object request) throws RemotingException {
public CompletableFuture<Object> request(Object request) throws RemotingException {
return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
}
@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}

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;
@ -62,7 +62,7 @@ public class HeaderExchangeClient implements ExchangeClient {
}
@Override
public ResponseFuture request(Object request) throws RemotingException {
public CompletableFuture<Object> request(Object request) throws RemotingException {
return channel.request(request);
}
@ -77,7 +77,7 @@ public class HeaderExchangeClient implements ExchangeClient {
}
@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
return channel.request(request, timeout);
}

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

@ -25,7 +25,6 @@ import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
import org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;

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

@ -29,6 +29,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import org.apache.dubbo.remoting.exchange.ExchangeServer;
import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.transport.dispatcher.FakeChannelHandlers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

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

@ -1,74 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public abstract class AbstractResult implements Result {
protected Map<String, String> attachments = new HashMap<String, String>();
protected Object result;
protected Throwable exception;
@Override
public Map<String, String> getAttachments() {
return attachments;
}
@Override
public void setAttachments(Map<String, String> map) {
this.attachments = map == null ? new HashMap<String, String>() : map;
}
@Override
public void addAttachments(Map<String, String> map) {
if (map == null) {
return;
}
if (this.attachments == null) {
this.attachments = new HashMap<String, String>();
}
this.attachments.putAll(map);
}
@Override
public String getAttachment(String key) {
return attachments.get(key);
}
@Override
public String getAttachment(String key, String defaultValue) {
String result = attachments.get(key);
if (StringUtils.isEmpty(result)) {
result = defaultValue;
}
return result;
}
@Override
public void setAttachment(String key, String value) {
attachments.put(key, value);
}
}

View File

@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
/**
* {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}:
* <ul>
* <li>AsyncRpcResult is the object that is actually passed in the call chain</li>
* <li>AppResponse only simply represents the business result</li>
* </ul>
*
* The relationship between them can be reflected in the definition of AsyncRpcResult:
* <pre>
* {@code
* Public class AsyncRpcResult implements Result {
* private CompletableFuture <AppResponse> resultFuture;
* ......
* }
* }
* </pre>
*
* In theory, AppResponse does not need to implement the {@link Result} interface, this is done mainly for compatibility purpose.
*
* @serial Do not change the class name and properties.
*/
public class AppResponse implements Result, Serializable {
private static final long serialVersionUID = -6925924956850004727L;
private Object result;
private Throwable exception;
private Map<String, String> attachments = new HashMap<String, String>();
public AppResponse() {
}
public AppResponse(Object result) {
this.result = result;
}
public AppResponse(Throwable exception) {
this.exception = exception;
}
@Override
public Object recreate() throws Throwable {
if (exception != null) {
throw exception;
}
return result;
}
@Override
public Object getValue() {
return result;
}
public void setValue(Object value) {
this.result = value;
}
@Override
public Throwable getException() {
return exception;
}
public void setException(Throwable e) {
this.exception = e;
}
@Override
public boolean hasException() {
return exception != null;
}
@Override
public Map<String, String> getAttachments() {
return attachments;
}
/**
* Append all items from the map into the attachment, if map is empty then nothing happens
*
* @param map contains all key-value pairs to append
*/
public void setAttachments(Map<String, String> map) {
this.attachments = map == null ? new HashMap<String, String>() : map;
}
public void addAttachments(Map<String, String> map) {
if (map == null) {
return;
}
if (this.attachments == null) {
this.attachments = new HashMap<String, String>();
}
this.attachments.putAll(map);
}
@Override
public String getAttachment(String key) {
return attachments.get(key);
}
@Override
public String getAttachment(String key, String defaultValue) {
String result = attachments.get(key);
if (result == null || result.length() == 0) {
result = defaultValue;
}
return result;
}
public void setAttachment(String key, String value) {
attachments.put(key, value);
}
@Override
public Result thenApplyWithContext(Function<AppResponse, AppResponse> fn) {
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
}
@Override
public <U> CompletableFuture<U> thenApply(Function<Result, ? extends U> fn) {
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
}
@Override
public Result get() throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly.");
}
@Override
public String toString() {
return "AppResponse [value=" + result + ", exception=" + exception + "]";
}
}

View File

@ -22,96 +22,27 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
/**
* <b>NOTICE!!</b>
*
* <p>
* You should never rely on this class directly when using or extending Dubbo, the implementation of {@link AsyncRpcResult}
* is only a workaround for compatibility purpose. It may be changed or even get removed from the next major version.
* Please only use {@link Result} or {@link RpcResult}.
*
* Extending the {@link Filter} is one typical use case:
* <pre>
* {@code
* public class YourFilter implements Filter {
* @Override
* public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
* System.out.println("Filter get the return value: " + result.getValue());
* // Don't do this
* // AsyncRpcResult asyncRpcResult = ((AsyncRpcResult)result;
* // System.out.println("Filter get the return value: " + asyncRpcResult.getValue());
* return result;
* }
*
* @Override
* public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
* return invoker.invoke(invocation);
* }
* }
* }
* </pre>
* </p>
* TODO RpcResult can be an instance of {@link java.util.concurrent.CompletionStage} instead of composing CompletionStage inside.
*/
public class AsyncRpcResult extends AbstractResult {
public class AsyncRpcResult implements Result {
private static final Logger logger = LoggerFactory.getLogger(AsyncRpcResult.class);
/**
* RpcContext can be changed, because thread may have been used by other thread. It should be cloned before store.
* So we use Invocation instead, Invocation will create for every invoke, but invocation only support attachments of string type.
* RpcContext may already have been changed when callback happens, it happens when the same thread is used to execute another RPC call.
* So we should keep the reference of current RpcContext instance and restore it before callback being executed.
*/
private RpcContext storedContext;
private RpcContext storedServerContext;
protected CompletableFuture<Object> valueFuture;
private CompletableFuture<AppResponse> responseFuture;
private Invocation invocation;
protected CompletableFuture<Result> resultFuture;
public AsyncRpcResult(CompletableFuture<Object> future) {
this(future, true);
}
public AsyncRpcResult(CompletableFuture<Object> future, boolean registerCallback) {
this(future, new CompletableFuture<>(), registerCallback);
}
/**
* @param future
* @param rFuture
* @param registerCallback
*/
public AsyncRpcResult(CompletableFuture<Object> future, final CompletableFuture<Result> rFuture, boolean registerCallback) {
if (rFuture == null) {
throw new IllegalArgumentException();
}
resultFuture = rFuture;
if (registerCallback) {
/**
* We do not know whether future already completed or not, it's a future exposed or even created by end user.
* 1. future complete before whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread subscribing, in our case, it's Dubbo thread.
* 2. future complete after whenComplete. whenComplete fn (resultFuture.complete) will be executed in thread calling complete, normally its User thread.
*/
future.whenComplete((v, t) -> {
RpcResult rpcResult;
if (t != null) {
if (t instanceof CompletionException) {
rpcResult = new RpcResult(t.getCause());
} else {
rpcResult = new RpcResult(t);
}
} else {
rpcResult = new RpcResult(v);
}
// instead of resultFuture we must use rFuture here, resultFuture may being changed before complete when building filter chain, but rFuture was guaranteed never changed by closure.
rFuture.complete(rpcResult);
});
}
this.valueFuture = future;
// employ copy of context avoid the other call may modify the context content
this.storedContext = RpcContext.getContext().copyOf();
this.storedServerContext = RpcContext.getServerContext().copyOf();
public AsyncRpcResult(CompletableFuture<AppResponse> future, Invocation invocation) {
this.responseFuture = future;
this.invocation = invocation;
this.storedContext = RpcContext.getContext();
this.storedServerContext = RpcContext.getServerContext();
}
@Override
@ -119,52 +50,87 @@ public class AsyncRpcResult extends AbstractResult {
return getRpcResult().getValue();
}
@Override
public void setValue(Object value) {
}
@Override
public Throwable getException() {
return getRpcResult().getException();
}
@Override
public void setException(Throwable t) {
}
@Override
public boolean hasException() {
return getRpcResult().hasException();
}
@Override
public Object getResult() {
return getRpcResult().getResult();
public CompletableFuture<AppResponse> getResponseFuture() {
return responseFuture;
}
public CompletableFuture getValueFuture() {
return valueFuture;
}
public CompletableFuture<Result> getResultFuture() {
return resultFuture;
}
public void setResultFuture(CompletableFuture<Result> resultFuture) {
this.resultFuture = resultFuture;
public void setResponseFuture(CompletableFuture<AppResponse> responseFuture) {
this.responseFuture = responseFuture;
}
public Result getRpcResult() {
try {
if (resultFuture.isDone()) {
return resultFuture.get();
if (responseFuture.isDone()) {
return responseFuture.get();
}
} catch (Exception e) {
// This should never happen;
logger.error("Got exception when trying to fetch the underlying result from AsyncRpcResult.", e);
}
return new RpcResult();
return new AppResponse();
}
@Override
public Object recreate() throws Throwable {
return valueFuture;
RpcInvocation rpcInvocation = (RpcInvocation) invocation;
if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) {
AppResponse rpcResult = new AppResponse();
CompletableFuture<Object> future = new CompletableFuture<>();
rpcResult.setValue(future);
responseFuture.whenComplete((result, t) -> {
if (t != null) {
if (t instanceof CompletionException) {
t = t.getCause();
}
future.completeExceptionally(t);
} else {
if (result.hasException()) {
future.completeExceptionally(result.getException());
} else {
future.complete(result.getValue());
}
}
});
return rpcResult.recreate();
} else if (responseFuture.isDone()) {
return responseFuture.get().recreate();
}
return (new AppResponse()).recreate();
}
public void thenApplyWithContext(Function<Result, Result> fn) {
this.resultFuture = resultFuture.thenApply(fn.compose(beforeContext).andThen(afterContext));
public Result get() throws InterruptedException, ExecutionException {
return responseFuture.get();
}
@Override
public Result thenApplyWithContext(Function<AppResponse, AppResponse> fn) {
this.responseFuture = responseFuture.thenApply(fn.compose(beforeContext).andThen(afterContext));
return this;
}
@Override
public <U> CompletableFuture<U> thenApply(Function<Result,? extends U> fn) {
return this.responseFuture.thenApply(fn);
}
@Override
@ -203,18 +169,49 @@ public class AsyncRpcResult extends AbstractResult {
private RpcContext tmpContext;
private RpcContext tmpServerContext;
private Function<Result, Result> beforeContext = (result) -> {
private Function<AppResponse, AppResponse> beforeContext = (appResponse) -> {
tmpContext = RpcContext.getContext();
tmpServerContext = RpcContext.getServerContext();
RpcContext.restoreContext(storedContext);
RpcContext.restoreServerContext(storedServerContext);
return result;
return appResponse;
};
private Function<Result, Result> afterContext = (result) -> {
private Function<AppResponse, AppResponse> afterContext = (appResponse) -> {
RpcContext.restoreContext(tmpContext);
RpcContext.restoreServerContext(tmpServerContext);
return result;
return appResponse;
};
/**
* Some utility methods used to quickly generate default AsyncRpcResult instance.
*/
public static AsyncRpcResult newDefaultAsyncResult(AppResponse result, Invocation invocation) {
return new AsyncRpcResult(CompletableFuture.completedFuture(result), invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Invocation invocation) {
return newDefaultAsyncResult(null, null, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) {
return newDefaultAsyncResult(value, null, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invocation) {
return newDefaultAsyncResult(null, t, invocation);
}
public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
CompletableFuture<AppResponse> future = new CompletableFuture<>();
AppResponse result = new AppResponse();
if (t != null) {
result.setException(t);
} else {
result.setValue(value);
}
future.complete(result);
return new AsyncRpcResult(future, invocation);
}
}

View File

@ -42,27 +42,16 @@ import org.apache.dubbo.common.extension.SPI;
*/
@SPI
public interface Filter {
/**
* do invoke filter.
* <p>
* <code>
* // before filter
* Result result = invoker.invoke(invocation);
* // after filter
* return result;
* </code>
*
* @param invoker service
* @param invocation invocation.
* @return invoke result.
* @throws RpcException
* @see org.apache.dubbo.rpc.Invoker#invoke(Invocation)
* Does not need to override/implement this method.
*/
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
default Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
return result;
interface Listener {
void onResponse(Result result, Invoker<?> invoker, Invocation invocation);
void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc;
import java.util.concurrent.CompletableFuture;
/**
* Used for async call scenario. But if the method you are calling has a {@link CompletableFuture<?>} signature
* you do not need to use this class since you will get a Future response directly.
* <p>
* Remember to save the Future reference before making another call using the same thread, otherwise,
* the current Future will be override by the new one, which means you will lose the chance get the return value.
*/
public class FutureContext {
public static ThreadLocal<CompletableFuture<?>> futureTL = new ThreadLocal<>();
/**
* get future.
*
* @param <T>
* @return future
*/
@SuppressWarnings("unchecked")
public static <T> CompletableFuture<T> getCompletableFuture() {
return (CompletableFuture<T>) futureTL.get();
}
/**
* set future.
*
* @param future
*/
public static void setFuture(CompletableFuture<?> future) {
futureTL.set(future);
}
}

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

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

View File

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

View File

@ -18,6 +18,9 @@ package org.apache.dubbo.rpc;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
/**
@ -25,7 +28,7 @@ import java.util.Map;
*
* @serial Don't change the class name and package name.
* @see org.apache.dubbo.rpc.Invoker#invoke(Invocation)
* @see org.apache.dubbo.rpc.RpcResult
* @see AppResponse
*/
public interface Result extends Serializable {
@ -36,6 +39,8 @@ public interface Result extends Serializable {
*/
Object getValue();
void setValue(Object value);
/**
* Get exception.
*
@ -43,6 +48,8 @@ public interface Result extends Serializable {
*/
Throwable getException();
void setException(Throwable t);
/**
* Has exception.
*
@ -66,14 +73,6 @@ public interface Result extends Serializable {
*/
Object recreate() throws Throwable;
/**
* @see org.apache.dubbo.rpc.Result#getValue()
* @deprecated Replace to getValue()
*/
@Deprecated
Object getResult();
/**
* get attachments.
*
@ -111,4 +110,10 @@ public interface Result extends Serializable {
void setAttachment(String key, String value);
Result thenApplyWithContext(Function<AppResponse, AppResponse> fn);
<U> CompletableFuture<U> thenApply(Function<Result, ? extends U> fn);
Result get() throws InterruptedException, ExecutionException;
}

View File

@ -70,7 +70,6 @@ public class RpcContext {
private final Map<String, String> attachments = new HashMap<String, String>();
private final Map<String, Object> values = new HashMap<String, Object>();
private Future<?> future;
private List<URL> urls;
@ -136,31 +135,6 @@ public class RpcContext {
LOCAL.set(oldContext);
}
public RpcContext copyOf() {
RpcContext copy = new RpcContext();
copy.attachments.putAll(this.attachments);
copy.values.putAll(this.values);
copy.future = this.future;
copy.urls = this.urls;
copy.url = this.url;
copy.methodName = this.methodName;
copy.parameterTypes = this.parameterTypes;
copy.arguments = this.arguments;
copy.localAddress = this.localAddress;
copy.remoteAddress = this.remoteAddress;
copy.invokers = this.invokers;
copy.invoker = this.invoker;
copy.invocation = this.invocation;
copy.request = this.request;
copy.response = this.response;
copy.asyncContext = this.asyncContext;
return copy;
}
/**
* remove context.
*
@ -242,7 +216,7 @@ public class RpcContext {
*/
@SuppressWarnings("unchecked")
public <T> CompletableFuture<T> getCompletableFuture() {
return (CompletableFuture<T>) future;
return FutureContext.getCompletableFuture();
}
/**
@ -253,7 +227,7 @@ public class RpcContext {
*/
@SuppressWarnings("unchecked")
public <T> Future<T> getFuture() {
return (Future<T>) future;
return FutureContext.getCompletableFuture();
}
/**
@ -261,8 +235,8 @@ public class RpcContext {
*
* @param future
*/
public void setFuture(Future<?> future) {
this.future = future;
public void setFuture(CompletableFuture<?> future) {
FutureContext.setFuture(future);
}
public List<URL> getUrls() {

View File

@ -47,6 +47,10 @@ public class RpcInvocation implements Invocation, Serializable {
private transient Invoker<?> invoker;
private transient Class<?> returnType;
private transient InvokeMode invokeMode;
public RpcInvocation() {
}
@ -89,6 +93,7 @@ public class RpcInvocation implements Invocation, Serializable {
public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
this(method.getName(), method.getParameterTypes(), arguments, attachment, null);
this.returnType = method.getReturnType();
}
public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
@ -220,6 +225,22 @@ public class RpcInvocation implements Invocation, Serializable {
return value;
}
public Class<?> getReturnType() {
return returnType;
}
public void setReturnType(Class<?> returnType) {
this.returnType = returnType;
}
public InvokeMode getInvokeMode() {
return invokeMode;
}
public void setInvokeMode(InvokeMode invokeMode) {
this.invokeMode = invokeMode;
}
@Override
public String toString() {
return "RpcInvocation [methodName=" + methodName + ", parameterTypes="

View File

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

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

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

View File

@ -24,9 +24,9 @@ import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
@ -45,44 +45,54 @@ import java.lang.reflect.Type;
* @see Filter
*
*/
public class CompatibleFilter implements Filter {
public class CompatibleFilter extends ListenableFilter {
private static Logger logger = LoggerFactory.getLogger(CompatibleFilter.class);
public CompatibleFilter() {
super.listener = new CompatibleListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result = invoker.invoke(invocation);
if (!invocation.getMethodName().startsWith("$") && !result.hasException()) {
Object value = result.getValue();
if (value != null) {
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?> type = method.getReturnType();
Object newValue;
String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
if ("json".equals(serialization)
|| "fastjson".equals(serialization)) {
// If the serialization key is json or fastjson
Type gtype = method.getGenericReturnType();
newValue = PojoUtils.realize(value, type, gtype);
} else if (!type.isInstance(value)) {
//if local service interface's method's return type is not instance of return value
newValue = PojoUtils.isPojo(type)
? PojoUtils.realize(value, type)
: CompatibleTypeUtils.compatibleTypeConvert(value, type);
return invoker.invoke(invocation);
}
} else {
newValue = value;
static class CompatibleListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
if (!invocation.getMethodName().startsWith("$") && !result.hasException()) {
Object value = result.getValue();
if (value != null) {
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?> type = method.getReturnType();
Object newValue;
String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
if ("json".equals(serialization) || "fastjson".equals(serialization)) {
// If the serialization key is json or fastjson
Type gtype = method.getGenericReturnType();
newValue = PojoUtils.realize(value, type, gtype);
} else if (!type.isInstance(value)) {
//if local service interface's method's return type is not instance of return value
newValue = PojoUtils.isPojo(type) ? PojoUtils.realize(value, type) : CompatibleTypeUtils.compatibleTypeConvert(value, type);
} else {
newValue = value;
}
if (newValue != value) {
result.setValue(newValue);
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
if (newValue != value) {
result = new RpcResult(newValue);
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
return result;
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -19,9 +19,9 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
@ -35,7 +35,11 @@ import org.apache.dubbo.rpc.RpcInvocation;
* @see RpcContext
*/
@Activate(group = Constants.CONSUMER, order = -10000)
public class ConsumerContextFilter implements Filter {
public class ConsumerContextFilter extends ListenableFilter {
public ConsumerContextFilter() {
super.listener = new ConsumerContextListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@ -49,18 +53,22 @@ public class ConsumerContextFilter implements Filter {
((RpcInvocation) invocation).setInvoker(invoker);
}
try {
// TODO should we clear server context?
RpcContext.removeServerContext();
return invoker.invoke(invocation);
} finally {
// TODO removeContext? but we need to save future for RpcContext.getFuture() API. If clear attachments here, attachments will not available when postProcessResult is invoked.
RpcContext.getContext().clearAttachments();
RpcContext.removeContext();
}
}
@Override
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
RpcContext.getServerContext().setAttachments(result.getAttachments());
return result;
static class ConsumerContextListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
RpcContext.getServerContext().setAttachments(result.getAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -18,9 +18,9 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
@ -36,7 +36,11 @@ import java.util.Map;
* @see RpcContext
*/
@Activate(group = Constants.PROVIDER, order = -10000)
public class ContextFilter implements Filter {
public class ContextFilter extends ListenableFilter {
public ContextFilter() {
super.listener = new ContextListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@ -84,10 +88,16 @@ public class ContextFilter implements Filter {
}
}
@Override
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
// pass attachments to result
result.addAttachments(RpcContext.getServerContext().getAttachments());
return result;
static class ContextListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
// pass attachments to result
result.addAttachments(RpcContext.getServerContext().getAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -18,12 +18,12 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcResult;
/**
* Dubbo provided default Echo echo service, which is available for all dubbo provider service interface.
@ -34,7 +34,7 @@ public class EchoFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
if (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
return new RpcResult(inv.getArguments()[0]);
return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
}
return invoker.invoke(inv);
}

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

View File

@ -19,21 +19,29 @@ package org.apache.dubbo.rpc.filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcStatus;
/**
*
* The maximum parallel execution request count per method per service for the provider.If the max configured
* <b>executes</b> is set to 10 and if invoke request where it is already 10 then it will throws exception. It
* continue the same behaviour un till it is <10.
*
*/
@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter {
public class ExecuteLimitFilter extends ListenableFilter {
private static final String EXECUTELIMIT_FILTER_START_TIME = "execugtelimit_filter_start_time";
public ExecuteLimitFilter() {
super.listener = new ExecuteLimitListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
@ -46,20 +54,32 @@ public class ExecuteLimitFilter implements Filter {
"\" /> limited.");
}
long begin = System.currentTimeMillis();
boolean isSuccess = true;
invocation.setAttachment(EXECUTELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
try {
return invoker.invoke(invocation);
} catch (Throwable t) {
isSuccess = false;
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
}
} finally {
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
}
}
static class ExecuteLimitListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), true);
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), false);
}
private long getElapsed(Invocation invocation) {
String beginTime = invocation.getAttachment(EXECUTELIMIT_FILTER_START_TIME);
return StringUtils.isNotEmpty(beginTime) ? System.currentTimeMillis() - Long.parseLong(beginTime) : 0;
}
}
}

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;
@ -47,11 +46,15 @@ import java.lang.reflect.Method;
* GenericInvokerFilter.
*/
@Activate(group = Constants.PROVIDER, order = -20000)
public class GenericFilter implements Filter {
public class GenericFilter extends ListenableFilter {
public GenericFilter() {
super.listener = new GenericListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
if (inv.getMethodName().equals(Constants.$INVOKE)
if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
@ -108,26 +111,7 @@ public class GenericFilter implements Filter {
}
}
}
Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
if (result.hasException()
&& !(result.getException() instanceof GenericException)) {
return new RpcResult(new GenericException(result.getException()));
}
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class)
.getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
.serialize(null, os).writeObject(result.getValue());
return new RpcResult(os.toByteArray());
} catch (IOException e) {
throw new RpcException("Serialize result failed.", e);
}
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
} else {
return new RpcResult(PojoUtils.generalize(result.getValue()));
}
return invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
} catch (NoSuchMethodException e) {
throw new RpcException(e.getMessage(), e);
} catch (ClassNotFoundException e) {
@ -137,4 +121,42 @@ public class GenericFilter implements Filter {
return invoker.invoke(inv);
}
static class GenericListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation inv) {
if ((inv.getMethodName().equals(Constants.$INVOKE) || inv.getMethodName().equals(Constants.$INVOKE_ASYNC))
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
String generic = inv.getAttachment(Constants.GENERIC_KEY);
if (StringUtils.isBlank(generic)) {
generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY);
}
if (result.hasException() && !(result.getException() instanceof GenericException)) {
result.setException(new GenericException(result.getException()));
}
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
try {
UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA).serialize(null, os).writeObject(result.getValue());
result.setValue(os.toByteArray());
} catch (IOException e) {
throw new RpcException("Serialize result failed.", e);
}
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
result.setValue(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD));
} else {
result.setValue(PojoUtils.generalize(result.getValue()));
}
}
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

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

View File

@ -20,12 +20,11 @@ import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import java.util.Arrays;
@ -33,42 +32,40 @@ import java.util.Arrays;
* Log any invocation timeout, but don't stop server from running
*/
@Activate(group = Constants.PROVIDER)
public class TimeoutFilter implements Filter {
public class TimeoutFilter extends ListenableFilter {
private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
private static final String TIMEOUT_FILTER_START_TIME = "timeout_filter_start_time";
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if (invocation.getAttachments() != null) {
long start = System.currentTimeMillis();
invocation.getAttachments().put(TIMEOUT_FILTER_START_TIME, String.valueOf(start));
} else {
if (invocation instanceof RpcInvocation) {
RpcInvocation invc = (RpcInvocation) invocation;
long start = System.currentTimeMillis();
invc.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(start));
}
}
return invoker.invoke(invocation);
public TimeoutFilter() {
super.listener = new TimeoutListener();
}
@Override
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
if (startAttach != null) {
long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
if (invoker.getUrl() != null
&& elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(),
"timeout", Integer.MAX_VALUE)) {
if (logger.isWarnEnabled()) {
logger.warn("invoke time out. method: " + invocation.getMethodName()
+ " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is "
+ invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
invocation.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
return invoker.invoke(invocation);
}
static class TimeoutListener implements Listener {
@Override
public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
if (startAttach != null) {
long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
if (invoker.getUrl() != null && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE)) {
if (logger.isWarnEnabled()) {
logger.warn("invoke time out. method: " + invocation.getMethodName() + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is " + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
}
}
}
}
return result;
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
}

View File

@ -16,7 +16,6 @@
*/
package org.apache.dubbo.rpc.protocol;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.Logger;
@ -24,13 +23,13 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import org.apache.dubbo.rpc.support.RpcUtils;
import java.lang.reflect.InvocationTargetException;
@ -148,9 +147,8 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
*/
invocation.addAttachments(contextAttachments);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
@ -158,21 +156,21 @@ public abstract class AbstractInvoker<T> implements Invoker<T> {
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
return AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
}
}

View File

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

View File

@ -92,13 +92,14 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
}
@Override
public <T> Invoker<T> refer(final Class<T> type, final URL url) throws RpcException {
final Invoker<T> target = proxyFactory.getInvoker(doRefer(type, url), type, url);
public <T> Invoker<T> doRefer(final Class<T> type, final URL url) throws RpcException {
final Invoker<T> target = proxyFactory.getInvoker(getFrameworkProxy(type, url), type, url);
Invoker<T> invoker = new AbstractInvoker<T>(type, url) {
@Override
protected Result doInvoke(Invocation invocation) throws Throwable {
try {
Result result = target.invoke(invocation);
// FIXME result is an AsyncRpcResult instance.
Throwable e = result.getException();
if (e != null) {
for (Class<?> rpcException : rpcExceptions) {
@ -143,6 +144,6 @@ public abstract class AbstractProxyProtocol extends AbstractProtocol {
protected abstract <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException;
protected abstract <T> T doRefer(Class<T> type, URL url) throws RpcException;
protected abstract <T> T getFrameworkProxy(Class<T> type, URL url) throws RpcException;
}

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.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
@ -70,14 +70,29 @@ public class ProtocolFilterWrapper implements Protocol {
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result = filter.invoke(next, invocation);
if (result instanceof AsyncRpcResult) {
AsyncRpcResult asyncResult = (AsyncRpcResult) result;
asyncResult.thenApplyWithContext(r -> filter.onResponse(r, invoker, invocation));
return asyncResult;
} else {
return filter.onResponse(result, invoker, invocation);
Result asyncResult;
try {
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult.thenApplyWithContext(r -> {
// onResponse callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onResponse(r, invoker, invocation);
}
}
return r;
});
}
@Override

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,47 @@ public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
public void destroy() {
}
// TODO Unified to AsyncResult?
@Override
public Result invoke(Invocation invocation) throws RpcException {
RpcContext rpcContext = RpcContext.getContext();
try {
Object obj = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
if (RpcUtils.isReturnTypeFuture(invocation)) {
return new AsyncRpcResult((CompletableFuture<Object>) obj);
} else if (rpcContext.isAsyncStarted()) { // ignore obj in case of RpcContext.startAsync()? always rely on user to write back.
return new AsyncRpcResult(((AsyncContextImpl)(rpcContext.getAsyncContext())).getInternalFuture());
} else {
return new RpcResult(obj);
}
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
CompletableFuture<Object> future = wrapWithFuture(value, invocation);
CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
AppResponse result = new AppResponse();
if (t != null) {
if (t instanceof CompletionException) {
result.setException(t.getCause());
} else {
result.setException(t);
}
} else {
result.setValue(obj);
}
return result;
});
return new AsyncRpcResult(appResponseFuture, invocation);
} catch (InvocationTargetException e) {
// TODO async throw exception before async thread write back, should stop asyncContext
if (rpcContext.isAsyncStarted() && !rpcContext.stopAsync()) {
if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
}
return new RpcResult(e.getTargetException());
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
private CompletableFuture<Object> wrapWithFuture (Object value, Invocation invocation) {
if (RpcContext.getContext().isAsyncStarted()) {
return ((AsyncContextImpl)(RpcContext.getContext().getAsyncContext())).getInternalFuture();
} else if (RpcUtils.isReturnTypeFuture(invocation)) {
if (value == null) {
return CompletableFuture.completedFuture(null);
}
return (CompletableFuture<Object>) value;
}
return CompletableFuture.completedFuture(value);
}
protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
@Override

View File

@ -16,12 +16,10 @@
*/
package org.apache.dubbo.rpc.proxy;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.support.RpcUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@ -54,16 +52,6 @@ public class InvokerInvocationHandler implements InvocationHandler {
return invoker.equals(args[0]);
}
return invoker.invoke(createInvocation(method, args)).recreate();
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
private RpcInvocation createInvocation(Method method, Object[] args) {
RpcInvocation invocation = new RpcInvocation(method, args);
if (RpcUtils.hasFutureReturnType(method)) {
invocation.setAttachment(Constants.FUTURE_RETURNTYPE_KEY, "true");
invocation.setAttachment(Constants.ASYNC_KEY, "true");
}
return invocation;
}
}

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

@ -19,18 +19,18 @@ package org.apache.dubbo.rpc.support;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.RpcResult;
import com.alibaba.fastjson.JSON;
@ -104,7 +104,7 @@ final public class MockInvoker<T> implements Invoker<T> {
try {
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
Object value = parseMockValue(mock, returnTypes);
return new RpcResult(value);
return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
} catch (Exception ew) {
throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
+ ", mock:" + mock + ", url: " + url, ew);

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 {
protected <T> Invoker<T> doRefer(Class<T> type, URL url) throws RpcException {
return new MockInvoker<T>(url);
}
}

View File

@ -23,15 +23,14 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.InvokeMode;
import org.apache.dubbo.rpc.RpcInvocation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
/**
@ -65,7 +64,6 @@ public class RpcUtils {
return null;
}
// TODO why not get return type when initialize Invocation?
public static Type[] getReturnTypes(Invocation invocation) {
try {
if (invocation != null && invocation.getInvoker() != null
@ -80,24 +78,7 @@ public class RpcUtils {
if (method.getReturnType() == void.class) {
return null;
}
Class<?> returnType = method.getReturnType();
Type genericReturnType = method.getGenericReturnType();
if (Future.class.isAssignableFrom(returnType)) {
if (genericReturnType instanceof ParameterizedType) {
Type actualArgType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
if (actualArgType instanceof ParameterizedType) {
returnType = (Class<?>) ((ParameterizedType) actualArgType).getRawType();
genericReturnType = actualArgType;
} else {
returnType = (Class<?>) actualArgType;
genericReturnType = returnType;
}
} else {
returnType = null;
genericReturnType = null;
}
}
return new Type[]{returnType, genericReturnType};
return ReflectUtils.getReturnTypes(method);
}
}
} catch (Throwable t) {
@ -184,11 +165,22 @@ public class RpcUtils {
}
public static boolean isReturnTypeFuture(Invocation inv) {
return Boolean.TRUE.toString().equals(inv.getAttachment(Constants.FUTURE_RETURNTYPE_KEY));
Class<?> clazz = getReturnType(inv);
return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv);
}
public static boolean hasFutureReturnType(Method method) {
return CompletableFuture.class.isAssignableFrom(method.getReturnType());
public static InvokeMode getInvokeMode(URL url, Invocation inv) {
if (isReturnTypeFuture(inv)) {
return InvokeMode.FUTURE;
} else if (isAsync(url, inv)) {
return InvokeMode.ASYNC;
} else {
return InvokeMode.SYNC;
}
}
public static boolean isGenericAsync(Invocation inv) {
return Constants.$INVOKE_ASYNC.equals(inv.getMethodName());
}
public static boolean isOneway(URL url, Invocation inv) {

View File

@ -26,7 +26,7 @@ public class RpcResultTest {
@Test
public void testRecreateWithNormalException() {
NullPointerException npe = new NullPointerException();
RpcResult rpcResult = new RpcResult(npe);
AppResponse rpcResult = new AppResponse(npe);
try {
rpcResult.recreate();
fail();
@ -64,7 +64,7 @@ public class RpcResultTest {
}
// end construct a NullPointerException with empty stackTrace
RpcResult rpcResult = new RpcResult(throwable);
AppResponse rpcResult = new AppResponse(throwable);
try {
rpcResult.recreate();
fail();

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);
Result rpcResult = asyncResult.get();
compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
assertEquals(Type.High, rpcResult.getValue());
}
@Test
public void testInvokerNonJsonEnumSerialization() {
public void testInvokerNonJsonEnumSerialization() throws Exception {
invocation = mock(Invocation.class);
given(invocation.getMethodName()).willReturn("enumlength");
given(invocation.getParameterTypes()).willReturn(new Class<?>[]{Type[].class});
@ -117,14 +120,16 @@ public class CompatibleFilterFilterTest {
invoker = mock(Invoker.class);
given(invoker.isAvailable()).willReturn(true);
given(invoker.getInterface()).willReturn(DemoService.class);
RpcResult result = new RpcResult();
AppResponse result = new AppResponse();
result.setValue("High");
given(invoker.invoke(invocation)).willReturn(result);
given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(result, invocation));
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
given(invoker.getUrl()).willReturn(url);
Result filterResult = compatibleFilter.invoke(invoker, invocation);
assertEquals(Type.High, filterResult.getValue());
Result asyncResult = compatibleFilter.invoke(invoker, invocation);
Result rpcResult = asyncResult.get();
compatibleFilter.listener().onResponse(rpcResult, invoker, invocation);
assertEquals(Type.High, rpcResult.getValue());
}
@Test
@ -137,7 +142,7 @@ public class CompatibleFilterFilterTest {
invoker = mock(Invoker.class);
given(invoker.isAvailable()).willReturn(true);
given(invoker.getInterface()).willReturn(DemoService.class);
RpcResult result = new RpcResult();
AppResponse result = new AppResponse();
result.setValue(new String[]{"High"});
given(invoker.invoke(invocation)).willReturn(result);
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
@ -157,7 +162,7 @@ public class CompatibleFilterFilterTest {
invoker = mock(Invoker.class);
given(invoker.isAvailable()).willReturn(true);
given(invoker.getInterface()).willReturn(DemoService.class);
RpcResult result = new RpcResult();
AppResponse result = new AppResponse();
result.setValue("hello");
given(invoker.invoke(invocation)).willReturn(result);
URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");

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

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