elasticsearch源码分析-01服务启动

服务启动

服务启动入口类为Elasticsearch

/**
* Main entry point for starting elasticsearch
*/
public static void main(final String[] args) throws Exception {
   
    LogConfigurator.registerErrorListener();
    final Elasticsearch elasticsearch = new Elasticsearch();
    //启动
    int status = main(args, elasticsearch, Terminal.DEFAULT);
    if (status != ExitCodes.OK) {
   
        final String basePath = System.getProperty("es.logs.base_path");
        // It's possible to fail before logging has been configured, in which case there's no point
        // suggesting that the user look in the log file.
        if (basePath != null) {
   
            Terminal.DEFAULT.errorPrintln(
                "ERROR: Elasticsearch did not exit normally - check the logs at "
                + basePath
                + System.getProperty("file.separator")
                + System.getProperty("es.logs.cluster_name") + ".log"
            );
        }
        exit(status);
    }
}

static int main(final String[] args, final Elasticsearch elasticsearch, final Terminal terminal) throws Exception {
   
    return elasticsearch.main(args, terminal);
}

elasticsearch类继承了EnvironmentAwareCommand,首先会解析配置文件,将配置信息加载进来

@Override
protected void execute(Terminal terminal, OptionSet options) throws Exception {
   
    final Map<String, String> settings = new HashMap<>();
    //遍历配置存入map
    for (final KeyValuePair kvp : settingOption.values(options)) {
   
        if (kvp.value.isEmpty()) {
   
            throw new UserException(ExitCodes.USAGE, "setting [" + kvp.key + "] must not be empty");
        }
        if (settings.containsKey(kvp.key)) {
   
            final String message = String.format(
                Locale.ROOT,
                "setting [%s] already set, saw [%s] and [%s]",
                kvp.key,
                settings.get(kvp.key),
                kvp.value);
            throw new UserException(ExitCodes.USAGE, message);
        }
        settings.put(kvp.key, kvp.value);
    }
    //设置data存储
    putSystemPropertyIfSettingIsMissing(settings, "path.data", "es.path.data");
    //home目录
    putSystemPropertyIfSettingIsMissing(settings, "path.home", "es.path.home");
    //日志记录目录
    putSystemPropertyIfSettingIsMissing(settings, "path.logs", "es.path.logs");

    execute(terminal, options, createEnv(settings));
}

然后调用elasticsearch下的execute方法

@Override
protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException {
   
    //后台运行
    final boolean daemonize = options.has(daemonizeOption);
    //记录pid的文件
    final Path pidFile = pidfileOption.value(options);
    //静默模式
    final boolean quiet = options.has(quietOption);

    // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately
    try {
   
        //确保配置的临时目录是有效目录
        env.validateTmpFile();
    } catch (IOException e) {
   
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }

    try {
   
        //初始化
        init(daemonize, pidFile, quiet, env);
    } catch (NodeValidationException e) {
   
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }
}

这里首先会根据不同的启动参数进行处理,及是否后台运行,是否指定了启动的pid文件,是否为静默模式,检查配置的临时目录是否存在调用Bootstrap的init方法

