preface

The last article briefly introduced the basic concepts of SPI, but Dubbo has made some improvements to the JDK’s SPI

  • The JDK’s standard SPI instantiates all implementations of extension points at once, which is time-consuming to initialize if there is an extension implementation, but wasteful of resources to load without it.
  • If the extension point fails to load, you will not even get the name of the extension point. Such as: If the RubyScriptEngine class fails to load because the jruby.jar on which RubyScriptEngine depends does not exist, the class fails to load because the jruby.jar on which RubyScriptEngine depends does not exist. This failure cause is eaten up and does not correspond to Ruby. When a user executes a Ruby script, it will report that Ruby is not supported, rather than the actual failure cause.
  • Added support for extension points IoC and AOP, where one extension point can directly setter for injection of other extension points.

According to the reading comprehension of primary school Chinese, it is not difficult to generalize that the performance has been improved and the function has been increased. Many friends like to ask, read the source code from where to start, to prepare something. If only a rough reading of the source code, master the general idea, in fact, with primary school language reading comprehension and look at the picture to write homework is about the same (can see this are fully qualified for this condition, so do not have any fear). But to understand the idea, to know the details well, and even to write a better framework, then there are four words -> lifelong learning.(For example, now follow the public account of Fat Dynasty, exchange and discuss together, lifelong learning will start immediately.)

For reading the source code, of course, there are still some small skills, as the saying goes,” technology is not pressure body “, we first look at a paragraph

Some fertilizer encountered a bug, need to introduce multithreading, the result of the introduction of multithreading, unexpectedly there are two bugs

As you can see, multithreading is a real headache, so I’ll show you some tricks later, such as

  • Debugging multithreaded code (hands-on, not theoretical)

  • How to view proxy object source code (hands-on practice, not theory)

How does dubbo’s improved SPI improve performance and add features? That’s what this article will cover.

We interrupt the interview questions

  • Now that you know something about SPI, is there any difference between dubbo’s SPI and the JDK’s SPI? If so, what’s the difference?

  • You said you read Dubbo’s source code, but can you briefly tell me what details of his design make you feel clever? (High degree of differentiation)

The concept stage

The extension point mechanism of Dubbo involves many knowledge points, which is also a difficult part of Dubbo. Like the previous Cluster fault tolerance keywords Cluster, Directory, Router and LoadBalance, this extension point mechanism also has several keywords,SPI, Adaptive and Activ ate. We’ll talk about this one by one, and then we’ll sum it up.

Straight into the theme

Improve performance

What’s the most obvious way to improve performance? In fact, this is the same as the middle and high school political questions, there is a universal formula, that is “cache “. So no matter what the interview asks you (Android,iOS,Web front-end,Java, etc…) If it’s related to improving performance, it’s ok to answer in the cache direction. (Of course, if you answer in the cache direction, you won’t get a 0, but you won’t get a full score just by answering in the cache direction.) So if you compare it to the JDK’s SPI, you can have the following points

1. From the perspective of “universal formula” analysis, increase cache

