Elasticsearch Write Debug 详细记录

Debug说明
  • 记录ES写入数据的整个Debug流程,具体实现细节没有做过多解释说明
  • Debug的索引,设置1shard、0replicas,便于理清主线
  • 先创建索引,其次再执行写入;因此这里Debug的过程暂时不涉及创建索引的操作
  • ES version:5.6.16
  • 一个ES节点(master and data)
Debug记录
## 创建索引
curl -XPUT localhost:9200/twitter -H "Content-Type:application/json" -d '{"settings":{"index":{"number_of_shards":1, "number_of_replicas":0}}}'

## 执行具体的写入操作
curl -XPOST localhost:9200/twitter/doc -H "Content-Type:application/json" -d '{"name":"tony","age":13}'

Elasticsearch接口主要以RestAPI的方式向外提供,上述我们通过curl的方式执行的写入操作即属于RestAPI;ES源码中所有Rest请求的父类均为BaseRestHandler,因此这里我们在如下的位置打上DEBUG断点开始调试:

通过curl发送写入请求,进入Debug流程;当前handleRequest函数主要是对请求参数以及请求体做一些操作,因此往下执行,进入action.accept(channel)方法中;此时执行流程进入到了RestIndexAction类的channel -> client.index(...)位置,如下:

Step into 上述client.index(...)方法中,此时流程进入到AbstractClient抽象类的index(...)方法内,如下:

接着Step into execute(...)方法内,此时流程进入到AbstractClient抽象类的execute(...)方法内,如下:

接着Step into doExecute(...)方法内,此时流程进入到NodeClient类的doExecute(...)方法内,如下:

接着Step into executeLocally(...)方法内,此时流程进入到NodeClient类的executeLocally(...)方法内,如下:

接着Step into 上述execute(...)方法内,这里可以看到ES将Rest类型的请求转换成了内部的Transport类型,此时执行流程进入到抽象类TransportAction的execute(...)方法内,如下:

接着Step into 上述图片中的execute(...)方法内,此时流程进入到TransportAction类的execute(...)方法内,如下:

接着Step into 请求过滤链中的proceed(...)方法,如下:

TransportAction类的proceed(...)方法,主要是以请求过滤链的方式对一些逻辑进行处理,而且此处proceed(...)方法后面也会再次进入;Step into this.action.doExecute(...)方法,如下:

此时执行流程进入到TransportSingleItemBullkWriteAction类的doExecute(...)方法内,Step into bulkAction.execute(...)方法,如下:

再次经过请求过滤链之后,流程开始进入到处理实体类TransportBulkAction类的doExecute(...)方法内,如下:

该函数主要对请求体中所涉及到的索引进行检测,如果不存在则会执行创建操作;如果索引已存在,则接着往下执行具体的写入操作。这个函数体逻辑还是值得深入研究的,包含的信息很多;这里暂时不对此做分析。由于这里的写入数据是在创建完成索引之后进行的;因此这里不需要当前请求再创建索引了;此时逻辑会进入无需创建索引的操作,如下:

接着Step into executeBulk(...)方法内,此时流程进入到TransportBulkAction类的executeBulk(...)方法,如下:

BulkOperation继承ES的AbstractRunnable抽象类,而AbstractRunnable类实现了Java的Runnable接口,层次关系为:

因此主要的执行逻辑会放在run()方法中,此处Step into BulkOperation类的run()方法内,如下:

接着Step into doRun()方法,如下:

此时,真实进入BulkOperation类的doRun()方法中,方法内做了大量的核心工作,比如maping操作、根据路由信息找出doc对应的shardID等,可以做深入研究;这里直接跳转到shardBulkAction对象的execute(...)方法处,如下:

接着Step into shardBulkAction的execute(...)方法内,执行经过如下:

接着Step into this.action.doExecute(...)方法内,此时执行流程进入TransportReplicationAction类的doExecute(...)方法内,如下:

接着Step into doRun()方法内,此时执行流程进入到TransportReplicationAction类的doRun()方法内,如下:

如果要写入的primary shard所在的节点是当前执行的节点,则执行performLocalAction(...)方法,否则执行performRemoteAction(...);由于当前Debug的ES节点集master与data于一身,因此这里会执行performLocalAction(...)方法,如下:

Step into performLocalAction(...)方法内,执行流程经过:

执行到这里,ES会将具体的写入请求操作发送到具体的data节点上去执行真实的写入,这里Step into sendRequest(...)方法内,执行流程经过:

写入流程执行到这里,算是进入了真正的写入逻辑了;primary shard的写入操作由primary.perform(request)完成,可以Debug进入查看,数据主要写入Lucene中;Step into primary.perform(request)继续跟踪可以完整了解到写入Lucene的整个逻辑,这里暂时不涉及Lucene的写入逻辑。执行完primaryShard的操作后接着需要获取当前最新的cluster state,最后会执行performOnReplicas(...)副本的写入操作。这也验证了ES文档中常说的先写primaryShard,后写replicaShard的顺序。由于这里没有副本写入,因此不会执行performOnReplicas(...)真实的操作。最后successfulShards.incrementAndGet()统计执行写入操作成功的shard数量。到这里整个写入的操作算是执行完毕了(由此处一直往下Step over,代码执行逻辑便会结束)。这就是整个写入的执行逻辑。

Debug小结

上述是从整体上梳理了ES执行写入操作的流程,从最开始的Debug,一直跟踪代码的执行流程,经过哪个类哪个方法,直到最后逻辑的完成。这一篇文档算是给想要研究ES写入流程源码操作的同学的一个宏观上的认知;其中一些实现细节并没有做深入的说明。ES的代码结构很复杂,写入流程也很复杂。在我们宏观上有了对ES写入流程的认知后,后续若要研究某些具体的问题,能够比较快速的深入到具体的类具体的方法内去做深入的研究。从上述的Debug整个流程来看,我们暂时可以学习到如下几点:

  • ES所有Rest请求的Handler类均继承BaseRestHandler
  • ES内部最终都会将用户执行的Rest请求转换成内部的Transport相关的请求处理
  • ES内部抽象出了AbstractRunnable的抽象类,然后在run()方法内定义具体的执行流程与规范,比如doRun、onFailure、onAfter
  • ES协调节点用于接受请求,然后根据路由信息最终要将请求发送到真实的节点上来做执行
  • ES的写入确实是先执行primaryShard的操作,其次再执行replicaShard的写入操作
  • ES的代码结构与逻辑都很复杂,所以我们在调试的过程中要使用一些技巧,方便我们尽快找到核心逻辑
    • 比如这里当前用一个节点即作为master又作为data
    • 为Debug的索引只创建一个primaryShard、0 replicaShard
    • 当然这些只是为了在刚开始接触ES代码时,减少Debug的难度;研究真实的问题时,Debug也是很复杂的

由于自己当时对ES与Java了解不够深,所以整个写入流程的打通花费了自己很长的时间,在此做个文档一方面是记录下来,方便自己以后查看;另一方面也是希望帮到和我当初一样处境的同学,尽快走通流程,然后往下继续做ES源码的深入分析。后续会继续将自己学习ES相关的内容与大家一起分享,一起学习。

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