Elasticsearch源码学习(一)——启动

【说明】

    本文以elasticsearch 2.4版本进行。elasticsearch代码变化快,本位内容为2.4版本Elasticsearch源码。

【代码结构】

    git上elasticsearch目前如下,其源码在核心core目录

195941_kyV9_614144.png

【elasticsearch脚本】

elasticsearch部署目录的./bin/elasticsear脚本中,暴露了elasticsearch服务的启动类:org.elasticsearch.bootstrap.Elasticsearch

launch_service()
{
    pidpath=$1
    daemonized=$2
    props=$3
    es_parms="-Delasticsearch"

    if [ "x$pidpath" != "x" ]; then
        es_parms="$es_parms -Des.pidfile=$pidpath"
    fi

    # Make sure we dont use any predefined locale, as we check some exception message strings and rely on english language
    # As those strings are created by the OS, they are dependant on the configured locale
    LANG=en_US.UTF-8
    LC_ALL=en_US.UTF-8

    export HOSTNAME=`hostname -s`

    # The es-foreground option will tell Elasticsearch not to close stdout/stderr, but it's up to us not to daemonize.
    if [ "x$daemonized" = "x" ]; then
        es_parms="$es_parms -Des.foreground=yes"
        exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" $props \
                org.elasticsearch.bootstrap.Elasticsearch
        # exec without running it in the background, makes it replace this shell, we'll never get here...
        # no need to return something
    else
        # Startup Elasticsearch, background it, and write the pid.
        exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" $props \
                    org.elasticsearch.bootstrap.Elasticsearch <&- &
        return $?
    fi
}

 

【org.elasticsearch.bootstrap.Elasticsearch】

  1. 提供了main方法,接收args参数;
  2. 调用org.elasticsearch.bootstrap.Bootstrap的init方法执行初始化
public final class Elasticsearch {

    ......

    /**
     * Main entry point for starting elasticsearch
     */
    public static void main(String[] args) throws StartupError {
        // we want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on the
        // presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy)
        System.setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(Permission perm) {
                // grant all permissions so that we can later set the security manager to the one that we want
            }
        });
        try {
            Bootstrap.init(args);
        } catch (Throwable t) {
            // format exceptions to the console in a special way
            // to avoid 2MB stacktraces from guice, etc.
            throw new StartupError(t);
        }
    }

    ......

}

 

【org.elasticsearch.bootstrap.Bootstrap】

  1. 校验启动参数合法性,获取配置文件参数;
  2. 设置环境信息与配置信息;
  3. 执行start方法
    1. 调用org.elasticsearch.node.Node#start方法初始化节点;
    2. 启动keepAliveThread线程,keepAliveThread在Bootstrap构造方法中定义:使用CountDownLatch进行线程挂载;
final class Bootstrap {

    ......

