服務容器是 一個 standalone 的啓動程序,由於後臺服務不須要 Tomcat 或 JBoss 等 Web 容器的功能,若是硬要用 Web 容器去加載服務提供方,增長複雜性,也浪費資源。java
服務容器 只是一個簡單的 Main 方法,並加載一個簡單的 Spring 容器,用於暴露服務。spring
服務容器的加載內容能夠擴展,內置了 spring, jetty, log4j, logback等加載,可經過容器擴展點進行擴展。配置配在 java 命令的 -Ddubbo.container
參數或者 dubbo.properties
中。bash
- 自動加載 META-INF/spring 目錄下的全部 Spring 配置。
- 配置 spring 配置加載位置(配在java命令-D參數或者dubbo.properties中):
dubbo.container=log4j,spring dubbo.spring.config=classpath*:META-INF/spring/*.xml 複製代碼
- 啓動一個內嵌 Jetty,用於彙報狀態。
- 配置:
dubbo.jetty.port=8080:配置 jetty 啓動端口 dubbo.jetty.directory=/foo/bar:配置可經過 jetty 直接訪問的目錄,用於存放靜態文件 dubbo.jetty.page=log,status,system:配置顯示的頁面,缺省加載全部頁面 複製代碼
- 自動配置 log4j 的配置,在多進程啓動時,自動給日誌文件按進程分目錄。
- 配置:
dubbo.log4j.file=/foo/bar.log:配置日誌文件路徑 dubbo.log4j.level=WARN:配置日誌級別 dubbo.log4j.subdirectory=20880:配置日誌子目錄,用於多進程啓動,避免衝突 複製代碼
com.alibaba.dubbo.container.Main
是服務啓動的主類,缺省只加載 spring:函數
java com.alibaba.dubbo.container.Main
複製代碼
經過 main 函數參數傳入要加載的容器:源碼分析
java com.alibaba.dubbo.container.Main spring jetty log4j
複製代碼
經過 JVM 啓動參數傳入要加載的容器:ui
java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j
複製代碼
經過 classpath 下的 dubbo.properties 配置傳入要加載的容器:spa
dubbo.container=spring,jetty,log4j
複製代碼
com.alibaba.dubbo.container.Main
,源碼以下:3d
package com.alibaba.dubbo.container;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConfigUtils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/** * Main. (API, Static, ThreadSafe) */
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);
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) {
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++) {
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) {
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();
}
}
}
複製代碼
如上圖,依據Dubbo SPI機制,經過ExtensionLoader.getExtensionLoader(Container.class)
,獲取ExtensionLoader實例:日誌
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
複製代碼
經過loader.getExtension(args[i])
,獲取擴展類實例:code
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i++) {
containers.add(loader.getExtension(args[i]));
}
複製代碼
遍歷containers
,啓動容器:
for (Container container : containers) {
container.start();
logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
}
複製代碼
Dubbo是經過JDK的 ShutdownHook
來完成優雅停機的,因此若是用戶使用 kill -9 PID
等強制關閉指令,是不會執行優雅停機的,只有經過 kill PID
時,纔會執行。
服務容器經過Runtime.getRuntime().addShutdownHook(new Thread())
添加停機時的回調鉤子,源碼以下:
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();
}
}
}
});
}
複製代碼
服務容器擴展,用於自定義加載內容。
Maven 項目結構:
src
|-main
|-java
|-com
|-xxx
|-XxxContainer.java (實現Container接口)
|-resources
|-META-INF
|-dubbo
|-com.alibaba.dubbo.container.Container (純文本文件,內容爲:xxx=com.xxx.XxxContainer)
複製代碼
XxxContainer.java:
package com.xxx;
import com.alibaba.dubbo.container.Container;
public class XxxContainer implements Container {
public Status start() {
// ...
}
public Status stop() {
// ...
}
}
複製代碼
META-INF/dubbo/com.alibaba.dubbo.container.Container:
xxx=com.xxx.XxxContainer
複製代碼