-
Notifications
You must be signed in to change notification settings - Fork 26.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide consumers with real exception thrown by providers #3387
Conversation
...pache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
Outdated
Show resolved
Hide resolved
e = t; | ||
} | ||
|
||
throw e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be thrown directly here, it does not need to judge, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
接口声明中 throws 的异常是 ValidationException
,但是消费者捕获到的却是 UndeclaredThrowableException
,此处通过解析出最内层的 Throwable
,来符合接口声明。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为何最内层的 Throwable 可以符合接口声明呢?
- 在我们使用了
ExceptionFilter
的情况下,将会由 ExceptionFilter 构造出同 Provider 中抛出的相同的Exception
({"error": { "data": {"name": "...."}}}
),此时可以捕获到相同的异常(Stacktrace不相同,此处暂时忽略,需要其他解决方案)。 - 在没有使用
ExceptionFilter
的情况下,将会由com.googlecode.jsonrpc4j.DefaultExceptionResolver
抛出一个可以由消费者使用的异常 - 以上两种情况抛出的异常均是消费者感兴趣的,但是由于通过
Proxy
调用 invoker,invoker 抛出的所有异常均会包装为UndeclaredThrowableException
,故我们需要在 handler 中将此异常还原为 invoker 中的异常。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @CrazyHZM.
Will this pr be merge into master branch? Or else be refused?
English version of previous answer:
Why does it need to judge?
Service interface defines throws ValidationException
, but try { ... } catch (ValidationException e) { }
in consumer side do not work.
Consumer side gets UndeclaredThrowableException
instead. So I add this pr to convert this Exception to the expected one.
How to do this
DefaultExceptionResolver
or ExceptionFilter
will repackage the exception from provider side, so we go inside and re-throw the innermost one.
This works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
@beiwei30
Please you review this PR.
Codecov Report
@@ Coverage Diff @@
## master #3387 +/- ##
=========================================
Coverage ? 63.85%
Complexity ? 71
=========================================
Files ? 661
Lines ? 28608
Branches ? 4822
=========================================
Hits ? 18269
Misses ? 8116
Partials ? 2223
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think typically, RPC interfaces don’t declare a exception to throw. So if the developer doesn’t declare a checked exception, the modification may eat some runtime exception stacktrace information.
I think giving the entire exception stacktrace to application layer can help developer trace problems better. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is the right approach.
I write a simple demo to mimic this problem but fail to reproduce, see the output below: java.lang.RuntimeException: hello world
at org.apache.dubbo.samples.basic.impl.DemoServiceImpl.sayHello(DemoServiceImpl.java:27)
at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47)
at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:76)
at com.alibaba.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:52)
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)
at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:62)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:73)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:138)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:104)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:96)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:173)
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834) I am wondering your mailService is enhanced by Spring framework. You could debug your app and double check. |
try { | ||
return method.invoke(bean, args); | ||
} catch (Throwable e) { | ||
logger.error("Convert RpcException to real exception"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think better to log throwable also e.g. logger.error("Convert RpcException to real exception",t);
The exception you got was logged in I want to: make consumer side get the same exception with the one thrown by provider NOTICE: ONLY with same name, NOT contains call stacks Thanks to @tswstarplanet for pointing out the weakness of this superficial resolution. More effective code is recommended: @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(bean, args);
} catch (UndeclaredThrowableException e) {
logger.error("Convert RpcException to real exception");
// Only care InvocationTargetException in UndeclaredThrowableException
if (e.getCause() instanceof InvocationTargetException) {
throw e.getCause().getCause();
} else {
throw e;
}
}
} |
@beiwei30 new code is ready. Review please. |
zhangjinshuai seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Currently, Related source code is no longer existed in the latest version of master branch. Please feel free to reopen this pr if you still have any question, also you can create a new pr for related code. |
What is the purpose of the change
Fix #3386
Brief changelog
Consumer can catch the same exception as which thrown by provider.
Verifying this change
Verified
Follow this checklist to help us incorporate your contribution quickly and easily: