启动的时候刚开始没有做啥,真正开始的执行是从这个方法开始的
org.elasticsearch.cli.Command#execute
1、创建Environment
会利用到elasticsearch home中conf的配置文件,必填项
在创建Environment之前先准备
核心的关键是获取命令行传入进去的path.home的值,放到setting里。因为在创建Environment的时候,要依据该参数将里面的
config、plugins、data、logs、bin、lib、modules等解析出来,正好对应相应文件夹
xiaohuihui@xiaohuihuideMBP 8_home % ls
config data lib logs modules plugins
相当于从path.home里解析相应路径,这个时候会有一个Environment的变量,这个变量还不是最终返回的Environment,因为还没解析配置文件elasticsearch.yaml。
接着开始解析elasticsearch.yaml文件,如果用户在该配置文件中设置了一些logs、data的路径,才会是最终的路径,因为解析完该配置文件后,es会new一个Environment 返回,这时的logs、data等的路径是解析完elasticsearch.yaml之后的。总结一下,es本身对于logs、data等有默认的路径,如果用户通过elasticsearch.yaml进行了设置,就使用用户设置的。
2、init
2.1 首先根据上文传下来的setting又创建了一个Environment,(为啥不知道)
2.2 接着开始配置log。还是依据环境中的log路径
加载的log plugin名字叫org.elasticsearch.common.logging。
和log相关的配置文件为log4j2.properties,es会加载该文件并解析里面的配置
默认log的level是Level.ERROR,但是通过logger.level配置,如果进行了设置会对默认值进行覆盖,所以也是可以调整该默认值的
2.3 检查Lucene的版本是否正确
2.4 开始处理modules模块
共49个。每一个都被封装为PluginInfo对象
但是并不做其他处理,只有一个符合条件作为进程被start
2.5 接着检查和设置系统的一些资源
比如不能是root用户在操作
如果是Linux平台尝试申请最大数量的线程数,就是申请开线程的时候不会受到数量的限制。
2.6 为Environment初始SecurityManager
es自定义了ESPolicy,并启动SecurityManager.(没看懂)
3、开始创建Node对象
Node表示的是集群中的一个节点,此时的setting信息
{"client.type":"node","cluster.name":"elasticsearch","node.name":null,"path.home":"/Users/xiaohuihui/Downloads/elasticsearch-master/build/elasticsearch-distros/extracted_elasticsearch_8.0.0-SNAPSHOT_archive_darwin_default/elasticsearch-8.0.0-SNAPSHOT","path.logs":"/Users/xiaohuihui/Downloads/elasticsearch-master/build/elasticsearch-distros/extracted_elasticsearch_8.0.0-SNAPSHOT_archive_darwin_default/elasticsearch-8.0.0-SNAPSHOT/logs"}
这个setting有什么用呢?在下面有些场景进行判断的时候会用到。
3.1 把对modules和plugin的处理封装为一个叫做PluginsService的对象,创建它的过程就是加载module和plugin的过程。创建完成后继续返回到Node的创建过程,并把PluginsService对象赋值给Node中的一个变量,接着就是将这些插件的角色抽取出来,放到内存中,使用叫做roleMap的Map保存
3.2 创建NodeEnvironment。它指的是es home中data的路径,因为data文件夹下还要存放一些和索引相关的文件等。
这里的NodeEnvironment指的就是红色框框出来的内容。
同时会检查对文件夹是否有写入权限,是否有原子移动权限
会按照data的路径将磁盘上的文件加载到内存中,封装成NodeMetadata对象,这个对象是什么意思呢。它存在于当前节点的data文件夹中,如果es实例重复启动,它依旧会存在。这个是可以验证出来的。当你启动es的时候,如果上次你进行过操作,会发现data文件夹中是有记录的。如果第一次启动es,data文件夹是空的。如果该对象有值,就如下面所示
NodeMetadata{nodeId='zTVfnC6sRReXE9mbtqd6Aw', nodeVersion=8.0.0}
这样NodeEnvironment就创建完成了。node对象使用引用nodeEnvironment指向它。
接下来又创建了一个对象LocalNodeFactory,根据名字是本地节点工厂,不过是依据nodeId和setting构建的,暂时还没看到到如何发挥作用
接下来创建了一个ThreadPool对象,不要被它的名字迷惑,这里可不是创建了一个线程池,而是创建了好几个线程池。具体创建了多少个呢?27个,而且每个线程池都有自己的名字,对每一个模块都创建一个线程池,其实正常我们的业务系统中也是这样使用的,不可能全局共用一个,而且每个模块用自己的,互不干扰。es也是如此
而内部类ExecutorHolder才是我们熟知的线程池,我们看它的构造方法
static class ExecutorHolder {
private final ExecutorService executor;
public final Info info;
ExecutorHolder(ExecutorService executor, Info info) {}
就更确定了这一点
不过毕竟是对线程池做了封装,es对线程池也是分类型的,
DIRECT("direct"),
FIXED("fixed"),
FIXED_AUTO_QUEUE_SIZE("fixed_auto_queue_size"), // TODO: remove in 9.0
SCALING("scaling");
fixed和scaling的差别主要是在线程数上
而在构建Node的整个过程中是如何处理资源的释放的呢?
这里的资源释放指的是读取磁盘数据的IO请求,创建线程池等内存线程资源,使用一个list管理。在代码继续往下走的过程中收集那些在出现异常情况回收的资源,而不是每个需要回收的资源的try finally等代码块,比如
resourcesToClose.add(() -> ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS));
resourcesToClose.add(() -> HeaderWarning.removeThreadContext(threadPool.getThreadContext()));
关注公众号,帮忙涨个粉