1.概述

转载:[8]elasticsearch源码深入分析——Node与NodeEnvironment的实例化

2.Node概览

前不久的分析中说到了,NodeElasticSearch启动的重中之重,一个Node代表在一个集群(cluster.name)中的一个节点。为了使用客户端对集群进行操作,客户端可以使用Node中的client()来取得org.elasticsearch.client.Client的实例。

任何时候,启动一个elasticsearch实例都是启动Node的一个实例,多个Node实例的集合叫做Cluster

集群中的节点默认都可以使用HTTPTransport两种方法通信。transport的通信可以使用Java TransportClient,而HTTP就只能使用Rest Client了。

集群中的Node都能相互发现,并转发请求到合适节点。而且每个Node会有以下的一个或多个作用:

  1. 通过设定node.master属性值为true(true为默认值)被选举为Master节点
  2. 通过设定node.Data属性值为true(true为默认值)来充当数据节点,顾名思义,这种节点持有数据且能做数据的关联操作
  3. 通过设定node.ingest属性值为true(true为默认值)来充当ingest node。ingest node是5.0新增的特性,简单点说就是elasticsearch内置的数据处理器,目前提供了convert,grok之类的操作,相信用过Logstash的同学一定不会陌生。
  4. 通过设置tribe.属性来使node成为Tribe node*,它是一个特殊的客户端,它可以连接多个集群,在所有连接的集群上执行搜索和其他操作

Node类首先构造了三个Setting<Boolean>属性,分别是:

属性名 key值 作用
WRITE_PORTS_FILE_SETTING node.portsfile 用于控制是否将文件写入到包含给定传输类型端口的日志目录中
NODE_DATA_SETTING node.data 使该node被选举为data节点
NODE_MASTER_SETTING node.master 使该node被选举为master节点
NODE_INGEST_SETTING node.ingest 使该node被选举为ingest节点
NODE_LOCAL_STORAGE_SETTING local_storage 控制节点是否需要持久化元数据到磁盘,这和data node没有必然联系,但是如果local_storage为false,node.data和node.master的值必须为false
NODE_NAME_SETTING node.name 节点名称
NODE_ATTRIBUTES node.attr. 添加gateway,zone,rack_id等参数key
BREAKER_TYPE_KEY indices.breaker.type 断路器类型,提供参数有hierarchy,none两种,主要是防止内存溢出后elasticsearch宕机

2.Node实例化

三个Node的构造参数:

Node的构造参数
最重要的构造方法是:

protected Node(final Environment environment, Collection<Class<? extends Plugin>> classpathPlugins)

