一直信奉世间万事万物的运行都有各自的规则。程序设计也有程序设计的原则,Linux有Linux的设计原则,Spring的设计原则是IoC和AOP。因此在阅读一个框架源码时要抓住这个框架设计的原则,这样才能容易理解。
Dubbo是阿里巴巴公司开源的一个分布式服务框架,主要功能有:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。
- Container 服务运行容器
- Provider 暴露服务的服务提供方
- Registry 服务注册与发现的注册中心
- Consumer 调用远程服务的服务消费方
- Monitor 统计服务的调用次数和调用时间的监控中心
我们按Dubbo架构的模块各个击破。
- Container Dubbo服务的启动都是从启动容器开始的,Dubbo服务的启动有三种方法: 1.使用Servlet容器运行; 2.自建Main方法运行(Spring容器); 3.使用Dubbo提供的Main方法类运行(Spring容器);
一般建议使用Dubbo提供的Main方法类运行,能够优雅关机。Main方法类里也是启动一个容器,这里就设计到SPI扩展机制了。首先咱们说说是SPI扩展机制。SPI(Service Provider Interface)是JDK内置的一直服务发现机制。比如JDBC中就是通过SPI机制提供给各数据库厂商调用实现接口。其实就是提供一个接口,在运行时动态添加实现。 下面我们说说Dubbo中怎么实现SPI的。在com.alibaba.dubbo.container.Main类中启动main方法。我会在代码注释中打上标识“① ②“可以根据标识定位查看具体的源码解析。
//com.alibaba.dubbo.container.Main
public class Main {
public static final String CONTAINER_KEY = "dubbo.container";
public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";
private static final Logger logger = LoggerFactory.getLogger(Main.class);
//创建ExtensionLoader对象,SPI扩展机制的实现就在这个类里。①解析ExtensionLoader.getExtensionLoader
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 {
//查看是否有入参
if (args == null || args.length == 0) {
//没有的话就通过ConfigUtils.getProperty()方法去获取默认定的config②解析ExtensionLoader.getDefaultExtensionName
String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
args = Constants.COMMA_SPLIT_PATTERN.split(config);
}
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i++) {
//通过上面获取的args从loader中获取上面已经加载的容器对象。比如这里是通过“spring”获取到容器对象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))) {
Runtime.getRuntime().addShutdownHook(new Thread() {
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();
} finally {
LOCK.unlock();
}
}
}
});
}
for (Container container : containers) {
//启动容器,这里就是spring容器
container.start();
logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
}
System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
} catch (RuntimeException e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
System.exit(1);
}
try {
LOCK.lock();
STOP.await();
} catch (InterruptedException e) {
logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
} finally {
LOCK.unlock();
}
}
}
复制代码
①解析ExtensionLoader.getExtensionLoader Main类中初始化了一个ExtensionLoader对象,这个对象是通过 ExtensionLoader.getExtensionLoader(Container.class)初始化的。SPI机制的实现就在ExtensionLoader中体现的。
//com.alibaba.dubbo.common.extension.ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//不能为空
if (type == null)
throw new IllegalArgumentException("Extension type == null");
//type要是一个接口
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//type上需要有SPI注解,比如Container上就有注解@SPI("spring"),有了这个注解才能实现SPI机制动态实现
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//EXTENSION_LOADERS是一个ConcurrentHashMap<Class<?>, ExtensionLoader<?>>()
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//创建一个type的ExtensionLoader
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
复制代码
②解析ExtensionLoader.getDefaultExtensionName
//获取默认的扩展名
public String getDefaultExtensionName() {
getExtensionClasses();
//cachedDefaultName是个全局变量,在loadExtensionClasses方法中赋值
return cachedDefaultName;
}
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//加载扩展类
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
//获取默认的注解,这里是@SPI("spring")
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//获取注解值这里是“spring”
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
//正则匹配注解值,通过“,”分隔符
String[] names = NAME_SEPARATOR.split(value);
//如果注解的字段大于1,就抛出异常,也就是说@SPI注解中只可以有一个值
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
//给cacheDefaultName赋值,这里赋值为“spring”
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//加载不同路径下的类放到extensionClasses,具体细节可以看loadFile方法
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
复制代码
spring=com.alibaba.dubbo.container.spring.SpringContainer
复制代码
因此可以通过"spring"扩展实现的类是com.alibaba.dubbo.container.spring.SpringContainer。
这里只是简单分析Dubbo的一种启动方式,不是很深入,抛砖引玉,大家可以继续深入的研究一下。