SPI adaptive extension mechanism of dubbo

Posted by matbennett on Thu, 05 Dec 2019 18:22:53 +0100

1, background

In Dubbo, many extensions are loaded through SPI mechanism, such as Protocol, Cluster, LoadBalance, etc. Sometimes, some extensions do not want to be loaded in the framework startup phase, but they want to be loaded according to the runtime parameters when the extension method is called. It sounds contradictory. If the extension is not loaded, then the extension method cannot be called (except for static methods). The extension cannot be loaded without the extension method being called. For this contradiction, Dubbo has solved it well through the adaptive expansion mechanism. The implementation logic of the adaptive extension mechanism is complex. First, Dubbo will generate code with agent function 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 is a complex process.

2, principle

For a better understanding, the following analysis is combined with an example. In the dubbbbo exposure service, there is a statement like this in the doExportUrlsFor1Protocol method of ServiceConfig class:

Exporter<?> exporter = protocol.export(wrapperInvoker);

Next, we will analyze the adaptive extension mechanism of Dubbo SPI according to this statement.

According to the source code query, the protocol object is created by the following statement:

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

According to the previous article, we know that getExtensionLoader only obtains ExtensionLoader object, so the core of adaptive extension is in getadapteextension() method:

    public T getAdaptiveExtension() {
        // Cache get instance object
        Object instance = cachedAdaptiveInstance.get();
        // Double detection
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            // Create instance object
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

In the getAdaptiveExtension method, get it from the cache first. The cache does not exist in the creation instance, and it is stored in the cache. The logic is relatively simple. Let's analyze the createAdaptiveExtension method:

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

Topics: Java Dubbo JDK