Skip to content

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

Merged
merged 2 commits into from
Dec 23, 2019

Conversation

CodingSinger
Copy link
Contributor

@CodingSinger CodingSinger commented Oct 29, 2019

Fixes #1114

@sczyh30 sczyh30 added kind/enhancement Category issues or prs related to enhancement. to-review To review labels Oct 29, 2019
Comment on lines 71 to 77
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();
}
}
Copy link
Collaborator

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?

Copy link
Member

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

Copy link
Contributor Author

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框架异常,则分两种情况:

  1. 当同步调用时,需要自己try catch来捕获。
  2. 当异步调用时,异常会在filter丢失,如果需要在filter记录异常,需要自己用whenComplete的方式来回调。

综上,我们需要在三个地方埋点。onResponse的trace针对正常返回,try...catch的trace针对同步调用异常,whenComplete针对异步调用异常

Comment on lines 23 to 24

static void trace(Throwable throwable, Invocation invocation) {
Copy link
Collaborator

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

是的,暂时没作用,可以去掉

Comment on lines 66 to 70
rpcContext.set(DubboUtils.DUBBO_INTERFACE_ENTRY_KEY, interfaceEntry);
rpcContext.set(DubboUtils.DUBBO_METHOD_ENTRY_KEY, methodEntry);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is neccessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

是的,异步调用的情况下,我们需要保存对应的Entry。

Comment on lines 71 to 75
} 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();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

miss exit() operate?

Copy link
Contributor Author

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())
Copy link
Member

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()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

细分粒度吧,getEncodedServiceKey细分为interfaceName+group+version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Friendly ping :)

最近工作有点忙... 没及时响应,抱歉

Copy link
Member

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.

Copy link
Contributor Author

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的接口多种实现,难道不应该区分开来吗

Copy link
Member

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.

Copy link
Contributor Author

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();
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

args有啥作用吗~

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

好的,我将会传递invocation作为args

Copy link
Contributor Author

Choose a reason for hiding this comment

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

所以,同样也需要在获取entry的时候,调用带args的参数?

Copy link
Member

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) {
Copy link
Member

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

Copy link
Contributor Author

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 拷贝来的,应该是该仓库对该功能的修改未同步过去

Copy link
Member

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.

Comment on lines 71 to 77
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();
}
}
Copy link
Member

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) {
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

traceException

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

嗯,也对,traceAndExit 更合适

@sczyh30
Copy link
Member

sczyh30 commented Nov 4, 2019

Friendly ping :)

@codecov-io
Copy link

codecov-io commented Nov 5, 2019

Codecov Report

Merging #1124 into master will increase coverage by 0.27%.
The diff coverage is 83.33%.

Impacted file tree graph

@@             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
Impacted Files Coverage Δ Complexity Δ
...alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java 95.83% <100%> (+0.37%) 9 <1> (+2) ⬆️
.../sentinel/adapter/dubbo/DubboAppContextFilter.java 100% <100%> (ø) 3 <2> (ø) ⬇️
...csp/sentinel/adapter/dubbo/config/DubboConfig.java 80% <60%> (-10%) 9 <2> (+2)
...entinel/adapter/dubbo/BaseSentinelDubboFilter.java 73.91% <73.91%> (ø) 3 <3> (?)
...nel/adapter/dubbo/SentinelDubboProviderFilter.java 83.33% <90%> (+24.71%) 2 <0> (ø) ⬇️
...nel/adapter/dubbo/SentinelDubboConsumerFilter.java 95% <92.3%> (+45%) 3 <0> (+1) ⬆️
...tinel/slots/block/flow/param/ParamFlowChecker.java 55.4% <0%> (+2.7%) 29% <0%> (+1%) ⬆️
...m/alibaba/csp/sentinel/log/DateFileLogHandler.java 57.57% <0%> (+3.03%) 7% <0%> (+1%) ⬆️
...ava/com/alibaba/csp/sentinel/node/ClusterNode.java 100% <0%> (+3.44%) 11% <0%> (+1%) ⬆️

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 49861f4...9d311f2. Read the comment docs.

