Skip to content
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

Closed
wants to merge 4 commits into from

Conversation

eachcan
Copy link

@eachcan eachcan commented Jan 30, 2019

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:

e = t;
}

throw e;
Copy link
Member

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?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

接口声明中 throws 的异常是 ValidationException,但是消费者捕获到的却是 UndeclaredThrowableException,此处通过解析出最内层的 Throwable,来符合接口声明。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为何最内层的 Throwable 可以符合接口声明呢?

  1. 在我们使用了 ExceptionFilter 的情况下,将会由 ExceptionFilter 构造出同 Provider 中抛出的相同的 Exception ({"error": { "data": {"name": "...."}}}),此时可以捕获到相同的异常(Stacktrace不相同,此处暂时忽略,需要其他解决方案)。
  2. 在没有使用 ExceptionFilter 的情况下,将会由 com.googlecode.jsonrpc4j.DefaultExceptionResolver 抛出一个可以由消费者使用的异常
  3. 以上两种情况抛出的异常均是消费者感兴趣的,但是由于通过 Proxy 调用 invoker,invoker 抛出的所有异常均会包装为 UndeclaredThrowableException,故我们需要在 handler 中将此异常还原为 invoker 中的异常。

Copy link
Author

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.

Copy link
Member

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-io
Copy link

Codecov Report

❗ No coverage uploaded for pull request base (master@19c1af8). Click here to learn what that means.
The diff coverage is 28.57%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #3387   +/-   ##
=========================================
  Coverage          ?   63.85%           
  Complexity        ?       71           
=========================================
  Files             ?      661           
  Lines             ?    28608           
  Branches          ?     4822           
=========================================
  Hits              ?    18269           
  Misses            ?     8116           
  Partials          ?     2223
Impacted Files Coverage Δ Complexity Δ
...notation/ReferenceAnnotationBeanPostProcessor.java 88.31% <28.57%> (ø) 0 <0> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 19c1af8...2f402dc. Read the comment docs.

Copy link
Contributor

@tswstarplanet tswstarplanet left a 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.

@tswstarplanet
Copy link
Contributor

I think giving the entire exception stacktrace to application layer can help developer trace problems better.

Copy link
Member

@beiwei30 beiwei30 left a 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.

@beiwei30
Copy link
Member

beiwei30 commented Jan 31, 2019

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");
Copy link
Contributor

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

@eachcan
Copy link
Author

eachcan commented Feb 13, 2019

@beiwei30

I write a simple demo to mimic this problem but fail to reproduce, see the output below:
...

The exception you got was logged in provider side, and the problem I wanted to describe is occurred in consumer side.

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

@eachcan
Copy link
Author

eachcan commented Feb 13, 2019

@beiwei30 new code is ready. Review please.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


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.

@AlbumenJ
Copy link
Member

Currently, ReferenceAnnotationBeanPostProcessor has been reformatted for serval times.

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.

@AlbumenJ AlbumenJ closed this Feb 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

consumer always catch java.lang.reflect.UndeclaredThrowableException for the exception thrown from provider
8 participants