一直信奉世間萬事萬物的執行都有各自的規則。程式設計也有程式設計的原則,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;
}
複製程式碼
META-INF.dubbo.internal下有個配置檔案,裡面的配置是
spring=com.alibaba.dubbo.container.spring.SpringContainer
複製程式碼
因此可以通過"spring"擴充套件實現的類是com.alibaba.dubbo.container.spring.SpringContainer。
這裡只是簡單分析Dubbo的一種啟動方式,不是很深入,拋磚引玉,大家可以繼續深入的研究一下。