【es】Node与NodeEnvironment的实例化
1.概述
转载:[8]elasticsearch源码深入分析——Node与NodeEnvironment的实例化
2.Node概览
前不久的分析中说到了,Node
是ElasticSearch
启动的重中之重,一个Node
代表在一个集群(cluster.name)
中的一个节点。为了使用客户端对集群进行操作,客户端可以使用Node
中的client()
来取得org.elasticsearch.client.Client
的实例。
任何时候,启动一个elasticsearch
实例都是启动Node
的一个实例,多个Node
实例的集合叫做Cluster
。
集群中的节点默认都可以使用HTTP
和Transport
两种方法通信。transport
的通信可以使用Java TransportClient
,而HTTP
就只能使用Rest Client
了。
集群中的Node
都能相互发现,并转发请求到合适节点。而且每个Node
会有以下的一个或多个作用:
- 通过设定
node.master
属性值为true
(true为默认值)被选举为Master
节点 - 通过设定
node.Data
属性值为true
(true为默认值)来充当数据节点,顾名思义,这种节点持有数据且能做数据的关联操作 - 通过设定
node.ingest
属性值为true(true为默认值)来充当ingest node。ingest node
是5.0新增的特性,简单点说就是elasticsearch
内置的数据处理器,目前提供了convert,grok
之类的操作,相信用过Logstash的同学一定不会陌生。 - 通过设置
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)
该构造方法所做的工作:
- 用当前节点名称设定临时
Logger
(因为后续可能节点名称会变动所以设定成临时Logger
) - 根据参数environment中的settings变量构造新的settings实例,添加默认的
CLIENT_TYPE="node"
值。 - 用生成的新的settings实例和environment参数构建新的节点环境(NodeEnvironment)
- 构造plugins
- 加载LocalNodeFactory
- 构造ThreadPool,接收参数为setting和plugins的builder
- 构造scriptModule,analysisModule,settingsModule
- 通过pluginsService构造NetworkService
- 通过pluginsService构造ClusterPugins
- 构造IngestService
- 构造DiskThresholdMonitor
- 构造ClusterInfoService
- 构造UsageService
- 实例化ModulesBuilder
- 通过pluginsService构造SearchModule
- 通过settingsModule构造CircuitBreakerService
- 构造ActionModule
- 构造NamedXContentRegistry
- 构造MetaStateService
- 构造IndicesService
- 构造RestController
- 构造NetworkModule
- 构造MetaDataUpgrader
- 构造TransportService
- 构造ResponseCollectorService
- 构造SearchTransportService
- 构造DiscoveryModule
- 构造NodeService
- 向构造好的ModuleBuilder中添加所有需要的服务
- 通过ModuleBuilder得到Guice注入类
- 构件LifecycleComponent集合
- 初始化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的实例化
首先说明Environment
和NodeEnvironment
是没有任何继承关系的,只是在NodeEnvironment
的实例化过程中,Environment
作为了构建所必需的参数。NodeEnvironment
主要是针对单个节点的包含所有数据路径的构件对象,说白了这个类就是xxx
,直接看NodeEnvironment
构造函数。构造函数中通过累加possibleLockId
的值来新增数据存储的路径,这个值是从0开始
的,所以才会在ElasticSearch
的数据存储页面生成如下图的文件夹:
数据存储路径
接下来使用FSDirectory.open(dir, NativeFSLockFactory.INSTANCE)
获取存储索引的目录,FSDirectory是对文件系统目录的操作
- 第一个参数
java.nio.file.Path:dir
这个参数是NIO的一个类Path,接收字符串参数创建的。 - 第二个参数
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的前缀也被作为Logger
的marker
值被注入。
至此nodeEnvironment = new NodeEnvironment(tmpSettings, environment);
的工作就结束了,总而言之就是载入了状态参数到内存中。
此处的state文件应该和这个问题 有关
org.elasticsearch.cluster.block.ClusterBlockException: blocked by: SERVICE_UNAVAILABLE/1/state
【es】Node与NodeEnvironment的实例化相关推荐
- 【Elasticsearch】es node 节点
1.概述 [Elasticsearch]es如何停用节点 [ES]ES 如何在一个机器上同时模拟多个node [elasticsearch]elasticsearch集群更换节点操作 [Elastic ...
- 【正则表达式】正则表达式
1.概述 转载:面试官:说说你对正则表达式的理解?应用场景? 一.是什么 正则表达式是一种用来匹配字符串的强有力的武器 它的设计思想是用一种描述性的语言定义一个规则,凡是符合规则的字符串,我们就认为它 ...
- ES failed to notify ClusterStateListener java.lang.IllegalStateException: environment is not locked
ES出现异常: failed to notify ClusterStateListener java.lang.IllegalStateException: environment is not lo ...
- Kubernetes 搭建 ES 集群(存储使用 local pv)
推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...
- Kubernetes 搭建 ES 集群(存储使用 cephfs)
推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...
- elasticsearch.client.transport.TransportClientNodesService[420] - node {#transport#-1}
最近启动项目,发现一直提示 ES node问题,即elasticsearch连接客户端后,创建索引index时报错如下,提示没有获取到可用的节点. 错误信息如下: elasticsearch.clie ...
- 常用服务的集群搭建(redis、MQ、es、zookerper)
常用服务的集群搭建(redis.MQ.es.zookerper) 1.集群概述 1.1什么是集群 1.1.1集群概念 集群是一种计算机系统, 它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地 ...
- ElasticSearch(ES)
文章目录 备注.必看 ElasticSearch 和 Solr 介绍,对比 ElasticSearch 7.8. 官网 基本概念 Cluster(集群) 与 Node(节点) Index -索引 索引 ...
- 从安装docker到部署es集群管理的艰辛之旅
前言 好久没有更新博客了 , 最近也是一直在忙 , 上班搬砖 , 下班深造自己 ; 偶尔打打王者 , 哈哈 , (留言带你们飞 , 哈哈) 最近呢 , 因为加了台新的服务器 , 需要把现有的es集群全 ...
最新文章
- android fragmentpageradapter切换不更新,关于android:在FragmentPagerAdapter中更新当前片段...
- iOS支付宝支付集成
- 学习Java的几大难题,你们都解决了吗?
- 数据仓库之电商数仓-- 1、用户行为数据采集
- 强烈推荐!入门大数据分析必看的知识点总结,适合零基础学习
- Docker系列一之基础快速入门企业实战
- 传统接口写法与Restful API 区别
- 实战PHP数据结构基础之队列
- java序列化,看这篇就够了
- 文件或目录损坏且无法读取怎么办?数据恢复用这招
- 中国生物oa系统服务器地址,每周一库 | 第十八期:中国生物医学文献服务系统(SinoMed) | 四川大学图书馆...
- 老飞飞秒进卡尔贝西龙洞不用走路的方法
- 最新阿里云短信服务接口类【亲测成功】
- python+selenium高级教程
- mysql数据库一些常用操作
- Git-2.12.0-64-bit .exe下载持续更新最新版下载
- 三国志战略版:斩首骑——张辽与王元姬不得不说的故事
- 双回路隔离输出DC/DC高压电源模块
- 这一波再抢不到微信红包封面,就只能怪你自己了
- T46 Keithley 2612如何输出高电压?
热门文章
- 最便宜iPad曝光 苹果给力了!
- 微信Windows版本升级 可以浏览朋友圈了
- 中国网民人均安装63款App:被腾讯支配的恐惧...
- 小米10正式发布:3999元起!
- 售价上4000?小米10 曝光:性能强大 值得期待
- 王思聪被传成“老赖”, 北京二中院:假的!
- 这家公司疑被查封、员工被带走,股东李笑来称“做天使投资人真不容易”
- 现男友代言!最强自拍手机荣耀20S正式发布:售价1899元起
- 中国移动或于8月底推出“达量不限速”套餐 满足用户需求
- 腾讯终于有动作了:国行版Switch快来了...