Elasticsearch启动流程源码学习(二)
2019-06-03 本文已影响0人
Ombres
Bootstrap类
上一篇说到由Bootstrap的init()方法启动Elasticsearch,这一篇主要讲解init中主要两个步骤
setup方法,基本环境的构建,node节点对象的生成,组件的加载等都在这里。
主要流程介绍
1. 为每个模块生成本地控制器进程,只有有需要的才生成
2. 初始化本地资源
3. 初始化探针
4. 如果有关闭钩子,添加日志关闭。
5. jar包冲突校验
6. Ifconfig注册
7. 启用Security Manager
8. 初始化Node对象,这个对象在初始化的时候加载了各种组件。
private void setup(boolean addShutdownHook, Environment environment) throws BootstrapException {
Settings settings = environment.settings();
try {
//1. 为每个模块生成本地控制器进程,有需要的才会生成
spawner.spawnNativeControllers(environment);
} catch (IOException e) {
throw new BootstrapException(e);
}
//2. 初始化本地资源
initializeNatives(
environment.tmpFile(),
BootstrapSettings.MEMORY_LOCK_SETTING.get(settings),
BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(settings),
BootstrapSettings.CTRLHANDLER_SETTING.get(settings));
// 3. 初始化探针
initializeProbes();
// 4. 如果有关闭钩子,添加日志关闭。
if (addShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
IOUtils.close(node, spawner);
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configurator.shutdown(context);
} catch (IOException ex) {
throw new ElasticsearchException("failed to stop node", ex);
}
}
});
}
// 5. jar包冲突校验
try {
final Logger logger = LogManager.getLogger(JarHell.class);
JarHell.checkJarHell(logger::debug);
} catch (IOException | URISyntaxException e) {
throw new BootstrapException(e);
}
// 6. Ifconfig注册
IfConfig.logIfNecessary();
// 7. 启用Security Manager
try {
Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings));
} catch (IOException | NoSuchAlgorithmException e) {
throw new BootstrapException(e);
}
// 8. 初始化Node对象,这个对象在初始化的时候加载了各种组件。
node = new Node(environment) {
@Override
protected void validateNodeBeforeAcceptingRequests(
final BootstrapContext context,
final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> checks) throws NodeValidationException {
BootstrapChecks.check(context, boundTransportAddress, checks);
}
};
}
1. 本地控制器进程
为每个模块生成本地控制器进程。忽略不包含用于正确平台的控制器的模块。
void spawnNativeControllers(final Environment environment) throws IOException {
if (!spawned.compareAndSet(false, true)) {
throw new IllegalStateException("native controllers already spawned");
}
if (!Files.exists(environment.modulesFile())) {
throw new IllegalStateException("modules directory [" + environment.modulesFile() + "] not found");
}
// 为每个模块生成本地控制器进程。忽略不包含用于正确平台的控制器的模块。
// 查找所有插件的目录
List<Path> paths = PluginsService.findPluginDirs(environment.modulesFile());
// 遍历插件路径下所有文件
for (final Path modules : paths) {
// 访问路径下的Properties配置文件,生成相应的插件信息
final PluginInfo info = PluginInfo.readFromProperties(modules);
final Path spawnPath = Platforms.nativeControllerPath(modules);
// 存在该目录的才需要构建本地控制器
if (!Files.isRegularFile(spawnPath)) {
continue;
}
if (!info.hasNativeController()) {
final String message = String.format(
Locale.ROOT,
"module [%s] does not have permission to fork native controller",
modules.getFileName());
throw new IllegalArgumentException(message);
}
// 启动一个进程,来控制插件,并将进程id记录。不详细介绍,很少用到
final Process process = spawnNativeController(spawnPath, environment.tmpFile());
processes.add(process);
}
}
2. 本地资源初始化。需要进行多步检查
1. 是否是root用户。 root用户无法运行
2. 允许系统调用过滤
3. mlockall 将进程的内存锁定到物理内存,禁止内存交换
4. 添加窗口关闭监听,如果关闭则停止节点。
5. 强制装载剩余的JNA(如果有的话)。
6. 最大进程,最大虚拟内存,最大文件大小的配置
7. 生成一个随机id,初始化lucene种子 /dev/urandom where available:
public static void initializeNatives(Path tmpFile, boolean mlockAll, boolean systemCallFilter, boolean ctrlHandler) {
final Logger logger = LogManager.getLogger(Bootstrap.class);
// 1. 是否是root用户。 root用户无法运行
if (Natives.definitelyRunningAsRoot()) {
throw new RuntimeException("can not run elasticsearch as root");
}
// 2. 允许系统调用过滤
if (systemCallFilter) {
Natives.tryInstallSystemCallFilter(tmpFile);
}
// 3. mlockall 将进程的内存锁定到物理内存,禁止内存交换
if (mlockAll) {
if (Constants.WINDOWS) {
Natives.tryVirtualLock();
} else {
Natives.tryMlockall();
}
}
// 4. 添加窗口关闭监听,如果关闭则停止节点。
if (ctrlHandler) {
Natives.addConsoleCtrlHandler(new ConsoleCtrlHandler() {
@Override
public boolean handle(int code) {
if (CTRL_CLOSE_EVENT == code) {
logger.info("running graceful exit on windows");
try {
Bootstrap.stop();
} catch (IOException e) {
throw new ElasticsearchException("failed to stop node", e);
}
return true;
}
return false;
}
});
}
// 5. 强制装载剩余的JNA(如果有的话)。
try {
JNAKernel32Library.getInstance();
} catch (Exception ignored) {
}
// 6. 最大进程,最大虚拟内存,最大文件大小的配置
Natives.trySetMaxNumberOfThreads();
Natives.trySetMaxSizeVirtualMemory();
Natives.trySetMaxFileSize();
// 7. 生成一个随机id,初始化lucene种子 /dev/urandom where available:
StringHelper.randomId();
}
3. 初始化探针,包括三大部分 ProcessProbe
、OsProbe
、JvmInfo
。
static void initializeProbes() {
// Force probes to be loaded
ProcessProbe.getInstance();
OsProbe.getInstance();
JvmInfo.jvmInfo();
}
ProcessProbe 主要是来获取进程的一些基本信息。文件描述符,进程CPU,虚拟内存等。
public class ProcessProbe {
private static final OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean();
private static final Method getMaxFileDescriptorCountField;
private static final Method getOpenFileDescriptorCountField;
private static final Method getProcessCpuLoad;
private static final Method getProcessCpuTime;
private static final Method getCommittedVirtualMemorySize;
static {
getMaxFileDescriptorCountField = getUnixMethod("getMaxFileDescriptorCount");
getOpenFileDescriptorCountField = getUnixMethod("getOpenFileDescriptorCount");
getProcessCpuLoad = getMethod("getProcessCpuLoad");
getProcessCpuTime = getMethod("getProcessCpuTime");
getCommittedVirtualMemorySize = getMethod("getCommittedVirtualMemorySize");
}
}
OsProbe 主要是来获取操作系统的一些基本信息。物理内存,交换空间等。
public class OsProbe {
private static final OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean();
private static final Method getFreePhysicalMemorySize;
private static final Method getTotalPhysicalMemorySize;
private static final Method getFreeSwapSpaceSize;
private static final Method getTotalSwapSpaceSize;
private static final Method getSystemLoadAverage;
private static final Method getSystemCpuLoad;
static {
getFreePhysicalMemorySize = getMethod("getFreePhysicalMemorySize");
getTotalPhysicalMemorySize = getMethod("getTotalPhysicalMemorySize");
getFreeSwapSpaceSize = getMethod("getFreeSwapSpaceSize");
getTotalSwapSpaceSize = getMethod("getTotalSwapSpaceSize");
getSystemLoadAverage = getMethod("getSystemLoadAverage");
getSystemCpuLoad = getMethod("getSystemCpuLoad");
}
}
JVM 信息。
4. addShutdownHook 顾名思义,不细说。
5. jar包冲突校验 不细说。
6. 日志。主要是debug级别的输出的一些信息。
7. 启用Security Manager.
8. new一个node对象。其实这里的才是真正核心内容的加载。后续我会补一篇文章详细描述。
Bootstrap的start方法
主要调用了两个方法。
-
node.start()
:启动节点,如果节点已经启动,则不进行任何操作 -
keepAliveThread.start();
这个对象在Bootstrap的构造器中创建的,用来保证进程运行。
private void start() throws NodeValidationException {
node.start();
keepAliveThread.start();
}