    Bootstrap() {
        keepAliveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    keepAliveLatch.await();
                } catch (InterruptedException e) {
                    // bail out
                }
            }
        }, "elasticsearch[keepAlive/" + Version.CURRENT + "]");
        keepAliveThread.setDaemon(false);
        // keep this thread alive (non daemon thread) until we shutdown
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                keepAliveLatch.countDown();
            }
        });
    }

    private void start() {
        node.start();
        keepAliveThread.start();
    }


    static void init(String[] args) throws Throwable {
        // 初始化es log默认配置
        System.setProperty("es.logger.prefix", "");

        // 启动参数校验
        BootstrapCLIParser bootstrapCLIParser = new BootstrapCLIParser();
        CliTool.ExitStatus status = bootstrapCLIParser.execute(args);

        if (CliTool.ExitStatus.OK != status) {
            exit(status.status());
        }

        INSTANCE = new Bootstrap();

        // 判断是否以console模式启动
        boolean foreground = !"false".equals(System.getProperty("es.foreground", System.getProperty("es-foreground")));

        // 使用service wapper启动,视为非console启动
        if (System.getProperty("wrapper.service", "XXX").equalsIgnoreCase("true")) {
            foreground = false;
        }

        // 初始化环境
        Environment environment = initialSettings(foreground);
        // 初始化配置
        Settings settings = environment.settings();
        LogConfigurator.configure(settings, true);
        // 配置检查
        checkForCustomConfFile();

        if (environment.pidFile() != null) {
            PidFile.create(environment.pidFile(), true);
        }

        // 若未设置文件句柄数,使用最大值
        if (System.getProperty("es.max-open-files", "false").equals("true")) {
            ESLogger logger = Loggers.getLogger(Bootstrap.class);
            logger.info("max_open_files [{}]", ProcessProbe.getInstance().getMaxFileDescriptorCount());
        }

        // jvm启动模式为client时,警告用户;server模式的jvm性能高
        if (JvmInfo.jvmInfo().getVmName().toLowerCase(Locale.ROOT).contains("client")) {
            ESLogger logger = Loggers.getLogger(Bootstrap.class);
            logger.warn("jvm uses the client vm, make sure to run `java` with the server vm for best performance by adding `-server` to the command line");
        }

        try {
            if (!foreground) {
                Loggers.disableConsoleLogging();
                closeSystOut();
            }

            // jvm检查,避免jvm存在bug导致数据损坏(Checks that the current JVM is "ok". This means it doesn't have severe bugs that cause data corruption)
            JVMCheck.check();

            // 设置环境信息
            INSTANCE.setup(true, settings, environment);

            // 启动
            INSTANCE.start();

            if (!foreground) {
                closeSysError();
            }
        } catch (Throwable e) {
            // disable console logging, so user does not see the exception twice (jvm will show it already)
            if (foreground) {
                Loggers.disableConsoleLogging();
            }
            ESLogger logger = Loggers.getLogger(Bootstrap.class);
            if (INSTANCE.node != null) {
                logger = Loggers.getLogger(Bootstrap.class, INSTANCE.node.settings().get("name"));
            }
            // HACK, it sucks to do this, but we will run users out of disk space otherwise
            if (e instanceof CreationException) {
                // guice: log the shortened exc to the log file
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                PrintStream ps = new PrintStream(os, false, "UTF-8");
                new StartupError(e).printStackTrace(ps);
                ps.flush();
                logger.error("Guice Exception: {}", os.toString("UTF-8"));
            } else {
                // full exception
                logger.error("Exception", e);
            }
            // re-enable it if appropriate, so they can see any logging during the shutdown process
            if (foreground) {
                Loggers.enableConsoleLogging();
            }

            throw e;
        }
    }

    ......
}

 

【org.elasticsearch.node.Node】

  1. Elasticsearch使用google的Guice进行依赖管理,Node类的构造方法将接口与实现进行绑定并由Guice的Injector管理。
  2. 判断节点是否已经启动,若启动返回该节点;
  3. 从Injector获取具体的实例,执行start方法,执行过程由内而外:单节点底层服务围内,通信服务次之,server服务最后;
public class Node implements Releasable {
   
