Finally, I understand DUBBO SPI

Posted by jokkis on Sat, 10 Aug 2019 13:08:05 +0200

I. DUBBO SPI usage scenarios

Dynamic loading of corresponding implementation classes according to configuration, SPI extensions have many classes:

II. dubbo spi extension point

(1) dubbo SPI Foundation:

Get the class name according to the key in META-INF file, and then get the instance of the class.
The corresponding method is getExtension(), for example:

 		LoadBalance loadBalance = 
            ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("random");
        //Get an instance of RadomLoadBalance class
        System.out.println(loadBalance);

(2) DUBBO SPI enhancement and adaptive extension mechanism:

The corresponding method is getAdaptive Extension (), for example:

	//What we get here is not a specific protocol implementation class, but an Adaptive Address class.
        Protocol protocol = 
                ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
        //Calling remote services through the Adaptive class can be adapted to the specific protocol implementation class according to the protocol of url to complete the remote protocol invocation.
        protocol.refer(type, url);
				

What is self-adaptation? An explanation from the official website

Sometimes, some extensions do not want to be loaded during the framework startup phase, but rather want to be loaded according to runtime parameters when the extensions method is called. That sounds contradictory. If the extension is not loaded, then the extension method cannot be invoked (except for static methods). Extension methods cannot be loaded without being invoked. To solve this contradiction, Dubbo has solved it well through self-adaptive expansion mechanism. The implementation logic of the adaptive extension mechanism is complex. First, Dubbo generates proxy code for the extension interface. Then compile the code through javassist or jdk to get the Class class. Finally, the proxy class is created by reflection, which makes the whole process more complicated.

Let's take another example to explain, for example: Dubbo provides many protocols: dubbo, rmi, http, web service, etc. But in practice, we may only choose one protocol. Suppose we choose to use RMI protocol, Dubbo will load RMI protocol implementation classes at run time (and cache them after loading once), and other protocol implementation classes such as http, web service will not load unless they are used. This is adaptive loading. The Adaptive class in Dubbo does this loading action.

Adaptive classes are loaded in two ways:

(1) Static state

Static Adaptive classes, i.e. Adaptive classes, exist in projects and do not need to be dynamically generated.
When the adaptive extension point is obtained through getAdaptive Extension (), all the classes under META-INF with the @Adaptive annotation are searched.

Give an example:
The Adaptive class of Compiler is: AdaptiveCompiler

The META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler.Compiler file is as follows:

adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

When the following code is executed, an instance of the @Adaptive annotated AdptiveCompiler class is returned directly:

Compiler compiler = 
                ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();

(2) Dynamics

Dynamic Adaptive classes are generated in real time at the time of invocation, bytecodes are generated at the time of running, and classes are created by dynamic reflection.

Example: Get the Adaptive class of the Protocol interface

First of all, under METTA-INF, in com.alibaba.dubbo.rpc.Protocol, all classes do not have @Adaptive annotation. At this time, the bytecode of Adaptive is generated by code.

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
....... Wait

Detailed methods for generating adaive class bytecodes are available for reference: Extension Loader. create Adaptive Extension ClassCode ()

You can see that the generated class name is the interface name $Adapative and that the method of the @Adaptive annotation is overridden
In this Adaptive class, we can boldly guess that in fact, it is based on a keyword to obtain the corresponding implementation class. Where does this keyword come from? We know that dubbo is invoked through the URL. The new timeout parameter and the protocol invoked will exist in the form of parameters in the URL. That's right. It's in the URL that you pick up __________.

Let's look at the generated code:

We copy the code and format it:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}

	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}

	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)
			throws com.alibaba.dubbo.rpc.RpcException {
		if (arg1 == null)
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}

	public com.alibaba.dubbo.rpc.Exporter export(
			com.alibaba.dubbo.rpc.Invoker arg0)
			throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
		com.alibaba.dubbo.common.URL url = arg0.getUrl();
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.export(arg0);
	}
}

The Protocol class has two @Adaptive methods: export()-"service exposure; refer()-" service invocation;
It is also obvious from these two methods that the protocol is obtained from the url, and then the specific implementation class is obtained according to the protocol name.

Finally, a mind map is used to summarize:

Topics: Dubbo JDK Java