Streamworks,袋鼠雲基於SQL的流計算開發平臺,其通過擴展FlinkSQL,實現FlinkSQL與界面化配置映射結合的方式,完成Kafka源數據的讀入,並支持流數據與Mysql/Oracle/MongDB等數據源進行維表關聯,將最終結果數據導出至Hbase/ES/Greenplum/Oracle/OceanBase等目標數據庫,進行一站式的流數據開發。
爲什麼擴展Flink-SQL?
Flink 本身的SQL語法並不提供對接輸入源和輸出目的的SQL語法,數據開發在使用過程中需要根據其提供的API接口編寫Source和 Sink,不僅需要了解FLink 各類Operator的API,還需要對各個組件的相關調用方式有了解(比如Kafka,Redis,Mongo、Hbase等),異常繁瑣。並且在需要關聯到外部數據源的時候Flink也沒有提供SQL相關的實現方式,若數據開發直接基於原生的Flink SQL進行實時的數據分析,需要較大的額外工作量。
袋鼠雲的SteamWorks則聚焦於數據開發人員使用Flink SQL時專注於業務邏輯,只需要關心做什麼,而不需要關心怎麼做。研發團隊對FlinkSQL進行了擴展,用戶只需通過可視化配置,完成源表到導入、維表的關聯、結果表的導出。
擴展了哪些Flink相關SQL
1.創建源表語句
CREATE TABLE tableName(
colName colType,
...
function(colNameX) AS aliasName,
WATERMARK FOR colName AS withOffset( colName , delayTime )
)WITH(
type ='kafka09',
bootstrapServers ='ip:port,ip:port...',
zookeeperQuorum ='ip:port,ip:port/zkparent',
offsetReset ='latest',
topic ='topicName',
parallelism ='parllNum'
);
2.創建輸出表語句
CREATE TABLE tableName(
colName colType,
...
colNameX colType
)WITH(
type ='mysql',
url ='jdbcUrl',
userName ='userName',
password ='pwd',
tableName ='tableName',
parallelism ='parllNum'
);
3.創建自定義函數;
Create (scala|table) FUNCTION name WITH com.xx.xx
4.維表關聯語句;
CREATE TABLE tableName(
colName cloType,
...
PRIMARY KEY(keyInfo),
PERIOD FOR SYSTEM_TIME
)WITH(
type='mysql',
url='jdbcUrl',
userName='dbUserName',
password='dbPwd',
tableName='tableName',
cache ='LRU',
cacheSize ='10000',
cacheTTLMs ='60000',
parallelism ='1',
partitionedJoin='false'
);
各個模塊是如何翻譯到Flink,進行數據處理
1.如何將創建源表的SQL語句轉換爲Flink的operator
Flink中表的都會映射到Table這個類。然後調用註冊方法將Table註冊到environment,StreamTableEnvironment.registerTable(tableName, table)。
當前我們只支持kafka數據源。Flink本身有讀取kafka 的實現類(FlinkKafkaConsumer09),所以只需要根據指定參數實例化出該對象,並調用註冊方法註冊即可。
另外需要注意在Flink SQL經常會需要用到Rowtime、Proctime, 所以我們在註冊表結構的時候額外添加Rowtime、Proctime。當需要用到rowtime的時候需要額外指定DataStream.watermarks(assignTimestampsAndWatermarks),自定義watermark主要做兩個事情1:如何從Row中獲取時間字段。 2:設定最大延遲時間。
2.如何將創建的輸出表sql語句轉換爲Flink的operator
Flink輸出Operator的基類是OutputFormat, 我們這裏繼承的是RichOutputFormat, 該抽象類繼承OutputFormat,額外實現了獲取運行環境的方法getRuntimeContext(), 方便於我們之後自定義metric等操作。
我們以輸出到Mysql插件Mysql-Sink爲例。
分兩部分
(1)將create table 解析出表名稱,字段信息,mysql連接信息。
該部分使用正則表達式的方式將create table 語句轉換爲內部的一個實現類。該類存儲了表名稱,字段信息,插件類型,插件連接信息。
(2)繼承RichOutputFormat將數據寫到對應的外部數據源。
主要是實現writeRecord方法,在mysql插件中其實就是調用jdbc 實現插入或者更新方法。
3.如何將自定義函數語句轉換爲Flink的operator;
Flink對udf提供兩種類型的實現方式:
- 繼承ScalarFunction
- 繼承TableFunction
需要做的將用戶提供的jar添加到URLClassLoader, 並加載指定的class (實現上述接口的類路徑),然後調用TableEnvironment.registerFunction(funcName, udfFunc);即完成UDF的註冊。之後即可使用改定義的UDF;
4.維表功能是如何實現的?
流計算中一個常見的需求就是爲數據流補齊字段。因爲數據採集端採集到的數據往往比較有限,在做數據分析之前,就要先將所需的維度信息補全,但是當前Flink並未提供join外部數據源的SQL功能。
實現該功能需要注意的幾個問題
(1)維表的數據是不斷變化的
在實現的時候需要支持定時更新內存中的緩存的外部數據源,比如使用LRU等策略。
(2)IO吞吐問題
如果每接收到一條數據就串行到外部數據源去獲取對應的關聯記錄的話,網絡延遲將會是系統最大的瓶頸。這裏我們選擇阿里貢獻給flink社區的算子RichAsyncFunction。該算子使用異步的方式從外部數據源獲取數據,大大減少了花費在網絡請求上的時間。
(3)如何將SQL 中包含的維表解析到Flink operator
爲了從SQL中解析出指定的維表和過濾條件,使用正則明顯不是一個合適的辦法,需要匹配各種可能性,將是一個無窮無盡的過程。查看Flink本身對SQL的解析,它使用了calcite做爲sql解析的工作。將sql解析出一個語法樹,通過迭代的方式,搜索到對應的維表,然後將維表和非維表結構分開。
通過上述步驟便可以通過Flink SQL完成常用的從kafka源表讀取數據,join部數據源,並寫入到指定的外部目標結構中,且袋鼠雲開源了相關實現:https://github.com/DTStack/flinkStreamSQL
Demo:Kafka流數據關聯Oracle維表,寫入PostgreSQL 。
讀取Kafka源數據:
CREATE TABLE MyTable(
name varchar,
channel varchar,
pv INT,
xctime bigint,
CHARACTER_LENGTH(channel) AS timeLeng
)WITH(
type ='kafka09',
kafka.bootstrap.servers ='172.16.8.198:9092',
kafka.zookeeper.quorum ='172.16.8.198:2181/kafka',
kafka.auto.offset.reset ='latest',
kafka.topic ='nbTest1,nbTest2,nbTest3',
--kafka.topic ='mqTest.*',
--patterntopic='true'
parallelism ='1',
sourcedatatype ='json' #可不設置
);
維表關聯Oracle語句
create table sideTable(
channel varchar,
info varchar,
PRIMARY KEY(channel),
PERIOD FOR SYSTEM_TIME
)WITH(
type='oracle',
url='jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl',
userName='xx',
password='xx',
tableName='sidetest',
cache ='LRU',
cacheSize ='10000',
cacheTTLMs ='60000',
cacheMode='unordered',
asyncCapacity='1000',
asyncTimeout='10000'
parallelism ='1',
partitionedJoin='false',
schema = 'MQTEST'
);
--創建PostgreSQL輸出表語句
Greenplum、LibrA數據庫寫入方式與PostgreSQL類似
CREATE TABLE MyResult(
channel VARCHAR,
info VARCHAR
)WITH(
type ='postgresql',
url ='jdbc:postgresql://localhost:9001/test?sslmode=disable',
userName ='dtstack',
password ='abc123',
tableName ='pv2',
parallelism ='1'
);
--基於FlinkSQL進行Kafka流數據讀入關聯Oracle維表,並寫入PostgreSQL的實現
insert
into
MyResult
select
a.channel,
b.info
from
MyTable a
join
sideTable b
on a.channel=b.channel