Bootstrap.init(!daemonize, pidFile, quiet, initialEnv);
static void init(
    final boolean foreground,
    final Path pidFile,
    final boolean quiet,
    final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException {
   
    INSTANCE = new Bootstrap();
    final SecureSettings keystore = loadSecureSettings(initialEnv);
    //创建运行环境
    final Environment environment = createEnvironment(pidFile, keystore, initialEnv.settings(), initialEnv.configFile());
    //设置node名称
    LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(environment.settings()));
    try {
   
        LogConfigurator.configure(environment);
    } catch (IOException e) {
   
        throw new BootstrapException(e);
    }
    //校验java版本
    if (JavaVersion.current().compareTo(JavaVersion.parse("11")) < 0) {
   
        final String message = String.format(
            Locale.ROOT,
            "future versions of Elasticsearch will require Java 11; " +
            "your Java version from [%s] does not meet this requirement",
            System.getProperty("java.home"));
        new DeprecationLogger(LogManager.getLogger(Bootstrap.class)).deprecatedAndMaybeLog("java_version_11_required", message);
    }
    if (environment.pidFile() != null) {
   
        try {
   
            //创建pid文件
            PidFile.create(environment.pidFile(), true);
        } catch (IOException e) {
   
            throw new BootstrapException(e);
        }
    }

    final boolean closeStandardStreams = (foreground == false) || quiet;
    try {
   
        if (closeStandardStreams) {
   
            final Logger rootLogger = LogManager.getRootLogger();
            final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
            if (maybeConsoleAppender != null) {
   
                Loggers.removeAppender(rootLogger, maybeConsoleAppender);
            }
            closeSystOut();
        }

        //校验lucene依赖版本,防止有人替换了lucene jar包
        checkLucene();

        // install the default uncaught exception handler; must be done before security is
        // initialized as we do not want to grant the runtime permission
        // setDefaultUncaughtExceptionHandler
        //设置异常处理类
        Thread.setDefaultUncaughtExceptionHandler(new ElasticsearchUncaughtExceptionHandler());
        //创建node实例,并创建相关的服务
        INSTANCE.setup(true, environment);

        try {
   
            // any secure settings must be read during node construction
            IOUtils.close(keystore);
        } catch (IOException e) {
   
            throw new BootstrapException(e);
        }
        //启动节点
        INSTANCE.start();
    } catch (NodeValidationException | RuntimeException e) {
   
        ...
    }
}

这里会先创建bootstrap实例,添加一个shutdownhook当接收到signal时候,会触发keepAliveThread线程任务退出

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

然后创建运行环境,检查jdk版本不能低于11,同时检查lucene的jar包依赖是否正常,然后调用setup方法创建node对象,node构造函数会加载所有组件。

 //插件服务
this.pluginsService = new PluginsService(tmpSettings, initialEnvironment.configFile(), initialEnvironment.modulesFile(),
                initialEnvironment.pluginsFile(), classpathPlugins);

ES插件服务,很多组件都是通过插件的形式加载进来的比如底层的通信服务等,就是将modules和plugins目录下的内容加载进来。

//创建配置
this.environment = new Environment(settings, initialEnvironment.configFile(), Node.NODE_LOCAL_STORAGE_SETTING.get(settings));
Environment.assertEquivalent(initialEnvironment, this.environment);
//创建节点配置
nodeEnvironment = new NodeEnvironment(tmpSettings, environment);

创建运行环境,封装一些配置信息

//初始化创建线程池
            final ThreadPool threadPool = new ThreadPool(settings, executorBuilders.toArray(new ExecutorBuilder[0]));