@sczyh30
Copy link
Member

sczyh30 commented Nov 19, 2019

Could you please resolve the conflicts? You may add an additional resourceType when calling SphU.entry(xxx) (see #1142).

@CodingSinger
Copy link
Contributor Author

Could you please resolve the conflicts? You may add an additional resourceType when calling SphU.entry(xxx) (see #1142).

ok,i will resolve it soon.

@sczyh30 sczyh30 added the area/integrations Issues or PRs related to integrations with open-source components label Dec 2, 2019
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;

public abstract class BaseSentinelDubboFilter implements Filter {
Copy link
Member

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?

Copy link
Contributor Author

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;
Copy link
Member

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.
 */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done~

if (result instanceof AsyncRpcResult) {
// catch timeout or nonbiz-exception when in async model
AsyncRpcResult asyncRpcResult = (AsyncRpcResult) result;
asyncRpcResult.getValueFuture().whenComplete((rs,ex) -> {
Copy link
Member

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

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 the whenComplete callback (Filter#onResponse will not catch it)
  • Biz exception. Both Filter#onResponse and the whenComplete callback could get signaled, but the Filter#onResponse will be notified first, so if we don't trace the biz exception here, it will be lost. We might need to trace the appResponse.getException() here.

@CodingSinger
Copy link
Contributor Author

CodingSinger commented Dec 3, 2019

Because of some stability and compatibility reasons, we decide to support the Dubbo based on Dubbo version 2.7.3.
Now normal responses and bizException will be handled in the Listener#onResponsecallback.
In addition, we also provide a parameter DubboConfig.TRACE_BIZ_EXCEPTION_ENABLED to let users decide whether to trace the BizException. Users can call the SentinelConfig.setConfig to enable or disable that .
TimeoutException or other RPCException will be handled in the Filter.Listener#onError .

}

public static Boolean getDubboBizExceptionTraceEnabled(){
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(TRACE_BIZ_EXCEPTION_ENABLED));
Copy link
Member

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.

Copy link
Contributor Author

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();
Copy link
Member

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

Copy link
Contributor Author

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());
Copy link
Member

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.

Copy link
Contributor Author

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.

@CodingSinger CodingSinger force-pushed the sentinel-dubbo branch 2 times, most recently from eb73dd3 to 4853f56 Compare December 5, 2019 09:47
return String.format("Hello, %s at %s", name, LocalDateTime.now());
final AsyncContext asyncContext = RpcContext.startAsync();
new Thread(() -> {
// 如果要使用上下文,则必须要放在第一句执行
Copy link
Member

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

Copy link
Contributor Author

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

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;
Copy link
Member

Choose a reason for hiding this comment

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

Missing traceAndExit() here?

Copy link
Contributor Author

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.

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

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.

Copy link
Contributor Author

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.~

Copy link
Member

@sczyh30 sczyh30 left a comment

Choose a reason for hiding this comment

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

LGTM

@sczyh30 sczyh30 merged commit 47100e6 into alibaba:master Dec 23, 2019
@sczyh30
Copy link
Member

sczyh30 commented Dec 23, 2019

Nice work. Thanks for contributing!

@sczyh30 sczyh30 removed the to-review To review label Dec 23, 2019
@sczyh30 sczyh30 added the size/XL Indicate a PR that changes 500-999 lines. label Dec 23, 2019
hughpearse pushed a commit to hughpearse/Sentinel that referenced this pull request Jun 2, 2021
…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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/integrations Issues or PRs related to integrations with open-source components kind/enhancement Category issues or prs related to enhancement. size/XL Indicate a PR that changes 500-999 lines.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

dubbo-sentinel-support can't work well when consumer and producer in async model
4 participants