Two years ago, I read the Dubbo source code and saw the logic for GenericService. I didn’t know why it was there and couldn’t figure it out so I skipped it.

It was two years later that I came back to Dubbo and understood the logic of the generalization call because it was used by the company’s internal business projects. The author is responsible for the usability transformation of this function, which also involves the transformation of the function of generalization call.

I learned from my colleagues that generalized calls are mainly used for cross-business calls, such as other business items calling the payment interface, or payment business calling other business interfaces.

Why and what is a generalized call?

For example, the go language does not currently support generics. To implement a List collection type, you need to implement a class for int, string, etc. (Just for example, go doesn’t support generics yet, but you don’t need to do that either.) While Java supports generics, a List class is common.

Dubbo’s generalization call functionality is similar to the generics functionality provided by the Java language for general purpose purposes.

So why do you need to generalize the call function?

The cross-business invocation scenario was also mentioned in my previous article. For cross-business invocation scenarios, interface interactions between two businesses should not depend on each other’s code, that is, they should not require each other to provide a JAR package.

Using Dubbo requires the creation of an interface project in which all business interfaces are declared, and consumers and providers need to rely on the packaged JAR of this project. But across businesses, do we need to expose all interfaces to other businesses? Obviously not.

Without relying on the jar package provided by the other party, it is impossible to call directly through the interface. In this case, RPC call can be implemented with the generalization call function.

Since there is no JAR package introduced, no interface class, and no request/response DTO class, the generalization call requires the consumer to specify the target interface class name, method name, method parameter type, and convert THE POJO parameter to map, and the provider response result to map if it is a POJO.

Here is the use of the official document: dubbo.apache.org/zh/docsv2.7…

Native generalization interface:

Generalization call function transformation

Why retrofit?

Because we’ve changed the way Dubbo is used, the new way is a little different from the original way, by specifying that the return value must be a Result and using code and message instead of the original throw exception. In addition, the asynchronous invocation is optimized to encapsulate the difference logic between synchronous and asynchronous invocation, so that it is not aware of the user.

In order to continue to provide support for the generalization call function, we abandoned the native generalization call interface and implemented the generalization call ourselves. However, there is still no need to modify the Dubbo source code, thanks to the extensibility of Dubbo, only through extension points to achieve.

The modified GenericService interface is declared as follows.

class GenericInvokeParams {        
    private String serviceName;        
    private String methodName;        
    private String[] paramTypes;        
    private Object[] request;
}
public interface GenericService {      
    Result<Object> invoke(InvokeParams params);
}
Copy the code

We also added the serviceName parameter to Invoker so that a GenericService proxy instance can call any interface.

The modified generalization call uses demo as follows:

Taking HTTP as an example, the implementation principle is summarized as follows:

GenericService provider:

For a generic call, the URI is the name of the interface class. For a generic call, you need to add the suffix “/generic” because the processing logic of a generic call is different from that of a generic call.

When a service provider receives a generalization call request, it needs to turn the generalization call into a normal call.

GenericService Consumer:

When the request is initiated, the serviceName parameter is used as the URI of the normal call and the “/generic” suffix is concatenated as the URI of the generalized call.

Get the service provider based on the serviceName and make the remote invocation.

Technical points involved in the implementation: dynamic proxy, ProxyFactory extension point, Protocol extension point.