美文网首页
Elasticsearch启动流程源码学习(二)

Elasticsearch启动流程源码学习(二)

作者: Ombres | 来源:发表于2019-06-03 19:52 被阅读0次

    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();
        }
    

    相关文章

      网友评论

          本文标题:Elasticsearch启动流程源码学习(二)

          本文链接:https://www.haomeiwen.com/subject/uucfxctx.html