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

本篇为elasticsearch源码分析系列文章的第八篇,又到了我们深扒ElasticSearch源码的时候了:)

本篇开始将会详细解释Node实例化的过程,从Node实例化这个操作为源点,了解ElasticSearch的编码思想,由于Node内容众多,所以会分篇叙述。

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宕机

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

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

构建默认的Setting

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

Environment的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实例了。

NodeEnvironment的实例化

首先说明EnvironmentNodeEnvironment是没有任何继承关系的,只是在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的实例,到这里锁就持久化到磁盘上了。

node.lock

补充一句,这个地方涉及到了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,如下图所示:

NodePath实例

程序首先通过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);的工作就结束了,总而言之就是载入了状态参数到内存中。

下一篇会讲述pluginsService相关的内容,希望大家持续关注哦^ _ ^。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,629评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,773评论 6 342
  • 明明喜欢你,却又不知从何说起。总是在你面前保持沉默,其实根本就是紧张到说不出话。 ...
    王牌花嫁阅读 282评论 0 0
  • 而今天我想谈谈为什么很多在传统企业工作的新媒体工作者,为什么普遍都认为在传统企业做新媒体不好做呢? 在此之前,我先...
    心旷神怡出来逛阅读 811评论 0 0
  • 不知不觉,从2.28到今天,已经整整两个月了。也是连续写了两个月。虽然有几天太忙碌没来得及写,不过总体还是跟着进度...
    67984f731c4b阅读 141评论 2 2