【Dubbo】1.容器启动

容器启动

Container

官方文档介绍说:Dubbo的服务容器负责启动加载运行服务。
这里的Container指的是Dubbo定义的接口:

package org.apache.dubbo.container;
import org.apache.dubbo.common.extension.SPI;

/**
 * Container. (SPI, Singleton, ThreadSafe)
 */
@SPI("spring")
public interface Container {
    /**
     * start method to load the container.
     */
    void start();
    /**
     * stop method to unload the container.
     */
    void stop();
}

Container的两个方法start(),stop()比较好理解,@SPI("spring")这段代码是什么意思?

Dubbo是一款Java开源框架,它的设计理念就是:微核插件,作者把自己也当作扩展者,这样做保证了框架的可持续性、稳定性,实现了对第三方插件一视同仁。

SPI的全称为Service Provider Interface,这里的SPIDubbo自己实现的一套插件扩展机制,和Java SPI大致相同,我们先来了解下Java SPI:

package com.kaiyuan.qyd.topic.dubbo.spi;
// 定义四季接口
public interface FourSeasonsSpiService {
    void print();
}
public class AutumnSpiService implements FourSeasonsSpiService {
    public void print() {System.out.println("秋");}
}
// 另外三个省略...

public static void main(String[] args) {
        // 从META-INF/services/中寻找同包名文件获取接口实现
        ServiceLoader<FourSeasonsSpiService> load = ServiceLoader.load(FourSeasonsSpiService.class);
        load.forEach(it->it.print());// 按照文件顺序打印 春夏秋冬
}

SPI定义文件描述:

// 文件路径:META-INF/services/com.kaiyuan.qyd.topic.dubbo.spi.FourSeasonsSpiService
com.kaiyuan.qyd.topic.dubbo.spi.SpringSpiService
com.kaiyuan.qyd.topic.dubbo.spi.SummerSpiService
com.kaiyuan.qyd.topic.dubbo.spi.AutumnSpiService
com.kaiyuan.qyd.topic.dubbo.spi.WinterSpiService

看完上面的示例我们对Java SPI的打开方式做个小结:

1.创建业务接口以及方法

2.在META-INF/services/下创立定义文件,名称为包路径全名

3.在定义文件中按需写入该接口实现类

4.使用ServiceLoader.load()获取接口列表

5.按需调用接口方法

6.用户要扩展只需要写实现类并修改定义文件即可


Dubbo SPIJava SPI思想上重新创造扩展了注解功能,允许使用key=>value的形势按需选择接口实现类,@SPI("spring")的意思是使用SpringContainer作为默认Container实现类(本章只需要了解,后面有专题介绍)

// 文件路径: META-INF/dubbo/internal/org.apache.dubbo.container.Container
spring=org.apache.dubbo.container.spring.SpringContainer
log4j=org.apache.dubbo.container.log4j.Log4jContainer
logback=org.apache.dubbo.container.logback.LogbackContainer

SpringContainer的代码很简单,实现父类start()/stop()之后启动/关闭Spring容器。现在的问题是:

SpringContainer在哪启动?

这个问题很好找,因为Container的下铺就是Main,这俩类都在org.apache.dubbo.container包目录结构里

Main

这个类就是SpringContainer的调用者,代码逻辑如下

1.获取Container默认实现:通过ExtensionLoader.getExtensionLoader()

2.一些参数验证,因为允许应用程序通过参数控制多容器

3.创建SHUTDOWN_HOOK停机逻辑,内部调用了Container.stop()

4.依次执行Container.start()启动容器

5.静态全局锁等待,直到Condition#STOP停机信号被触发(可能永远等待)

package org.apache.dubbo.container;
import org.apache.dubbo.common.extension.ExtensionLoader;
import ...
/* 代码有修剪 */
public class Main {
    // SPI获取Container.class的默认实现,这里是SpringContainer实现类
    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
    private static final ReentrantLock LOCK = new ReentrantLock();
    // 条件锁,唤醒受代码控制
    private static final Condition STOP = LOCK.newCondition();
    public static void main(String[] args) {
        try {
            // 省略一些参数验证,因为dubbo允许应用程序通过参数控制多容器
            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i++) {
                // 默认只有一个容器SpringContainer
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
               // SHUTDOWN_HOOK停机代码
            }
            for (Container container : containers) {
                container.start();// 依次执行start()
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
        } catch (RuntimeException e) {
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        try {
            LOCK.lock(); // 静态可重入锁
            STOP.await();// 挂起等待停机信号,当前释放lock锁
        } catch (InterruptedException e) {
            logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
        } finally {
            LOCK.unlock();
        }
    }

}

SHUTDOWN_HOOK

这段代码说明如果SHUTDOWN_HOOK_KEYfalse那么Condition#STOP永远不会触发,也就说明Main()永远等待。

if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
    // 监听Shutdown事件
    Runtime.getRuntime().addShutdownHook(new Thread("dubbo-container-shutdown-hook") {
        @Override
        public void run() {
            for (Container container : containers) {
                try {
                    container.stop();
                    logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                } catch (Throwable t) {
                    logger.error(t.getMessage(), t);
                }
                try {
                    LOCK.lock();   // 可重入锁
                    STOP.signal(); // 等container.stop()执行完毕触发停机信号
                } finally {
                    LOCK.unlock();
                }
            }
        }
    });
}

小结:
以上是Dubbo的容器启动代码分析,但是我们完全可以自己从Spring启动Dubbo程序,无需依赖jar包中的Main()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。