public ThreadPool(final Settings settings, final ExecutorBuilder<?>... customBuilders) {
   
    assert Node.NODE_NAME_SETTING.exists(settings);

    final Map<String, ExecutorBuilder> builders = new HashMap<>();
    //分片分配操作处理线程数
    final int allocatedProcessors = EsExecutors.allocatedProcessors(settings);
    //allocatedProcessors的一半,最大为5
    final int halfProcMaxAt5 = halfAllocatedProcessorsMaxFive(allocatedProcessors);
    //allocatedProcessors的一半,最大为10
    final int halfProcMaxAt10 = halfAllocatedProcessorsMaxTen(allocatedProcessors);
    //generic线程池最大数
    final int genericThreadPoolMax = boundedBy(4 * allocatedProcessors, 128, 512);
    //定义线程池
    builders.put(Names.GENERIC, new ScalingExecutorBuilder(Names.GENERIC, 4, genericThreadPoolMax, TimeValue.timeValueSeconds(30)));
    builders.put(Names.WRITE, new FixedExecutorBuilder(settings, Names.WRITE, allocatedProcessors, 200));
    builders.put(Names.GET, new FixedExecutorBuilder(settings, Names.GET, allocatedProcessors, 1000));
    builders.put(Names.ANALYZE, new FixedExecutorBuilder(settings, Names.ANALYZE, 1, 16));
    builders.put(Names.SEARCH, new AutoQueueAdjustingExecutorBuilder(settings,
                                                                     Names.SEARCH, searchThreadPoolSize(allocatedProcessors), 1000, 1000, 1000, 2000));
    builders.put(Names.SEARCH_THROTTLED, new AutoQueueAdjustingExecutorBuilder(settings,
                                                                               Names.SEARCH_THROTTLED, 1, 100, 100, 100, 200));
    builders.put(Names.MANAGEMENT, new ScalingExecutorBuilder(Names.MANAGEMENT, 1, 5, TimeValue.timeValueMinutes(5)));
    // no queue as this means clients will need to handle rejections on listener queue even if the operation succeeded
    // the assumption here is that the listeners should be very lightweight on the listeners side
    builders.put(Names.LISTENER, new FixedExecutorBuilder(settings, Names.LISTENER, halfProcMaxAt10, -1, true));
    builders.put(Names.FLUSH, new ScalingExecutorBuilder(Names.FLUSH, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
    builders.put(Names.REFRESH, new ScalingExecutorBuilder(Names.REFRESH, 1, halfProcMaxAt10, TimeValue.timeValueMinutes(5)));
    builders.put(Names.WARMER, new ScalingExecutorBuilder(Names.WARMER, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
    builders.put(Names.SNAPSHOT, new ScalingExecutorBuilder(Names.SNAPSHOT, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
    builders.put(Names.FETCH_SHARD_STARTED,
                 new ScalingExecutorBuilder(Names.FETCH_SHARD_STARTED, 1, 2 * allocatedProcessors, TimeValue.timeValueMinutes(5)));
    builders.put(Names.FORCE_MERGE, new FixedExecutorBuilder(settings, Names.FORCE_MERGE, 1, -1));
    builders.put(Names.FETCH_SHARD_STORE,
                 new ScalingExecutorBuilder(Names.FETCH_SHARD_STORE, 1, 2 * allocatedProcessors, TimeValue.timeValueMinutes(5)));
    for (final ExecutorBuilder<?> builder : customBuilders) {
   
        if (builders.containsKey(builder.name())) {
   
            throw new IllegalArgumentException("builder with name [" + builder.name() + "] already exists");
        }
        builders.put(builder.name(), builder);
    }
    this.builders = Collections.unmodifiableMap(builders);

    threadContext = new ThreadContext(settings);

    final Map<String, ExecutorHolder> executors = new HashMap<>();
    for (final Map.Entry<String, ExecutorBuilder> entry : builders.entrySet()) {
   
        final ExecutorBuilder.ExecutorSettings executorSettings = entry.getValue().getSettings(settings);
        final ExecutorHolder executorHolder = entry.getValue().build(executorSettings, threadContext);
        if (executors.containsKey(executorHolder.info.getName())) {
   
            throw new IllegalStateException("duplicate executors with name [" + executorHolder.info.getName() + "] registered");
        }
        logger.debug("created thread pool: {}", entry.getValue().formatInfo(executorHolder.info));
        executors.put(entry.getKey(), executorHolder);
    }

    executors.put(Names.SAME, new ExecutorHolder(DIRECT_EXECUTOR, new Info(Names.SAME, ThreadPoolType.DIRECT)));
    this.executors = unmodifiableMap(executors);

    final List<Info> infos =
        executors
        .values()
        .stream()
        .filter(holder -> holder.info.getName().equals("same") == false)
        .map(holder -> holder.info)
        .collect(Collectors.toList());
    this.threadPoolInfo = new ThreadPoolInfo(infos);
    //初始化定时线程池
    this.scheduler = Scheduler.initScheduler(settings);
    TimeValue estimatedTimeInterval = ESTIMATED_TIME_INTERVAL_SETTING.get(settings);
    //缓存时间线程
    this.cachedTimeThread = new CachedTimeThread(EsExecutors.threadName(settings, "[timer]"), estimatedTimeInterval.millis());
    this.cachedTimeThread.start();
}

创建自定义的线程池,ES中线程池分为generic、index、search、get、bulk、snapshot、same等

 //es扩展脚本模块
            final ScriptModule scriptModule = new ScriptModule(settings, pluginsService.filterPlugins(ScriptPlugin.class));
            final ScriptService scriptService = newScriptService(settings, scriptModule.engines, scriptModule.contexts);

加载ES扩展脚本模块painless,复杂查询支持通过脚本形式

//分词模块
            AnalysisModule analysisModule = new AnalysisModule(this.environment, pluginsService.filterPlugins(AnalysisPlugin.class));

创建分词器模块,用于索引和搜索时进行分词

final SettingsModule settingsModule =
                    new SettingsModule(settings, additionalSettings, additionalSettingsFilter, settingsUpgraders);
            scriptModule.registerClusterSettingsListeners(scriptService, settingsModule.getClusterSettings());

加载各种配置参数,如elasticsearch.yml和jvm.options中配置的参数

//网络服务
final NetworkService networkService = new NetworkService(
    getCustomNameResolvers(pluginsService.filterPlugins(DiscoveryPlugin.class)));
//集群插件
List<ClusterPlugin> clusterPlugins = pluginsService.filterPlugins(ClusterPlugin.class);
//集群服务
final ClusterService clusterService = new ClusterService(settings, settingsModule.getClusterSettings(), threadPool);
clusterService.addStateApplier(scriptService);

创建网络服务和集群组件服务,用于后期集群选举主节点、生成集群状态、分片分配、发布集群状态、分片恢复、数据同步等功能

//集群信息更新服务
final ClusterInfoService clusterInfoService = newClusterInfoService(settings, clusterService, threadPool, client);
//用于监控 Elasticsearch 功能使用情况的服务。
final UsageService usageService = new UsageService();

周期性同步集群状态服务和监控ES使用情况服务

ModulesBuilder modules = new ModulesBuilder();
 // plugin modules must be added here, before others or we can get crazy injection errors...
 for (Module pluginModule : pluginsService.createGuiceModules()) {
   
 modules.add(pluginModule);
 }

加载组件

//监控服务
final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService);

创建监控服务,监控jvm运行参数和GC运行情况、句柄使用、操作系统等信息

//集群模块
ClusterModule clusterModule = new ClusterModule(settings, clusterService, clusterPlugins, clusterInfoService);
modules.add(clusterModule);

public ClusterModule(Settings settings, ClusterService clusterService, List<ClusterPlugin> clusterPlugins,
                     ClusterInfoService clusterInfoService) {
   
    this.clusterPlugins = clusterPlugins;
    //分配选择器
    this.deciderList = createAllocationDeciders(settings, clusterService.getClusterSettings(), clusterPlugins);
    this.allocationDeciders = new AllocationDeciders(deciderList);
    //分片分配
    this.shardsAllocator = createShardsAllocator(settings, clusterService.getClusterSettings(), clusterPlugins);
    this.clusterService = clusterService;
    this.indexNameExpressionResolver = new IndexNameExpressionResolver();
    //分配服务
    this.allocationService = new AllocationService(allocationDeciders, shardsAllocator, clusterInfoService);
}

public static Collection<AllocationDecider> createAllocationDeciders(Settings settings, ClusterSettings clusterSettings,                                           List<ClusterPlugin> clusterPlugins) {
   
    // collect deciders by class so that we can detect duplicates
    Map<Class, AllocationDecider> deciders = new LinkedHashMap<>();
    addAllocationDecider(deciders, new MaxRetryAllocationDecider());
    addAllocationDecider(deciders, new ResizeAllocationDecider());
    addAllocationDecider(deciders, new ReplicaAfterPrimaryActiveAllocationDecider());
    addAllocationDecider(deciders, new RebalanceOnlyWhenActiveAllocationDecider());
    addAllocationDecider(deciders, new ClusterRebalanceAllocationDecider(settings, clusterSettings));
    addAllocationDecider(deciders, new ConcurrentRebalanceAllocationDecider(settings, clusterSettings));
    addAllocationDecider(deciders, new EnableAllocationDecider(settings, clusterSettings));
    addAllocationDecider(deciders, new NodeVersionAllocationDecider());
    addAllocationDecider(deciders, new 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值