Because some of my friends feedback that they like the form of the code, so explain in the notes

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)// Extend point type non-null judgment
        throw new IllegalArgumentException("Extension type == null");
    if(! type.isInterface()) {// Extension point type can only be interface
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    if(! withExtensionAnnotation(type)) {// Spi annotations need to be added, otherwise an exception is thrown
        throw new IllegalArgumentException("Extension type(" + type + 
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }
    // get it from cache EXTENSION_LOADERS, or add it to cache if it does not exist
    // For each extension, there is one and only one ExtensionLoader
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

private ExtensionLoader(Class
        type) {
    this.type = type;
    // There will be recursive calls, the objectFactory of ExtensionFactory is null, everything else is AdaptiveExtensionFactory
    / / AdaptiveExtensionFactory SpiExtensionFactory of factories, SpringExtensionFactory
    //getAdaptiveExtension() this gets an extension decorator object. Details are in the next section
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
Copy the code

The following is the cache extension point object

public T getExtension(String name) {
	if (name == null || name.length() == 0)
	    throw new IllegalArgumentException("Extension name == null");
	if ("true".equals(name)) {
	    return getDefaultExtension();
	}
	Holder<Object> holder = cachedInstances.get(name);
	if (holder == null) {
	    cachedInstances.putIfAbsent(name, new Holder<Object>());
	    holder = cachedInstances.get(name);
	}
	Object instance = holder.get();
	if (instance == null) {
	    synchronized (holder) {
            instance = holder.get();
            if (instance == null) { instance = createExtension(name); holder.set(instance); }}}return (T) instance;
}
Copy the code

Why do I have to separate this out? Dubbo is no different from many people who think that caching is all about checking to see if it exists and adding it if it doesn’t. I don’t read the source code. But if you pay attention, you’ll see that it does a good job of detail.

Type on the blackboard and underline

As I emphasized in the last article [Talking about parental delegation from the Dubbo kernel], whatever you do, make a difference. One of the easiest areas of differentiation in Java is the JVM and the concurrent distribution of packages. Since we covered JVMS in the last article (parental delegation), this article will cover and send packages. Take a closer look at the double-checked locking code above

//double-checked locking
Object instance = holder.get();
if (instance == null) {
    synchronized (holder) {
        instance = holder.get();
        if (instance == null) { instance = createExtension(name); holder.set(instance); }}}Copy the code
public class Holder<T> {
    
    private volatile T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get(a) {
        returnvalue; }}Copy the code

Why is the volatile keyword used here? Look at the source code is more important to see these details, the devil are hidden in the details! DoubleCheck Singleton reorder, so run this through your browser. Then read through the first page of results, and you’ll know that this code isn’t as simple as you thought it would be. This is one of the reasons I often say that without middleware development, it is difficult to have a deep understanding of how to package.

2. Analyze from the perspective of annotations

Since it’s different from SPI, and dubbo has the @SPI annotation, let’s follow the annotation and see what clues we can get.

If you had used dubbo in 2015, you would have noticed the @extension annotation, but since then it has been replaced with @spi because of its widespread meaning.

@SPI("javassist")
public interface Compiler {
	/ / to omit...
}
Copy the code
publicClass<? > compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER;// copy reference
    if(name ! =null && name.length() > 0) {
        compiler = loader.getExtension(name);
    } else {
        compiler = loader.getDefaultExtension();
    }
    return compiler.compile(code, classLoader);
}
Copy the code
/ / com.alibaba.dubbo.common.compiler.Com piler file configuration 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
Copy the code

From the above two parts of code and configuration files, it is not difficult to analyze two points (if you are not familiar with SPI, please read the previous SPI (I) first, because you cannot analyze it if the foundation is not solid and the earth is shaking).

  • The JDKspiTo get the specified SPI object,dubbo gets it with the specified key, using a for loop and then an if judgment
// Returns an extension of the specified name
public T getExtension(String name){}
Copy the code
  • The JDKspiDefault values are not supported, and Dubbo has added a default value design
// @spi ("javassist") represents the default SPI object. For example, Compiler uses Javassist by default and can pass
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
compiler = loader.getDefaultExtension();
According to the configuration, that is
//com.alibaba.dubbo.common.compiler.support.JavassistCompiler
Copy the code

Adding features

The added functionality, as documented,spi adds IoC, AOP

AOP this is a platitude topic, talking about AOP we are most likely to think of Spring, even because AOP is commonly used in the transaction scenario, there are even many people think that AOP is a transaction. So fertilizer suggests beginners to learn AOP route roughly as follows:

// This step by step evolution process, is the biggest harvest decorator design pattern -> static proxy ->JDK, Cglib, Javassist pros and cons comparison ->AOP source codeCopy the code

Write in the last

Fertilizer toward is a focus on principle, source code, development skills of technical public number, number of original thematic source code analysis, real scene source code principle combat (key).Scan the following QR codePay attention to fat, let should build rocket you, no longer screw!