该构造方法所做的工作:

  1. 用当前节点名称设定临时Logger(因为后续可能节点名称会变动所以设定成临时Logger
  2. 根据参数environment中的settings变量构造新的settings实例,添加默认的CLIENT_TYPE="node"值。
  3. 用生成的新的settings实例和environment参数构建新的节点环境(NodeEnvironment)
  4. 构造plugins
  5. 加载LocalNodeFactory
  6. 构造ThreadPool,接收参数为setting和plugins的builder
  7. 构造scriptModule,analysisModule,settingsModule
  8. 通过pluginsService构造NetworkService
  9. 通过pluginsService构造ClusterPugins
  10. 构造IngestService
  11. 构造DiskThresholdMonitor
  12. 构造ClusterInfoService
  13. 构造UsageService
  14. 实例化ModulesBuilder
  15. 通过pluginsService构造SearchModule
  16. 通过settingsModule构造CircuitBreakerService
  17. 构造ActionModule
  18. 构造NamedXContentRegistry
  19. 构造MetaStateService
  20. 构造IndicesService
  21. 构造RestController
  22. 构造NetworkModule
  23. 构造MetaDataUpgrader
  24. 构造TransportService
  25. 构造ResponseCollectorService
  26. 构造SearchTransportService
  27. 构造DiscoveryModule
  28. 构造NodeService
  29. 向构造好的ModuleBuilder中添加所有需要的服务
  30. 通过ModuleBuilder得到Guice注入类
  31. 构件LifecycleComponent集合
  32. 初始化NodeClient

我们的源码解析也会按照这个流程来开展。

2.1 构建默认的Setting

在Node刚开始构造的时候,这个时候Node对象中还没有存在Setting实例的,有的配置只有在BootStrap方法中传过来的Environment实例,这个Envi的实例(environment)其实就是解析了启动环境中若干的配置路径(lib路径,module路径,logs路径),在对environment的setting化后(调用Environment的settings()方法,就是对初始的环境变量标准化为Settings类型的对象),如下图:

在构造完这个最初始版本的Settings后,代码视图取得配置中的node.name,为什么会在Node刚开始初始化的时候就去查找node的name呢?在跟进源码后会知道,ElasticSearch这么做是为了给Logger的实例增加marker这个参数,相信对log4j熟悉的同学会对这个参数很熟悉,merker是log4j中LayoutPattern的参数之一,作用是event元素中的标记元素,这种标记元素仅在日志消息中使用标记时出现,且具有继承性。如下图:

logger中的marker元素

当然如果配置了node.name,且在log4j.properties中配置了属性appender.console.layout.pattern包含元素%marker,那么在控制台中会很容易看到形如下图中的日志打印,这就能很容易区分出日志的归属Node

logger中的marker
当然到这里我们都还没给Node设置名称。

接下来给Node设置了client.type的值为node,这个也是写在代码里的配置。

private static final String CLIENT_TYPE = "node";

接下来开始就开始构建NodeEnvironment实例了。

2.2 NodeEnvironment的实例化

首先说明EnvironmentNodeEnvironment是没有任何继承关系的,只是在NodeEnvironment的实例化过程中,Environment作为了构建所必需的参数。NodeEnvironment主要是针对单个节点的包含所有数据路径的构件对象,说白了这个类就是xxx,直接看NodeEnvironment构造函数。构造函数中通过累加possibleLockId的值来新增数据存储的路径,这个值是从0开始的,所以才会在ElasticSearch的数据存储页面生成如下图的文件夹:

数据存储路径

接下来使用FSDirectory.open(dir, NativeFSLockFactory.INSTANCE)获取存储索引的目录,FSDirectory是对文件系统目录的操作

  1. 第一个参数java.nio.file.Path:dir这个参数是NIO的一个类Path,接收字符串参数创建的。
  2. 第二个参数org.apache.lucene.store.LockFactory:这个参数是Lucene中的索引锁。因为Lucene必须知道一份索引是否已经被某个IndexWriter打开,所以必须使用锁的机制来保证写索引的同步性。首先大家要明确一个问题,在ElasticSearch异常退出,或是JVM异常关闭的情况下,在下次重启ElasticSearch,索引依然能够正确读写,就是这么神奇。这是怎么实现的呢?秘密就在这个NativeFSLockFactory.INSTANCE参数中,他是FSDirectory提供的默认锁,他的最大优势就是当程序异常退出后,可以由操作系统负责解除索引的锁,操作系统会释放文件上所有的引用,以确保索引可以正确读写。LockFactory还提供了其他类型的锁,由于涉及到Lucene的深层次知识点,这里就不展开叙述。

通过locks[dirIndex] = luceneDir.obtainLock(NODE_LOCK_FILENAME);取得锁后生成一个内部类NodePath的实例,到这里锁就持久化到磁盘上了。

补充一句,这个地方涉及到了ElasticSearch的参数max_local_storage_nodes,这个配置限制了单节点上可以开启的ES存储实例的个数,如果我们需要开多个实例,就要把这个配置写到配置文件中,并为这个配置赋值为2或者更高,这样的话ElasticSearch就会用for循环创建多个NodePath,而不只是创建唯一的那个ID为0的实例。

在NodeEnvironment中加载或创建Node元数据

接下类是构造NodeMetaData节点元数据,这个元数据有个关键数据叫nodeId,构造出来后是形如D2_COg3LTUeQcrYjcj_fQQ这样的字符串。

程序执行到这个地方,其内部类NodePath的对象里已经保存了节点目录xxxx\data\nodes\0和节点索引目录xxxx\data\nodes\0\indices,如下图所示:

程序首先通过DirectoryStream<Path> paths = Files.newDirectoryStream(stateDir)遍历data\nodes\0_state文件夹下的状态文件,再通过匹配正则表达式\Qnode-\E(\d+)(.st)?,查找到状态文件node-xxx.st

注意,如果有多个数据存储路径,那么状态文件夹下可能会有多个最新状态版本。这种情况下,只会取最高的版本。如果至少有一个状态文件使用了新的格式(format,也就是编码中的legacy==false),那么最新的状态文件肯定是最新的的格式(format)。如果不是使用最新的状态文件,那编码中的pathAndStateIds值是空的,且会在日志中报加载状态文件失败的错误。

最后从node-xxx.st文件中读出ID,至此NodeMetaData对象的nodeId字段就被赋值了。而这个ID的前缀也被作为Loggermarker值被注入。

至此nodeEnvironment = new NodeEnvironment(tmpSettings, environment);的工作就结束了,总而言之就是载入了状态参数到内存中。

此处的state文件应该和这个问题 有关

org.elasticsearch.cluster.block.ClusterBlockException: blocked by: SERVICE_UNAVAILABLE/1/state

【es】Node与NodeEnvironment的实例化相关推荐

  1. 【Elasticsearch】es node 节点

    1.概述 [Elasticsearch]es如何停用节点 [ES]ES 如何在一个机器上同时模拟多个node [elasticsearch]elasticsearch集群更换节点操作 [Elastic ...

  2. 【正则表达式】正则表达式

    1.概述 转载:面试官:说说你对正则表达式的理解?应用场景? 一.是什么 正则表达式是一种用来匹配字符串的强有力的武器 它的设计思想是用一种描述性的语言定义一个规则,凡是符合规则的字符串,我们就认为它 ...

  3. ES failed to notify ClusterStateListener java.lang.IllegalStateException: environment is not locked

    ES出现异常: failed to notify ClusterStateListener java.lang.IllegalStateException: environment is not lo ...

  4. Kubernetes 搭建 ES 集群(存储使用 local pv)

    推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...

  5. Kubernetes 搭建 ES 集群(存储使用 cephfs)

    推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...

  6. elasticsearch.client.transport.TransportClientNodesService[420] - node {#transport#-1}

    最近启动项目,发现一直提示 ES node问题,即elasticsearch连接客户端后,创建索引index时报错如下,提示没有获取到可用的节点. 错误信息如下: elasticsearch.clie ...

  7. 常用服务的集群搭建(redis、MQ、es、zookerper)

    常用服务的集群搭建(redis.MQ.es.zookerper) 1.集群概述 1.1什么是集群 1.1.1集群概念 集群是一种计算机系统, 它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地 ...

  8. ElasticSearch(ES)

    文章目录 备注.必看 ElasticSearch 和 Solr 介绍,对比 ElasticSearch 7.8. 官网 基本概念 Cluster(集群) 与 Node(节点) Index -索引 索引 ...

  9. 从安装docker到部署es集群管理的艰辛之旅

    前言 好久没有更新博客了 , 最近也是一直在忙 , 上班搬砖 , 下班深造自己 ; 偶尔打打王者 , 哈哈 , (留言带你们飞 , 哈哈) 最近呢 , 因为加了台新的服务器 , 需要把现有的es集群全 ...

最新文章

  1. android fragmentpageradapter切换不更新,关于android:在FragmentPagerAdapter中更新当前片段...
  2. iOS支付宝支付集成
  3. 学习Java的几大难题,你们都解决了吗?
  4. 数据仓库之电商数仓-- 1、用户行为数据采集
  5. 强烈推荐!入门大数据分析必看的知识点总结,适合零基础学习
  6. Docker系列一之基础快速入门企业实战
  7. 传统接口写法与Restful API 区别
  8. 实战PHP数据结构基础之队列
  9. java序列化,看这篇就够了
  10. 文件或目录损坏且无法读取怎么办?数据恢复用这招
  11. 中国生物oa系统服务器地址,每周一库 | 第十八期:中国生物医学文献服务系统(SinoMed) | 四川大学图书馆...
  12. 老飞飞秒进卡尔贝西龙洞不用走路的方法
  13. 最新阿里云短信服务接口类【亲测成功】
  14. python+selenium高级教程
  15. mysql数据库一些常用操作
  16. Git-2.12.0-64-bit .exe下载持续更新最新版下载
  17. 三国志战略版:斩首骑——张辽与王元姬不得不说的故事
  18. 双回路隔离输出DC/DC高压电源模块
  19. 这一波再抢不到微信红包封面,就只能怪你自己了
  20. T46 Keithley 2612如何输出高电压?

热门文章

  1. 最便宜iPad曝光 苹果给力了!
  2. 微信Windows版本升级 可以浏览朋友圈了
  3. 中国网民人均安装63款App:被腾讯支配的恐惧...
  4. 小米10正式发布:3999元起!
  5. 售价上4000?小米10 曝光:性能强大 值得期待
  6. 王思聪被传成“老赖”, 北京二中院:假的!
  7. 这家公司疑被查封、员工被带走,股东李笑来称“做天使投资人真不容易”
  8. 现男友代言!最强自拍手机荣耀20S正式发布:售价1899元起
  9. 中国移动或于8月底推出“达量不限速”套餐 满足用户需求
  10. 腾讯终于有动作了:国行版Switch快来了...