support apache dubbo asynchronous call #1114 - #1124
Conversation
| } catch (RpcException e) { | ||
| 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.
interfaceEntry and interfaceEntry exit() method should ensure executed regardless of whether there is an exception?
There was a problem hiding this comment.
The logic might be:
- When the invocation is successful, then the entries will be exited in
onResponsecallback. - When the invocation fails, then record the exception and complete the entries. For asynchronous call, it's handled in
whenCompletecallback.
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.
根据我的理解,在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.
invocation arg is not used?
| rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry); | ||
| rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry); |
There was a problem hiding this comment.
是的,异步调用的情况下,我们需要保存对应的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.
trace方法包括了exit的逻辑
| .append(":") | ||
| .append(invocation.getMethodName()) | ||
| .append("("); | ||
| buf.append(invoker.getUrl().getEncodedServiceKey()) |
There was a problem hiding this comment.
What's the difference between invoker.getInterface().getName() and invoker.getUrl().getEncodedServiceKey()?
There was a problem hiding this comment.
细分粒度吧,getEncodedServiceKey细分为interfaceName+group+version
There was a problem hiding this comment.
Friendly ping :)
最近工作有点忙... 没及时响应,抱歉
There was a problem hiding this comment.
细分粒度吧,
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.
我也赞同#67这个人的回答,为啥不把group和version作为resourceName的一部分呢?当用户决定使用group和version属性的时候,往往这也表示这是对同一个java的接口多种实现,难道不应该区分开来吗
There was a problem hiding this comment.
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.
你是说不兼容的问题吗,确实应该设置一个开关去让用户选择使用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.
This should be methodEntry.exit(1, args).
There was a problem hiding this comment.
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.
好的,我将会传递invocation作为args
There was a problem hiding this comment.
所以,同样也需要在获取entry的时候,调用带args的参数?
There was a problem hiding this comment.
Yes. You may refer to the former implementation.
| return buf.toString(); | ||
| } | ||
|
|
||
| public static String getResourceName(Invoker<?> invoker, Invocation invocation, String prefix) { |
There was a problem hiding this comment.
There was a problem hiding this comment.
We sync the code in every release, while the baseline is in this repo.
| } catch (RpcException e) { | ||
| 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.
The logic might be:
- When the invocation is successful, then the entries will be exited in
onResponsecallback. - When the invocation fails, then record the exception and complete the entries. For asynchronous call, it's handled in
whenCompletecallback.
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.
The name (trace) can be confusing. How about traceAndComplete or traceAndExit?
There was a problem hiding this comment.
traceException?
There was a problem hiding this comment.
In this method the entries are also exited, so the name traceException might not be enough.
There was a problem hiding this comment.
嗯,也对,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.
Could you please add your @author information here?
| @@ -0,0 +1,41 @@ | |||
| package com.alibaba.csp.sentinel.adapter.dubbo; | |||
There was a problem hiding this comment.
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.
*/
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.
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.
Why it's not necessary to trace the biz exception? I've tested three different scenarios:
- Normal responses will be handled in the
Filter#onResponsecallback (whenCompletecallback will not catch it) - Timeout. The
org.apache.dubbo.remoting.TimeoutExceptionwill be caught in thewhenCompletecallback (Filter#onResponsewill not catch it) - Biz exception. Both
Filter#onResponseand thewhenCompletecallback could get signaled, but theFilter#onResponsewill 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.
This should be enabled by default.
| RpcContext.getContext().remove(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY); | ||
| } | ||
| if (!(interfaceEntry instanceof AsyncEntry)) { | ||
| ContextUtil.exit(); |
There was a problem hiding this comment.
In the consumer side, we don't need to (and should not) clear the context (though there's guard checking in ContextUtil.exit())
| 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.
The interface entry should not carry the invocation.getArguments(), which is only needed in the method resource.
There was a problem hiding this comment.
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.
Using English comment is better :)
There was a problem hiding this comment.
sry. I will delete this modification.
| public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled"; | ||
|
|
||
| public static boolean isUsePrefix(){ | ||
| public static final String TRACE_BIZ_EXCEPTION_ENABLED = "csp.sentinel.dubbo.trace.biz.exceptio.enabled"; |
| } catch (RpcException e) { | ||
| Tracer.traceEntry(e, interfaceEntry); | ||
| Tracer.traceEntry(e, methodEntry); | ||
| throw e; |
There was a problem hiding this comment.
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.
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.
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
|
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