跳到内容
argsno
返回

04. Elasticsearch 节点启动

启动流程核心工作

总体而言,节点启动流程的任务包括:

启动流程分析

启动入口:org.elasticsearch.bootstrap.Elasticsearch#main(java.lang.String[])

检测外部环境

在创建Node时覆盖validateNodeBeforeAcceptingRequests方法,在接收请求之前执行BootstracpChecks.check执行各项外部环境检查:

node = new Node(environment) {
    @Override
    protected void validateNodeBeforeAcceptingRequests(
        final BootstrapContext context,
        final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> checks) throws NodeValidationException {
        BootstrapChecks.check(context, boundTransportAddress, checks);
    }
};

执行的检查包括:

// the list of checks to execute
static List<BootstrapCheck> checks() {
    final List<BootstrapCheck> checks = new ArrayList<>();
    checks.add(new HeapSizeCheck());
    final FileDescriptorCheck fileDescriptorCheck
        = Constants.MAC_OS_X ? new OsXFileDescriptorCheck() : new FileDescriptorCheck();
    checks.add(fileDescriptorCheck);
    checks.add(new MlockallCheck());
    if (Constants.LINUX) {
        checks.add(new MaxNumberOfThreadsCheck());
    }
    if (Constants.LINUX || Constants.MAC_OS_X) {
        checks.add(new MaxSizeVirtualMemoryCheck());
    }
    if (Constants.LINUX || Constants.MAC_OS_X) {
        checks.add(new MaxFileSizeCheck());
    }
    if (Constants.LINUX) {
        checks.add(new MaxMapCountCheck());
    }
    checks.add(new ClientJvmCheck());
    checks.add(new UseSerialGCCheck());
    checks.add(new SystemCallFilterCheck());
    checks.add(new OnErrorCheck());
    checks.add(new OnOutOfMemoryErrorCheck());
    checks.add(new EarlyAccessCheck());
    checks.add(new G1GCCheck());
    return Collections.unmodifiableList(checks);
}

如果插件有额外的检查项也会加进去。

启动内部模块

环境检查完毕,调用Node.start()开始启动节点各子模块。子模块在Node类中创建,启动它们时调用各自的start()方法,例如:

启动keepalive线程

调用keepAliveThread.start()方法启动keepalive线程,线程本身不做具体的工作。主线程执行完启动流程后会退出,keepalive线程是唯一的用户线程,作用是保持进程运行。

节点关闭流程

设想当我们为 ES 集群更新配置、升级版本时,需要通过“kill”ES进程来关闭节点。

ES进程会捕获SIGTERM信号(kill命令默认信号)进行处理,调用各模块的stop方法,让它们有机会停止服务,安全退出。

进程重启期间,如果主节点被关闭,则集群会重新选主,在这期间,集群有一个短暂的无主状态。如果集群中的主节点是单独部署的,则新主当选后,可以跳过gateway和recovery流程,否则新主需要重新分配旧主所持有的分片:提升其他副本为主分片,以及分配新的副分片。

如果数据节点被关闭,则读写请求的TCP连接也会因此关闭,对客户端来说写操作执行失败。但写流程已经到达Engine环节的会正常写完,只是客户端无法感知结果。此时客户端重试,如果使用自动生成ID,则数据内容会重复。

综合来说,滚动升级产生的影响是中断当前写请求,以及主节点重启可能引起的分片分配过程。

关闭流程分析

在节点启动过程中,Bootstrap#setup 方法中添加了 shutdown hook,当进程收到系统SIGTERM(kill命令默认信号)或SIGINT信号时,调用Node#close方法,执行节点关闭流程。

每个模块的Service中都实现了doStop和doClose,用于处理这个模块的正常关闭流程。节点总的关闭流程位于Node#close,在close方法的实现中,先调用一遍各个模块的doStop,然后再次遍历各个模块执行doClose。

综合来看,关闭顺序大致如下:

分片读写过程中执行关闭

写入过程中关闭:线程在写入数据时,会对Engine加写锁。IndicesService的doStop方法对本节点上全部索引并行执行removeIndex,当执行到Engine的flushAndClose(先flush然后关闭Engine),也会对Engine加写锁。由于写入操作已经加了写锁,此时写锁会等待,直到写入执行完毕。因此数据写入过程不会被中断。但是由于网络模块被关闭,客户端的连接会被断开。客户端应当作为失败处理,虽然ES服务端的写流程还在继续。

读取过程中关闭:线程在读取数据时,会对Engine加读锁。flushAndClose时的写锁会等待读取过程执行完毕。但是由于连接被关闭,无法发送给客户端,导致客户端读失败。

节点关闭过程中,IndicesService的doStop对Engine设置了超时,如果flushAndClose一直等待,则CountDownLatch.await默认1天才会继续后面的流程。

主节点被关闭

主节点被关闭时,没有想象中的特殊处理,节点正常执行关闭流程,当TransportService模块被关闭后,集群重新选举新Master。因此,滚动重启期间会有一段时间处于无主状态。

参考


分享这篇文章:

上一篇文章
以 Codex 源码入坑 AI Agent
下一篇文章
03. Elasticsearch allocation模块分析