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. 初始化探针,包括三大部分 ProcessProbeOsProbeJvmInfo
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方法

主要调用了两个方法。

  1. node.start() :启动节点,如果节点已经启动,则不进行任何操作
  2. keepAliveThread.start(); 这个对象在Bootstrap的构造器中创建的,用来保证进程运行。
 private void start() throws NodeValidationException {
        node.start();
        keepAliveThread.start();
    }
上一篇下一篇

猜你喜欢

热点阅读