实现elasticsearch网关,兼容不同版本elasticseach读定请求,滚动升级
实现elasticsearch网关,兼容不同版本es,滚动升级
通用ES读写特征
-
读特征,以_search结尾
localhost:9200/[index_name]/[type_name]/_search?a=b&c=d
localhost:9200/filebeat_2020*/_search
localhost:9200/filebeat_202001*,filebeat_202002*/_search
索引按年月日分割,查询方式以url限制时间段和type,并不在查询的body里添加index或type的查询条件,只是在url里传入,这减少了分发的实现难度,只用管url就可以
因为6.8只支持单type,index_name->type_name 关系为1:1,很容易维护已有的index和type的映射
为了描述简单,我们假设index_name
为 filebeat_202003_log,filebeat_202003_metrics 最后的_后字符串即为type_name
-
写特征,大批量写入通常用bulk
localhost:9200/_bulk
官方示例为
[Bulk API | Elasticsearch Reference 7.11] | Elastic
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
[Bulk API | Elasticsearch Reference 6.8] | Elastic
6.8和7.X的区别是需要传入_type
参数
POST _bulk
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
方案介绍
es 升级迁移数据成本较高,考虑以remote的方式实现,不迁移旧版本数据至新集群而采用新旧集群多版本串联的方式,逐步升级
因为索引的特征是按月分割,假设以2021年3月分割完成
202103及之前的月度数据都存储在old es cluster 6.8.14内
202103之后的月度数据存储在new es cluster 7.10.2
es地址 | 版本 | 索引数据 |
---|---|---|
old_es:9200 | 6.8.14 | 202103之前,例filebeat_202003 |
new_es:9200 | 7.10.2 | 202103之后,例filebeat_202004 |
现集群读写
读写 -> old es cluster 6.8.0
操作方式
- 布署新集群es 7.10.2
- 在 es 7.10.2内 注册旧集群es 6.8.14 为remote cluster 考虑url长度问题,remote cluster name 为单字符 o,含义是old
- 启动网关服务
- 将所有对es集群的日常读写(仅限_search,bulk,scroll)请求,提交至网关
- 网关接收请求url,并根据外部维护的索引信息,变更url为真实的url,提交至新es 7.10.2
升级后集群读写
流程图
读写->
[自实现网关,重写request url,request body]->
新es 7.10.2->
旧数据的访问通过es remote cluster
分别举例如下,只讲最简单的几项,更复杂和细节的url,以此类推
_search 读
请求es6.8.14 原始url
es6:9200/filebeat_202103_log,filebeat_202104_log/log/_search
重写为
es7:9200/o:filebeat_202103_log,filebeat_202104_log/_search
filebeat_202103_log 在es6内,es6 做为name为o的remote cluster 注册在es7内
通过o:filebeat_202103_log,es7会调用es6 获取数据
以上是只在url里限制index,type的情况,如需在request_body里添加条件,也需更改body内容
_bulk 写
bulk 需要重写body,另es不支持通过remote cluster写入,需要将请求拆分
这里有两个方向的调整
-
是以6.8.14的为准,变更为兼容7.10.2
优点是,写入方不需要做任何变化,统一在网关层做适配
但最终所有服务都要以7.10.2的规范为标准做改造
-
还是以7.10.2的为准,变更为兼容6.8.14
整体上和6.8.14的优缺点相反
其实两类方案并不互斥,可以都做
网关公开两个host:port ,分别提供方案1和方案2的服务
-
方案1,接收6.8.14标准的请求,为兼容老服务
接收
POST _bulk
{ "index" : { "_index" : "filebeat_202103_log", "_type" : "log", "_id" : "1" } }
{ "field1" : "value1" }
{ "index" : { "_index" : "filebeat_202104_log", "_type" : "log", "_id" : "2" } }
{ "field1" : "value1" }
对分别属于es6,es7的index拆分为两个请求
es6
POST _bulk
{ "index" : { "_index" : "filebeat_202103_log", "_type" : "log", "_id" : "1" } }
{ "field1" : "value1" }
es7
{ "index" : { "_index" : "filebeat_202104_log", "_id" : "2" } }
{ "field1" : "value1" }
-
方案2,接收7.10.2标准的请求,接入适配完7.10.2的服务
接收
POST _bulk
{ "index" : { "_index" : "filebeat_202103_log", "_id" : "1" } }
{ "field1" : "value1" }
{ "index" : { "_index" : "filebeat_202104_log", "_id" : "2" } }
{ "field1" : "value1" }
对分别属于es6,es7的index拆分为两个请求
es6
POST _bulk
{ "index" : { "_index" : "filebeat_202103_log", "_type" : "log", "_id" : "1" } }
{ "field1" : "value1" }
es7
{ "index" : { "_index" : "filebeat_202104_log", "_id" : "2" } }
{ "field1" : "value1" }
两种方案只是接收格式不同,拆分的结果是一致的
返回内容也需要根据分别对es6,es7请求的结果,合并统计,再以es bulk response的标准返回给client
最后,可以结合外部存储,例如sql来维护index的版本信息,最近月的数据在es7,其他索引默认在es6
另外执行必要的index迁移服务,迁移某索引至7.10.2,则添加该索引的信息,使路由到es7
方案已经确定,然后开始实施
实施前评估主要的技术点
1 读_search request url的解析适配
2 写 bulk的body解析,及拆分
3 拆分为对es6,es7的请求,如果采用es 的sdk 可能有不兼容的问题,必要时直接拼接json http request 而不使用sdk
3 合并分发bulk的response 为es标淮的bulk response
4 mysql 维护index 索引位置信息,及可选的type信息
5 服务不能每次都查mysql,需简单做个缓存
6 限流,限频,熔断,线程池请求队列等暂不做考虑
7 gc是个需要留意的项
8 高并发量下的瓶颈问题
风险点
已经做过些可行性验证,但未必能覆盖全量数据,会有功能开发完成,但部分请求不适用的情况隐患