-
Notifications
You must be signed in to change notification settings - Fork 8.1k
support apache dubbo asynchronous call #1114 #1124
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
Conversation
Tracer.traceEntry(e, interfaceEntry); | ||
Tracer.traceEntry(e, methodEntry); | ||
// catch timeout or nonbiz-exception when in sync model | ||
trace(e, invocation); | ||
throw e; | ||
} finally { | ||
if (methodEntry != null) { | ||
methodEntry.exit(); | ||
} | ||
if (interfaceEntry != null) { | ||
interfaceEntry.exit(); | ||
} | ||
} |
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.
interfaceEntry
and interfaceEntry
exit() method should ensure executed regardless of whether there is an 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.
The logic might be:
- When the invocation is successful, then the entries will be exited in
onResponse
callback. - When the invocation fails, then record the exception and complete the entries. For asynchronous call, it's handled in
whenComplete
callback.
But for the synchronous case, we may need to check whether the result has exceptions or not, or some business exceptions might be ignored.
Besides, could you please provide a more detailed design in the PR description? @CodingSinger
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.
根据我的理解,在Dubbo2.7.1中,请求如果正常返回,则会回调Filter#onResponse
,如果请求超时或者是发生Rpc框架异常,则分两种情况:
- 当同步调用时,需要自己try catch来捕获。
- 当异步调用时,异常会在filter丢失,如果需要在filter记录异常,需要自己用
whenComplete
的方式来回调。
综上,我们需要在三个地方埋点。onResponse的trace针对正常返回,try...catch
的trace针对同步调用异常,whenComplete
针对异步调用异常
|
||
static void trace(Throwable throwable, Invocation invocation) { |
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.
invocation arg
is not used?
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.
是的,暂时没作用,可以去掉
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry); | ||
rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry); |
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.
this is neccessary?
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.
是的,异步调用的情况下,我们需要保存对应的Entry。
} catch (RpcException e) { | ||
Tracer.traceEntry(e, interfaceEntry); | ||
Tracer.traceEntry(e, methodEntry); | ||
trace(e, invocation); | ||
throw e; | ||
} finally { | ||
if (methodEntry != null) { | ||
methodEntry.exit(1, invocation.getArguments()); | ||
} | ||
if (interfaceEntry != null) { | ||
interfaceEntry.exit(); | ||
} | ||
ContextUtil.exit(); | ||
} | ||
} |
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.
miss exit()
operate?
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.
trace
方法包括了exit
的逻辑
.append(":") | ||
.append(invocation.getMethodName()) | ||
.append("("); | ||
buf.append(invoker.getUrl().getEncodedServiceKey()) |
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.
What's the difference between invoker.getInterface().getName()
and invoker.getUrl().getEncodedServiceKey()
?
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.
细分粒度吧,getEncodedServiceKey
细分为interfaceName+group+version
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.
Friendly ping :)
最近工作有点忙... 没及时响应,抱歉
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.
细分粒度吧,
getEncodedServiceKey
细分为interfaceName+group+version
This could bring breaking changes, which is not recommended. Actually we've had a discussion before: #67
In most scenarios, the groupName and version are not necessary for flow control. Maybe we could add a property to control it.
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.
我也赞同#67这个人的回答,为啥不把group和version作为resourceName的一部分呢?当用户决定使用group和version属性的时候,往往这也表示这是对同一个java的接口多种实现,难道不应该区分开来吗
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.
We could add a property configuration to control this (e.g. in DubboConfig), but I don't think it's a good idea to change the default behavior, as this could bring breaking changes, which will break existing rules of users.
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.
你是说不兼容的问题吗,确实应该设置一个开关去让用户选择使用getEncodedServiceKey
或者是getInterface
Entry methodEntry = (Entry) RpcContext.getContext().get(DubboUtils.DUBBO_METHOD_ENTRY_KEY); | ||
if (methodEntry != null) { | ||
Tracer.traceEntry(throwable, methodEntry); | ||
methodEntry.exit(); |
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.
This should be methodEntry.exit(1, args)
.
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.
args有啥作用吗~
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.
The args
is used for parameter flow control. The args
in SphU.entry(xxx, et, c, args)
and entry.exit(c, args)
should match, or statistic error might occur.
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.
好的,我将会传递invocation作为args
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.
所以,同样也需要在获取entry的时候,调用带args的参数?
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.
Yes. You may refer to the former implementation.
@@ -51,17 +52,5 @@ public static String getResourceName(Invoker<?> invoker, Invocation invocation) | |||
return buf.toString(); | |||
} | |||
|
|||
public static String getResourceName(Invoker<?> invoker, Invocation invocation, String prefix) { |
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.
Why this is removed? It was added in #859
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.
好吧.. 我代码是从https://github.com/apache/dubbo-sentinel-support/blob/master/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java 拷贝来的,应该是该仓库对该功能的修改未同步过去
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.
We sync the code in every release, while the baseline is in this repo.
Tracer.traceEntry(e, interfaceEntry); | ||
Tracer.traceEntry(e, methodEntry); | ||
// catch timeout or nonbiz-exception when in sync model | ||
trace(e, invocation); | ||
throw e; | ||
} finally { | ||
if (methodEntry != null) { | ||
methodEntry.exit(); | ||
} | ||
if (interfaceEntry != null) { | ||
interfaceEntry.exit(); | ||
} | ||
} |
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.
The logic might be:
- When the invocation is successful, then the entries will be exited in
onResponse
callback. - When the invocation fails, then record the exception and complete the entries. For asynchronous call, it's handled in
whenComplete
callback.
But for the synchronous case, we may need to check whether the result has exceptions or not, or some business exceptions might be ignored.
Besides, could you please provide a more detailed design in the PR description? @CodingSinger
} | ||
|
||
|
||
static void trace(Throwable throwable, Invocation invocation) { |
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.
The name (trace
) can be confusing. How about traceAndComplete
or traceAndExit
?
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.
traceException
?
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.
In this method the entries are also exited, so the name traceException
might not be enough.
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.
嗯,也对,traceAndExit
更合适
Friendly ping :) |
03520a3
to
08742d6
Compare
Codecov Report
@@ Coverage Diff @@
## master #1124 +/- ##
============================================
+ Coverage 43.01% 43.29% +0.27%
- Complexity 1567 1578 +11
============================================
Files 337 338 +1
Lines 9877 9892 +15
Branches 1332 1335 +3
============================================
+ Hits 4249 4283 +34
+ Misses 5099 5082 -17
+ Partials 529 527 -2
Continue to review full report at Codecov.
|
Could you please resolve the conflicts? You may add an additional |
ok,i will resolve it soon. |
08742d6
to
bf4a611
Compare
import org.apache.dubbo.rpc.Result; | ||
import org.apache.dubbo.rpc.RpcContext; | ||
|
||
public abstract class BaseSentinelDubboFilter implements Filter { |
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.
Could you please add your @author
information here?
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.
done~
@@ -0,0 +1,41 @@ | |||
package com.alibaba.csp.sentinel.adapter.dubbo; |
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.
Plz add the license header here like:
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
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.
done~
bf4a611
to
92912d2
Compare
if (result instanceof AsyncRpcResult) { | ||
// catch timeout or nonbiz-exception when in async model | ||
AsyncRpcResult asyncRpcResult = (AsyncRpcResult) result; | ||
asyncRpcResult.getValueFuture().whenComplete((rs,ex) -> { |
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.
This is not compatible with Dubbo 2.7.2+. Since 2.7.2, the AsyncRpcResult
directly extends the CompletableFuture
and removed the getValueFuture
method. You have to handle this for both versions.
@Override | ||
public Result onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) { | ||
//it's unnecessary to traceAndExit the business exception | ||
traceAndExit(null, invocation); |
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.
Why it's not necessary to trace the biz exception? I've tested three different scenarios:
- Normal responses will be handled in the
Filter#onResponse
callback (whenComplete
callback will not catch it) - Timeout. The
org.apache.dubbo.remoting.TimeoutException
will be caught in thewhenComplete
callback (Filter#onResponse
will not catch it) - Biz exception. Both
Filter#onResponse
and thewhenComplete
callback could get signaled, but theFilter#onResponse
will be notified first, so if we don't trace the biz exception here, it will be lost. We might need to trace theappResponse.getException()
here.
92912d2
to
a847403
Compare
Because of some stability and compatibility reasons, we decide to support the Dubbo based on Dubbo version 2.7.3. |
} | ||
|
||
public static Boolean getDubboBizExceptionTraceEnabled(){ | ||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(TRACE_BIZ_EXCEPTION_ENABLED)); |
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.
This should be enabled by default.
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.
ok~
RpcContext.getContext().remove(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY); | ||
} | ||
if (!(interfaceEntry instanceof AsyncEntry)) { | ||
ContextUtil.exit(); |
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.
In the consumer side, we don't need to (and should not) clear the context (though there's guard checking in ContextUtil.exit()
)
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.
1
Tracer.traceEntry(e, interfaceEntry); | ||
Tracer.traceEntry(e, methodEntry); | ||
if (InvokeMode.SYNC == invokeMode) { | ||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments()); |
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.
The interface entry should not carry the invocation.getArguments()
, which is only needed in the method resource.
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.
sry. that's my mistake.
eb73dd3
to
4853f56
Compare
return String.format("Hello, %s at %s", name, LocalDateTime.now()); | ||
final AsyncContext asyncContext = RpcContext.startAsync(); | ||
new Thread(() -> { | ||
// 如果要使用上下文,则必须要放在第一句执行 |
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.
Using English comment is 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.
sry. I will delete this modification.
|
||
public static boolean isUsePrefix(){ | ||
public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exceptio.enabled"; |
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.
typo here
} catch (BlockException e) { | ||
return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e); | ||
} catch (RpcException e) { | ||
Tracer.traceEntry(e, interfaceEntry); | ||
Tracer.traceEntry(e, methodEntry); | ||
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.
Missing traceAndExit()
here?
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.
No. It's also unnecessary to catch the RpcException in SentinelDubboProvider
and SentinelDubboConsumer
to trace. Dubbo will catch the Exception when exec filter.invoke
and call the onError
to handle this.
4853f56
to
0b402bf
Compare
0b402bf
to
9d311f2
Compare
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN); | ||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments()); | ||
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry); | ||
rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry); |
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.
A fatal bug here: we have two resources here: interface level and method level. If we set a flow rule for the method resource and when it's blocked, the Entry of the interface level will not be carried in the RpcContext, leading to unexpected behavior (some entries cannot be exited finally). This should be carefully designed and tested. More unit tests and integration tests are needed.
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.
Yes, the entry should be bound to context immediately when finish the call of entry
method. And should call the superclass's traceAndExit
if there is a BlockException
be thrown. I will write some tests for this.~
acddddd
to
db8a612
Compare
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.
LGTM
Nice work. Thanks for contributing! |
…ibaba#1124) * Improve async support for Dubbo 2.7.2 and above (not compatible with 2.7.0 and 2.7.1 due to the bad compatibility design of Dubbo Filter)
Fixes #1114