Dubbo source depth - and the benefits of using the SPI

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/red_sheeps/article/details/100854469

background

I believe the source of the students read Dubbo in Dubbo should see many of the interface has a @SPIcomment, the author is no exception, but did not know specifically what this annotation, in order to solve a problem, how to use? A simple search online, the Chinese name: service delivery interfaces, see the following figure (from Baidu Encyclopedia).
Here Insert Picture Description
Perhaps because dubbothe function itself is powerful, so I only know that can dubbobe customized to achieve certain policies, such as load balancing, serialization, thread pool type, etc., but has not yet officially on-line environments. Take time to study at taking advantage of the holidays, the next record, want to be useful.

Sample Code

The following codes are validated through local, purely hand to knock, the specific implementation see Glory's Github
Note: The test project to build spring-boot+ the Spring-the Boot-Dubbo

Verify ideas

As the figure belongs, said that @SPIto achieve a particular service, it would be simple to achieve, the most familiar than load balancing ( LoadBalance) strategy, the local start two providerdifferent ports, through consumerto determine access parameters specified in the a provider.

Start provider

Extremely simple codes, the code follows the frame
Here Insert Picture Description

import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// 指定版本和分组
@Service(version = "1.0.0",group = "glory")
public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(Integer port) {
        logger.info("Hello " + port + " request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello ,"+port+" response from provider: " + RpcContext.getContext().getLocalAddress();
    }

}

The following is the application.ymlconfiguration file

server:
  port: 8083
dubbo:
  application:
    name: dubbo-common-provider
  scan:
    base-packages: com.redsun.rpc.dubbo
  protocol:
    name: dubbo
    port: 12345
  registry:
    address: zookeeper://127.0.0.1:2181

Also note that you need to specify a different port at startup, whether in not start.
Here Insert Picture Description
At this point you can start the normal two local application, start effect is as follows:
Here Insert Picture Description

Start consumer

consumerThe code is also very simple framework
Here Insert Picture Description
surely we can guess guessed, GloryLoadBalanceis a kind of load balancing strategy to achieve their own, to choose the parameters passed through the frontinvoker

public class GloryLoadBalance implements LoadBalance {

    private static final Logger logger = LoggerFactory.getLogger(GloryLoadBalance.class);

    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        Integer port = (Integer) invocation.getArguments()[0];// 前端传入的参数
        logger.info("前端传入端口为:" + port);
        // java8的流式编程,有兴趣的同学可以研究下,后续会再专门写一篇
        Invoker<T> invokerRet = invokers.stream().filter(invoker -> invoker.getUrl().getPort() == port).findFirst().get();
        return invokerRet == null ? invokers.get(0) : invokerRet;
    }
}

Here also wrote a simple controller, easy to change the dynamic parameters

@RestController
@RequestMapping("/admin")
public class AdminController {
    private static final Logger logger = LoggerFactory.getLogger(AdminController.class);

    @Reference(version = "1.0.0",group = "glory", loadbalance = "glory")
    private DemoService demoService;

    @RequestMapping("/invoke")
    public String invoke(@RequestParam(name = "port") Integer port) {
        logger.info("invoke method be invoked!port = " + port);
        return demoService.sayHello(port);
    }
}

Of course, the final step will be, and most importantly, the META-INF.dubbo.internaladded profile directory, following the equal sign in front of the gloryfact that you have configured the loadbalancekey, and if the path is wrong or not configured, will still get to the default implementationrandom

glory=com.redsun.rpc.dubbo.loadbalance.GloryLoadBalance

dubboWill load META-INF.dubbo.internalall the configuration information in the directory, the dubbodirectory will be a lot of default implementation
Here Insert Picture Description

postman calls

The following two graphs tested to meet expectations.
Here Insert Picture Description
Here Insert Picture Description

Source

Here it's time to turn the source code, and I am here to talk about simple look at the source code of that experience.

  • Do not tangle in the implementation of each method, usually debug with, it is easy with the halo, and finally lost interest
  • Learn to read notes, in particular, method names, class names, variable names, source code is different from the usual I have written, since open source, that is accessible for all, it notes, named often written in very great detail specification, the English do not know ? Translation, more often met
  • Remember when debug with several core classes, after reading the entire call chain, the structure of the code you have to have a rough sort of cognitive next (really do not want to see, Baidu also, I often do it, then and then look at myself, under certification)

load

Extension Loader # getExtensionClasses

    // synchronized in getExtensionClasses
    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }

Here Insert Picture Description

    /**
     * put clazz in extensionClasses
     */
    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) {
        Class<?> c = extensionClasses.get(name);
        if (c == null) {
            // 将扩展类GloryLoadBalance存入map中,最终会将默认提供的几种都存入map中
            extensionClasses.put(name, clazz);
        } else if (c != clazz) {
            throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName());
        }
    }

Load class will eventually achieve good ReferenceConfigload balancing parameter is set toglory
Here Insert Picture Description

run

AbstractClusterInvoker#invoke

In the consumerlaunch invokewill be to create an instance of selective according to config key time

    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();

        // binding attachments into invocation.
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }

        List<Invoker<T>> invokers = list(invocation);
        // 初始化加载负载均衡类
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

Extension Loader # getExtensionLoader

Each of the META-INF.dubbo.internalfiles in the directory are ExtensionLoaderobjects stored in a static class variable EXTENSION_LOADERSin

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        // 此处判断是否以SPI注解修饰
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        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;
    }

Extension Loader # getExtension

If this time is empty, an example of the use of information by loading the above-Class newInstance, the code is relatively simple, interested can talk about.
Here Insert Picture Description

to sum up

The above should be seen, if you want to expand the use of dubbo follow-up will be very simple, to increase an implementation class (implement the corresponding interface), and then in META-INFadding a configuration file directory, key corresponds to the good can be done. Serialization can also be in accordance with their wishes.
When the code can be seen with some other extension classes are not used, dubbo are loaded into a Class object, it can be considered a little bit of small flaws it.

Guess you like

Origin blog.csdn.net/red_sheeps/article/details/100854469