There is a pit in the Dubbo asynchronous method call

       When using dubbo to call the method asynchronously, I encountered a pit, similar to this description scenario: the client remotely calls ServiceA asynchronously, ServiceA needs to remotely synchronously call ServiceB in the process of processing the client request, and when ServiceA fetches data from ServiceB's response, What I get is null. The solution found on the Internet, thank the author for explaining the confusion, record it here, please refer to the original text: http://blog.csdn.net/windrui/article/details/52150345

     Dubbo's description of context information is as follows ( click to view  ): RpcContext is a ThreadLocal temporary state recorder. When an RPC request is received or an RPC request is initiated, the state of RpcContext will change. For example: A tunes B, B tunes C again, then on machine B, before B tunes C, RpcContext records the information of A tunes B, and after B tunes C, RpcContext records the information of B tunes C.

      Back to the problem description: The client calls ServiceA remotely and asynchronously. ServiceA needs to remotely synchronously call ServiceB in the process of processing the client request. When ServiceA fetches data from ServiceB's response, it gets null. The client calls ServiceA asynchronously, and RpcContext will add the key value of async=true to the attachments (Map structure) attribute. When ServiceA calls ServiceB synchronously, dubbo will operate the call as an asynchronous call and return ResponseFuture, then it will immediately Returns a null value (for the asynchronous call of dubbo, please click here ).

 

      The source code analysis is as follows:

      

The asynchronous call from the client to the remote method of ServiceA will be in the RpcContext (RpcContext is a temporary state recorder. When an RPC request is received or an RPC request is initiated, the state of the RpcContext will change. For example: A calls B, B then calls C, Then on machine B, before B calls C, RpcContext records the information of A calls B, after B calls C, RpcContext records the information of B calls C) add async=true to the attachments (Map structure) attribute. Key-value pairs, and also add async=true key-value pairs in the attachments (Map structure) of RpcInvocation.
After a series of Filters, the program runs to the invoke method of AbstractInvoker, pay attention to the following code segment in this method,

Map<String, String> context = RpcContext.getContext().getAttachments();
if (context != null) {
	invocation.addAttachmentsIfAbsent (context);
}

Here, the attachments in the current RpcContext will be added to the RpcInvocation that calls ServiceB. At this time, async=true has been added, and then the following code segment is executed,

if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
	invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
The above code determines whether the URL calling ServiceB contains async=true. If so, it will set async=true to the attachments of RpcInvocation, which is not included at this time.
Continue to track the code, run it into DubboInvoker, and call the doInvoke method, which has the following code segment, boolean isAsync = RpcUtils.isAsync(getUrl(), invocation), the isAsync method is specifically declared as follows,
public static boolean isAsync(URL url, Invocation inv) {
	boolean isAsync ;
	//If the setting in Java code takes precedence.
	if (Boolean.TRUE.toString().equals(inv.getAttachment(Constants.ASYNC_KEY))) {
		isAsync = true;
	} else {
		isAsync = url.getMethodParameter(getMethodName(inv), Constants.ASYNC_KEY, false);
	}
	return isAsync;
}
The above method first determines whether async=true in the attachments of RpcInvocation is established. If so, it is an asynchronous call. Otherwise, it is judged whether async=true in the request URL is established. If so, it is an asynchronous call. Otherwise, it is a synchronous call. According to the above The passed parameters, at this time the isAsync method returns true, ServiceA synchronously calls ServiceB into an asynchronous call, continue to look at the asynchronous call below, the code segment is as follows,
else if (isAsync) {
	ResponseFuture future = currentClient.request(inv, timeout) ;
	RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
	return new RpcResult();
}
Here, an RpcResult object is returned directly, with no data content, so here, the case is broken. Of course, what ServiceA wants to get the target data from the response is null. To extend, if ServiceB calls ServiceC synchronously, it can be called synchronously normally, because after ServiceA calls ServiceB, the invoke method of ConsumerContextFilter will clear the attachments, so ServiceB can call ServiceC synchronously normally.

    

    Solution: For the above problem, there are three solutions:    

1. The method is called twice
Where ServiceA calls ServiceB, write the same call twice. The principle of this method is the same as ServiceB calling ServiceC, that is, to clear the attachments. This method is the simplest, but for those who don’t know, this piece of business code is written repeatedly and will Accidentally deleted, and from the perspective of writing code, this is very tasteless, so it is not recommended.
2. Modify Dubbo source code
Modify the line 137 of AbstractInvoker, and change it to perform the actual assignment to async every time,
boolean isAsync = getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false);
invocation.setAttachment(Constants.ASYNC_KEY, String.valueOf(isAsync));
3. Custom Filter
Implement com.alibaba.dubbo.rpc.Filter, clear this async in RpcContext,

@Activate(group = {Constants.PROVIDER})
public class AsyncFilter implements Filter {
	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		RpcContext.getContext().getAttachments().remove(Constants.ASYNC_KEY);
		return invoker.invoke(invocation);
	}
}
At the same time, add the com.alibaba.dubbo.rpc.Filter file under src/main/resources/META-INF/dubbo/. The content file is as follows:

asyncFilter=com.abc.filter.AsyncFilter

 

      

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326307405&siteId=291194637