ステップSシリーズの--dubbo非同期呼出伝達性により、ネスト呼び出しがnull値のbugに戻る。
4291 ワード
一、現象
三つのアプリケーションがあります。serviceA、serviceB、serviceCは、消費が乱れていないことを確保する前提で、(いずれも単一のサービスプロバイダだけです。)その呼び出し関係は
ただし、Bが2回目の呼び出しCは正常に戻ります。
二、問題の根源を探します。ソースコードです。
1.私たちの考え方を調べる
現象はdubboの非同期呼び出しによって、そしてサービスプロバイダ内部にdubboの入れ子コールがあります。==だから、dubboの内部入れ子コールに非同期の伝達性があるかどうかを確認したいです。伝達するからには、コンテキスト環境が必要です。さらに、dubboのコンテキスト環境==RpcContext==を思い出しました。
2.予備知識:RpcContect概要
RpcContectは、ThreadLocalの一時状態レコーダであり、RPC要求を受信したり、RPC要求を開始したりすると、RpcContectの状態は変化します。
例えば、A調B、BがCを再調整すると、Bマシンでは、B調Cの前に、==RpcContectがA調Bの情報=、B調Cの後、RpcContectがB調Cの情報を記録します。
我々はdubboのメソッド呼び出しを知っています。すべてinvokerのエージェントによって呼び出されました。私たちはAbstractInvokerを見つけました。下の階のinvoke方法を調べてみます。ソースは以下の通りです。
ここでFilter ConsmerContertextFilterのソースコードは以下の通りです。
三、解決方法
問題発生の原因を分析した後、dubboソースの状況を修正しないで、いくつかの処理方法があります。 serviceBを同期呼出しに変更します。業務上に非同期呼出しが必要な場合、以下の2つの処理方法 があります。 serviceBの方法は、リターン値を必要とせずに、Onewayの方式を採用することができる(消費者側にdubboを配置する:methodでreturn=false) 。には戻り値があり、非同期が必要であり、最も簡単な方法は、実現においてスレッドプールを使用してトラフィックを実行することである。 Provider端のFilterを追加して、filterチェーンの最後に、方法を実行する前に、atachmentのasyncフラグをクリアすることを保証します。同じ効果を達成することもできます。
転載は出典アブの夏を明記してください。
三つのアプリケーションがあります。serviceA、serviceB、serviceCは、消費が乱れていないことを確保する前提で、(いずれも単一のサービスプロバイダだけです。)その呼び出し関係は
sequenceDiagram
serviceA-->>serviceB:serviceA serviceB
serviceB->>serviceC:serviceB serviceC
serviceC->>serviceB: true
serviceA dubb消費serviceBは非同期消費async=「true」に構成されています。構成は以下の通りです。// serviceA serviceB
// serviceB serviceC
しかし、上記配置後、実際の呼び出し関係は下図になります。sequenceDiagram
serviceA-->>serviceB:serviceA serviceB
serviceB-->>serviceC:serviceB serviceC
serviceC->>serviceB: null, boolean false
上述したように、B−Cは、dubbo非同期構成の伝達性により非同期呼出しとなり、結果としてnullに戻り、所望の同期呼出結果が異常となり、ただし、Bが2回目の呼び出しCは正常に戻ります。
二、問題の根源を探します。ソースコードです。
1.私たちの考え方を調べる
現象はdubboの非同期呼び出しによって、そしてサービスプロバイダ内部にdubboの入れ子コールがあります。==だから、dubboの内部入れ子コールに非同期の伝達性があるかどうかを確認したいです。伝達するからには、コンテキスト環境が必要です。さらに、dubboのコンテキスト環境==RpcContext==を思い出しました。
2.予備知識:RpcContect概要
RpcContectは、ThreadLocalの一時状態レコーダであり、RPC要求を受信したり、RPC要求を開始したりすると、RpcContectの状態は変化します。
例えば、A調B、BがCを再調整すると、Bマシンでは、B調Cの前に、==RpcContectがA調Bの情報=、B調Cの後、RpcContectがB調Cの情報を記録します。
我々はdubboのメソッド呼び出しを知っています。すべてinvokerのエージェントによって呼び出されました。私たちはAbstractInvokerを見つけました。下の階のinvoke方法を調べてみます。ソースは以下の通りです。
public Result invoke(Invocation inv) throws RpcException {
···
// serviceB-->serviceC , RpcContext, RpcContext , , context serviceA->serviceB , ,
Map context = RpcContext.getContext().getAttachments();
if (context != null) {
invocation.addAttachmentsIfAbsent(context);
}
// ,
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
//
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
//
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
return doInvoke(invocation);
} catch (InvocationTargetException e) { ···
} catch (RpcException e) {
···
} catch (Throwable e) {
···
}
}
3.上にはまだ小さな問題がありますが、serviceBがServiceCを2回目に呼び出すと、ある方法が正常に戻ってきます。これはなぜですか?ここでFilter ConsmerContertextFilterのソースコードは以下の通りです。
@Activate(group = Constants.CONSUMER, order = -10000)
public class ConsumerContextFilter implements Filter {
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
RpcContext.getContext()
.setInvoker(invoker)
.setInvocation(invocation)
.setLocalAddress(NetUtils.getLocalHost(), 0)
.setRemoteAddress(invoker.getUrl().getHost(),
invoker.getUrl().getPort());
if (invocation instanceof RpcInvocation) {
((RpcInvocation)invocation).setInvoker(invoker);
}
try {
return invoker.invoke(invocation);
} finally {
// ① ,
RpcContext.getContext().clearAttachments();
}
}
}
==上のコードのように、serviceBが初めてServiceCを呼び出したときに、consumerのfilter chainに、ConsmerContertext Filterがあります。呼び出しが終了したらRpcContact.get Contxt()を実行します。clearAttachments()方法で、RpcContxt=をクリアします。私たちの疑問は説明されます。三、解決方法
問題発生の原因を分析した後、dubboソースの状況を修正しないで、いくつかの処理方法があります。
転載は出典アブの夏を明記してください。