    ......

public Node(Settings preparedSettings) {
        this(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), Version.CURRENT, Collections.<Class<? extends Plugin>>emptyList());
    }

    protected Node(Environment tmpEnv, Version version, Collection<Class<? extends Plugin>> classpathPlugins) {
        Settings tmpSettings = settingsBuilder().put(tmpEnv.settings())
                .put(Client.CLIENT_TYPE_SETTING, CLIENT_TYPE).build();
        tmpSettings = TribeService.processSettings(tmpSettings);

        ESLogger logger = Loggers.getLogger(Node.class, tmpSettings.get("name"));
        logger.info("version[{}], pid[{}], build[{}/{}]", version, JvmInfo.jvmInfo().pid(), Build.CURRENT.hashShort(), Build.CURRENT.timestamp());

        logger.info("initializing ...");

        if (logger.isDebugEnabled()) {
            logger.debug("using config [{}], data [{}], logs [{}], plugins [{}]",
                    tmpEnv.configFile(), Arrays.toString(tmpEnv.dataFiles()), tmpEnv.logsFile(), tmpEnv.pluginsFile());
        }

        this.pluginsService = new PluginsService(tmpSettings, tmpEnv.modulesFile(), tmpEnv.pluginsFile(), classpathPlugins);
        this.settings = pluginsService.updatedSettings();
        // create the environment based on the finalized (processed) view of the settings
        this.environment = new Environment(this.settings());

        final NodeEnvironment nodeEnvironment;
        try {
            nodeEnvironment = new NodeEnvironment(this.settings, this.environment);
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to created node environment", ex);
        }

        final ThreadPool threadPool = new ThreadPool(settings);
        NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry();

        boolean success = false;
        try {
            ModulesBuilder modules = new ModulesBuilder();
            modules.add(new Version.Module(version));
            modules.add(new CircuitBreakerModule(settings));

            // 插件模块加载
            for (Module pluginModule : pluginsService.nodeModules()) {
                modules.add(pluginModule);
            }
            modules.add(new PluginsModule(pluginsService));
            modules.add(new SettingsModule(this.settings));
            modules.add(new NodeModule(this));
            modules.add(new NetworkModule(namedWriteableRegistry));
            modules.add(new ScriptModule(this.settings));
            modules.add(new EnvironmentModule(environment));
            modules.add(new NodeEnvironmentModule(nodeEnvironment));
            modules.add(new ClusterNameModule(this.settings));
            modules.add(new ThreadPoolModule(threadPool));
            modules.add(new DiscoveryModule(this.settings));
            modules.add(new ClusterModule(this.settings));
            modules.add(new RestModule(this.settings));
            modules.add(new TransportModule(settings, namedWriteableRegistry));
            if (settings.getAsBoolean(HTTP_ENABLED, true)) {
                modules.add(new HttpServerModule(settings));
            }
            modules.add(new IndicesModule());
            modules.add(new SearchModule());
            modules.add(new ActionModule(false));
            modules.add(new MonitorModule(settings));
            modules.add(new GatewayModule(settings));
            modules.add(new NodeClientModule());
            modules.add(new ShapeModule());
            modules.add(new PercolatorModule());
            modules.add(new ResourceWatcherModule());
            modules.add(new RepositoriesModule());
            modules.add(new TribeModule());


            pluginsService.processModules(modules);

            injector = modules.createInjector();

            client = injector.getInstance(Client.class);
            threadPool.setNodeSettingsService(injector.getInstance(NodeSettingsService.class));
            success = true;
        } finally {
            if (!success) {
                nodeEnvironment.close();
                ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS);
            }
        }

        logger.info("initialized");
    }

    public Node start() {

        // 若已经启动,返回当前当前Node
        if (!lifecycle.moveToStarted()) {
            return this;
        }

        ESLogger logger = Loggers.getLogger(Node.class, settings.get("name"));
        logger.info("starting ...");


        injector.getInstance(Discovery.class).setRoutingService(injector.getInstance(RoutingService.class));

        // 插件加载
        for (Class<? extends LifecycleComponent> plugin : pluginsService.nodeServices()) {
            injector.getInstance(plugin).start();
        }

        injector.getInstance(MappingUpdatedAction.class).setClient(client);
        injector.getInstance(IndicesService.class).start();
        injector.getInstance(IndexingMemoryController.class).start();
        injector.getInstance(IndicesClusterStateService.class).start();
        injector.getInstance(IndicesTTLService.class).start();
        injector.getInstance(SnapshotsService.class).start();
        injector.getInstance(SnapshotShardsService.class).start();
        injector.getInstance(RoutingService.class).start();
        injector.getInstance(SearchService.class).start();
        injector.getInstance(MonitorService.class).start();
        injector.getInstance(RestController.class).start();

        // TODO hack around circular dependencies problems
        injector.getInstance(GatewayAllocator.class).setReallocation(injector.getInstance(ClusterService.class), injector.getInstance(RoutingService.class));

        injector.getInstance(ResourceWatcherService.class).start();
        injector.getInstance(GatewayService.class).start();

        // Start the transport service now so the publish address will be added to the local disco node in ClusterService
        TransportService transportService = injector.getInstance(TransportService.class);
        transportService.start();
        injector.getInstance(ClusterService.class).start();

        // start after cluster service so the local disco is known
        DiscoveryService discoService = injector.getInstance(DiscoveryService.class).start();


        transportService.acceptIncomingRequests();
        discoService.joinClusterAndWaitForInitialState();

        if (settings.getAsBoolean("http.enabled", true)) {
            injector.getInstance(HttpServer.class).start();
        }
        injector.getInstance(TribeService.class).start();
        if (settings.getAsBoolean("node.portsfile", false)) {
            if (settings.getAsBoolean("http.enabled", true)) {
                HttpServerTransport http = injector.getInstance(HttpServerTransport.class);
                writePortsFile("http", http.boundAddress());
            }
            TransportService transport = injector.getInstance(TransportService.class);
            writePortsFile("transport", transport.boundAddress());
        }
        logger.info("started");

        return this;
    }

    ......

}

 

至此服务启动完成。

es启动涉及3个类

org.elasticsearch.bootstrap.Elasticsearch

服务启动入口

org.elasticsearch.bootstrap.Bootstrap

启动参数校验与获取配置信息,挂载服务

org.elasticsearch.node.Node

服务、插件等功能启动启动

转载于:https://my.oschina.net/SEyanlei/blog/915369

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值