flink-connector-elasticsearch-source flink elasticsearch source table 集成elasticsearch-hadoop connector开发
flink elasticsearch source table 集成 connector开发
代码 https://github.com/cclient/flink-connector-elasticsearch-source
使用示例,查询条件只能通过es.query指定,flink 通过elasticsearch-hadoop(又封装的scroll) 拉数据(string格式),通过flink table scheme ,json解析并转为table 后 应用flink-sql
支持flink-sql的所有功能,多表的join,开窗函数等
CREATE TABLE flink_es_table_copy_source(
_metadata ROW<_index STRING,_type STRING,_id STRING>,
int_key INT,int_array ARRAY<INT>,int_object MAP<STRING,INT>,int_nested ARRAY<ROW<key_3 INT,key_4 INT>>
) WITH (
'connector.type'='elasticsearch',
'es.resource'='flink_es_table_copy/_doc',
'es.nodes'='127.0.0.1:9200',
'es.port'='9200',
'es.query'='?q=*',
'es.nodes.client.only'='false',
'es.nodes.discovery'='false',
'es.nodes.wan.only'='true'
);
select * from flink_es_table_copy_source;
result
SQL Query Result (Table)
Table program finished. Page: Last of 1 Updated: 11:25:32.615
_metadata int_key
+I[flink_es_table_copy, _doc,~ 10
+I[flink_es_table_copy, _doc,~ 20
Q Quit + Inc Refresh G Goto Page N Next Page O Open Row
R Refresh - Dec Refresh L Last Page P Prev Page
---
_metadata (ROW<`_index` STRING, `_type` STRING, `_id` STRING>):
+I[flink_es_table_copy, _doc, es_id_1]
int_key (INT):
10
int_array (ARRAY<INT>):
[11, 12]
int_object (MAP<STRING, INT>):
{key_2=14, key_1=13}
int_nested (ARRAY<ROW<`key_3` INT, `key_4` INT>>):
[+I[15, 16], +I[17, 18]]
原理上不支持谓词下推,所以大数据量的agg,join开销相对较大
也可以通过分别create flink的es-source-table, es-sink-table,再通过写sql,例 insert into flink_es_table_copy_sink SELECT _id,int_key,int_array,int_object,int_nested FROM flink_es_table
实现es 通过flink的es数据迁移,在经过flink时会严格规范doc的结构
迁移的高性能方案有很多,原生reindex,中间store sharding,备份恢复等,这方式只是测试参考
flink 官方集成了es connector 的sink 整体上完成度很高-在性能上比elasticsearch-hadoop差些,不过接口和应用简单,反而方便定制开发
source 这方面比较冷门,没有官方的方案
因为es本身有自已的query/aggs的dsl语法,新版本官方也集成了sql并可以免费应用
source 的方案并不多
因为flink本身有对hadoop的兼容性支持
个人也较深度的使用过elasticsearch-hadoop,大量的读写应用,及一些源码级的feature添加
对elasticsearch-hadoop较为熟悉
完全从无到有开发一个flink的elasticsearch source connector 成本较高
就想能否结合elasticsearch-hadoop 实现es的source
最终验证可行,各方面特性良好,其间也有很多坑,临时代码很多,最后都精简了,保留了最简单的核心代码
开发过程有以下几个坑
1 flink connector 支持两种表,table和dynamic table,目前大量的connector都改用了dynamic table,只保留了很少一部分的table
起初尝试使用dynamic table实现source,但因为dynamic table的一个方法较难重载(方法提供可操作的入口不多),方法要求提供<rowdata,?>参数,而flink的hadoop兼容层,无法提供
因此放弃使用最新的dynamic table,而是用table的方式实现
2 elasticsearch-hadoop 默认可以提供两种outputformt Text和Map,都是hadoop的writable实现类,但是此Map flink却不能直接解析,必须自已把Map转为java的标准map才可用
试过实现,mapWritable到java.map的转换,不完美,基本放弃了使用mapwritable
试过实现Text转json,再转为map,也不完美,但这时想到,flink本身支持一些序列化,包括json,就想能否用flink-json内的方法实现
3 json序列化,简而言之,json序列化是和dynamic table方法深度集成的,加载创建json序列化类时,需要依赖dynamic table 一些方法的上下文,最直接的是context对象,但是因为问题1 ,我是按table来实现,并没有可用的入口
4 查看dynamic table,json序列化相关的代码,比较幸运的,能脱离context,把json序列化工具实例的构造提取出来
json序列化主要供dynamic table使用,dynamic table的行类是rowdata,而因此官方集成的的序列化工具类(包括json)返序列化出来的是个 rowdata对象
5 rowdata对象 table使用异常,因为table需要的是row对象
因此需要实现rowdata 对象到row对象的转换
这是个坑,思维定势了,按以上的步骤一步步来,转换也想着自已来,又是看源码,又是写代码,又是测试的,搞到半夜1点多,总算把rowdata 到row的转换写完了,但这有个缺陷
方式是遍历rowdata的每一个列,对每一列,判断列的实际类型,判断flink的声名类型,做类型转换后再构造新的row出来,但我只转换了json的最外层结构,对嵌套结构只能以String的方法展示,内层结构需要深度递归,理论上也好实现,精力有限,已经搞到半夜两点,核心问题告一段落,先去休息了
第二天起床转念一想,rowdata和row和转换是应该是比较通用的方法,flink同时支持table/dynamic table, row/rowdata 会不会官方代码内部就有转换代码呢?
文档当然查不到,直接从源码里找
看了一圈可疑代码一查果真有,而且嵌套解析完美
5行代码完全替换掉了我自已实现的,各种类型判断,类型转换又递归的几十行自定义rowdata row转换类
public Row parseToRow(byte[] message) throws Exception {
RowData rowData = deserializationSchema.deserialize(message);
RowRowConverter rowRowConverter = RowRowConverter.create(producedDataType);
rowRowConverter.toExternal(rowData);
Row row = rowRowConverter.toExternal(rowData);
return row;
}
以上几个大问题,中间阶段还有不少的小问题,比如参数构造,参数传递,参数用途...
本来预期会有些问题,只能做到可用,但多少有些应用受限,意外的感觉完成度还挺高
包装了elasticsearch-hadoop,可用elasticsearch-hadoop的所有特性,并实现了source/table,基于table的sql应用良好
同时结合官方sink实现,同时做读写
对es这么应用有些不伦不类,部分场景低效,但还是有一些应用场景的
最大的缺点是性能,因为多了一步row到rowdata的转换,但读es最受限的还是网络io和dsl查询开销上上,性能转换的影响整体可以接受