ElasticSearch的gateway分析
来源:互联网 发布:淘宝能寄到印尼吗 编辑:程序博客网 时间:2024/05/20 03:45
ElasticSearch的gateway功能,官方上的解释为时间机器。当集群整体down掉的时候,就好比时间机器一样进行数据的恢复。因此ElasticSearch的gateway模块就是为了集群的整体数据恢复服务的。
在上篇博客ElasticSearch的shard迁移中简单讲到了ElasticSearch的数据迁移,其实它算是当集群中有部分节点down掉后的局部恢复功能。本篇博客简要分析整体数据恢复gateway功能模块,主要针对的是Local方式。
仔细分析ElasticSearch生成的数据,会发现包括三方面的数据:state、index、translog。其中state存储的是集群中的状态信息,index是lucene的索引文件,而translog是ElasticSearch为lucene添加的日志文件。在上篇博客中,数据迁移只用到了index和translog。这里面讲到的gateway功能这三种数据全部用到了。
Lucene的索引恢复是比较简单的,只是将IndexWriter的打开模式设置为append即可。然而ElasticSearch是分布式的系统,它将数据打碎了分配到不同的shard上,这种数据恢复就很有难度了。
这里我认为的难点:
1) Elasticsearch 如何恢复路由信息,将旧的shard数据一一对应上。(否则数据是不可用的)
2) Elasticsearch如何保证的主副本数据的一致性。(在数据迁移的时候也有这方面的问题,上篇博客没有讲清楚,有遗漏点,后期补充。)
3) Elasticsearch是如何利用translog里面的数据的,它的读取时机是什么时候。
首先简要提下state文件是如何写入的。这里可以参考LocalGateway、LocalGatewayMetaState和LocalGatewayShardsState类,注意到它们都是ClusterStateListener的实现类。因此是当集群状态有变化时会自动存储的。
调试代码可以发现:global文件中的信息是对应的MetaData对象。Index的state文件信息是存储的IndexMetaData对象。而shard的state文件存储的是ShardStateInfo对象。经过分析发现这些对象中都是没有存储集群的路由表信息的。那么Elasticsearch是如何恢复路由信息的?下面引出Elasticsearch恢复路由信息的具体过程。
在Elasticsearch节点启动的时候,会执行InternalNode的start方法。代码如下:
……
……
DiscoveryService discoService = injector.getInstance(DiscoveryService.class).start();
// gateway should start afterdisco, so it can try and recovery from gateway on "start"
injector.getInstance(GatewayService.class).start();
……
……
我们只关注上面的两行代码。上面的两行代码关系到Elasticsearch的gateway功能。首先,是DiscoveryService服务,该服务的作用是发现节点、发现集群;并将自己加入集群;管理并维护各个节点间的心跳等信息。该服务的一系列行为会触发集群变更路由表状态,即调用clusterService.submitStateUpdateTask方法。此时集群路由的状态可能是这样的:
{
"master_node" : "CYyxDyGxQjWtrQXKWPHwXQ",
"blocks" : {
"global" : {
"1" : {
"description" : "state not recovered / initialized",
"retryable" : true,
"disable_state_persistence" : true,
"levels" : [ "read", "write", "metadata" ]
}
}
},
"nodes" : {
"-gQwFu1NRUSoJp9dV_Ok7Q" : {
"name" : "Dreadnought",
"transport_address" : "inet[/192.168.1.1:9302]",
"attributes" : { }
},
"gr3Lt9RPSTSygKKoGGqRMw" : {
"name" : "Zuras",
"transport_address" : "inet[/192.168.1.1:9301]",
"attributes" : { }
},
"CYyxDyGxQjWtrQXKWPHwXQ" : {
"name" : "Gertrude Yorkes",
"transport_address" : "inet[/192.168.1.1:9300]",
"attributes" : { }
}
},
"metadata" : {
"templates" : { },
"indices" : { }
},
"routing_table" : {
"indices" : { }
},
"routing_nodes" : {
"unassigned" : [ ],
"nodes" : {
"-gQwFu1NRUSoJp9dV_Ok7Q" : [ ],
"gr3Lt9RPSTSygKKoGGqRMw" : [ ],
"CYyxDyGxQjWtrQXKWPHwXQ" : [ ]
}
},
"allocations" : [ ]
}
注意到,这个时候路由表已经有一部分信息了。在这张不完整的路由表里存储了node的地址等信息。然而没有索引分片等信息。这是执行DiscoveryService服务所得到的路由结果,算是为gatewayService铺路。
接下来,gatewayService会判断一些条件,包括最少启动节点数、时间等信息,当满足条件后,gatewayService会根据上面生成的路由表,去各个点请求state信息。
可以参考LocalGateway的performStateRecovery方法,在该方法中master会获得各个节点汇聚过来的state信息,找到各个节点中version最高的index state信息。可以恢复出:系统中有多少index,每个index的具体配置是什么情况(包括分配多少shard、每个shard多少副本等)然后计算路由,此时可能会生成如下的路由表:
{
"master_node" : null,
"blocks" : { },
"nodes" : { },
"metadata" : {
"templates" : { },
"indices" : {
"codeingappleTestIndex" : {
"state" : "open",
"settings" : {
"index.version.created" : "1000001",
"index.number_of_replicas" : "1",
"index.number_of_shards" : "2"
},
"mappings" : {
"tweet" : {
"properties" : {
"message" : {
"type" : "string"
},
"postDate" : {
"format" : "dateOptionalTime",
"type" : "date"
},
"user" : {
"type" : "string"
}
}
}
},
"aliases" : [ ]
}
}
},
"routing_table" : {
"indices" : { }
},
"routing_nodes" : {
"unassigned" : [ ],
"nodes" : { }
},
"allocations" : [ ]
}
注意了,上面的路由表已经具备了索引的详细配置信息。根据上面的两张表,GatewayService的GatewayRecoveryListener会再次更新集群路由状态信息,此时会进行shard的routing算法。注意routing部分,会执行GatewayAllocator.allocateUnassigned()方法,这是一个接口方法,在LocalGatewayAllocator实现类里面,我们可以发现该方法优先分配P shard到具体的node节点上。分配时是向各个node发起请求,查询到集群中node上指定shardId的ShardStateInfo信息,然后将当前P shard分配到shard version最大的node上去。由此完成了对P shard的分配。后面就调用其他路由算法将所有未分配的shard通通分配一遍。
经过上面的处理,一个完整的路由表就会建立起来。此时仅仅用到了state类型的文件(注:还有文件夹结构),这只是完成了gateway的第一步操作。我们发现,在这步操作中,我们保证了P shard分配到原来down掉的集群中版本最高的shard所在node节点上(这种node一定也是集群中最后死掉的node,也就是数据最新的node)。
算出路由表后,开始gateway的第2步操作,根据路由表的信息对数据进行恢复。这时候就会用到index文件和translog文件。
在上篇博客中重点介绍了一个IndicesClusterStateService类,gateway的数据恢复部分还在该类中。和数据迁移一样,也是INITIALIZING状态的shard作为数据恢复的发起者。注意到IndicesClusterStateService类的applyInitializingShard方法,在该方法中是大致如下的逻辑:
……
……
if (!shardRouting.primary()) {
// recovery from primary
……
// only recover from started primary, if we can't find one, we will do it next round
……
} else {
if (shardRouting.relocatingNodeId() ==null) {
// we are the first primary, recover from the gateway
// if its post api allocation, the index should exists
……
调用shardGatewayService.recover方法进行数据的恢复
} else {
//进行数据迁移
}
}
在集群启动阶段,Elasticsearch的路由表是不断变化的,是一个迭代过程,节点可能会反复收到路由表信息。从上面的逻辑可以看出,当节点中有INITIALIZING的R shard时,它不先启动,而是找到started状态的P shard,然后从P shard上拷贝数据。这一点其实在启动时保证了数据的一致性。当started状态的Pshard找不到时,就什么也不做了,等待下一次接受到路由表信息再进行激发操作。当节点中有INITIALIZING的P shard时,这个时候就要进行判断了,此时的P shard状态是处于数据迁移过程,还是处于启动阶段。Elasticsearch巧妙的运用了shardRouting.relocatingNodeId是否为空进行判断。在启动阶段路由表中的relocatingNodeId一定是空值的。因此这样的P shard会调用shardGatewayService.recover方法将数据从本地gateway中恢复过来。具体的恢复方法的实现可以参考LocalIndexShardGateway. Recover方法。在该方法中用到了index文件和translog文件进行数据的恢复。
这里有一点思考,为什么Elasticsearch要采用重新计算的方式,而不是整个将路由表存储起来,集群启动的时候直接恢复呢?我认为有2点原因,其一:Elasticsearch的nodeid每次启动都是新算的,是没法和旧状态一致的;其二:这种方式较通过整体保存路由表再读出恢复的方式更加灵活。(当集群全部down后,只启动其中的部分节点也是可以的,当然了也是有数据丢失的风险的)
以上就是我对Elasticsearch的gateway功能中local部分代码的一些分析总结,是基于其0.93版本。对于某些问题点进行试验也是比较困难,有错误的地方,欢迎大家留言或发邮件给我,我将及时改正。我的邮箱是:codingapple@126.com。同时如有人需要转载此文,请注明出处,最好放上本人的邮箱地址,方便大家提出问题与我沟通。
- ElasticSearch的gateway分析
- elasticsearch源码分析之Gateway(六)
- elasticsearch源码分析之Gateway(六)
- ElasticSearch的Gateway及存储原理
- ElasticSearch的Gateway及存储原理
- ElasticSearch的Gateway及存储原理
- ElasticSearch的Gateway及存储原理
- elasticsearch之gateway模块
- Elasticsearch的核心概念cluster/shards/replicas/recovery/gateway/discovery.zen/transport/settings/mapping等
- Nginx 502 Bad Gateway错误的原因分析与解决方法
- elasticsearch的Search Type类型分析
- mysql转ElasticSearch的案例分析
- elasticsearch和hadoop集成,gateway.type hdfs设置
- gateway
- ElasticSearch学习8_在elasticsearch.yml中设置gateway.expected_nodes控制集群数据恢复
- Cloud Foundry Service Gateway源码分析
- Cloud Foundry Service Gateway源码分析
- 查看linux的 default gateway
- weblogic中web.xml中DWR Servlet加入跨域调用功能
- Android - 文件读写操作 总结
- GridView导出数据到Excel
- 图文并茂 在MyEclipse 8.6上搭建Android开发环境
- 文件io(一)--unix环境高级编程读书笔记
- ElasticSearch的gateway分析
- 在linux中如何安装文件
- godaddy域名绑定DNSPOD解析
- wsprintf()函数
- Html框架使用详解
- 在redhat6.4 64位 中安装firefox浏览器的flash插件
- yii中登录后跳转回登录前请求的页面
- 河南财大计算机与信息工程学院CSDN社团 教学活动
- 成员变量[default] ===局部变量[NO]=== 初始化