Apache RocketMQ + Hudi 快速構建 Lakehouse

簡介:基於RocketMQ和Hudi零代碼構建Lakehouse架構,以及RocketMQ Connector & RocketMQ Stream助力ETL數據分析,爲大家提供快速構建Lakehouse的技術方案和低運維成本實現實時計算的解決方案。

本文目錄

  • 背景知識
  • 大數據時代的構架演進
  • RocketMQ Connector&Stream
  • Apache Hudi
  • 構建Lakehouse實操

本文標題包含三個關鍵詞:Lakehouse、RocketMQ、Hudi。我們先從整體Lakehouse架構入手,隨後逐步分析架構產生的原因、架構組件特點以及構建Lakehouse架構的實操部分。

背景知識

1、Lakehouse架構

Lakehouse最初由Databrick提出,並對Lakehouse架構特徵有如下要求:

(1)事務支持

企業內部許多數據管道通常會併發讀寫數據。對ACID事務的支持確保了多方併發讀寫數據時的一致性問題;

(2)Schema enforcement and governance

Lakehouse應該有一種方式可以支持模式執行和演進、支持DW schema的範式(如星星或雪花模型),能夠對數據完整性進行推理,並且具有健壯的治理和審計機制;

(3)開放性

使用的存儲格式是開放式和標準化的(如parquet),並且爲各類工具和引擎,包括機器學習和Python/R庫,提供API,以便它們可以直接有效地訪問數據;

(4)BI支持

Lakehouse可以直接在源數據上使用BI工具。這樣可以提高數據新鮮度、減少延遲,並且降低了在數據池和數據倉庫中操作兩個數據副本的成本;

(5)存儲與計算分離

在實踐中,這意味着存儲和計算使用單獨的集羣,因此這些系統能夠擴展到支持更大的用戶併發和數據量。一些現代數倉也具有此屬性;

(6)支持從非結構化數據到結構化數據的多種數據類型

Lakehouse可用於存儲、優化、分析和訪問許多數據應用所需的包括image、video、audio、text以及半結構化數據;

(7)支持各種工作負載

包括數據科學、機器學習以及SQL和分析。可能需要多種工具來支持這些工作負載,但它們底層都依賴同一數據存儲庫;

(8)端到端流

實時報表是許多企業中的標準應用。對流的支持消除了需要構建單獨系統來專門用於服務實時數據應用的需求。

從上述對Lakehouse架構的特點描述我們可以看出,針對單一功能,我們可以利用某些開源產品組合構建出一套解決方案。但對於全部功能的支持,目前好像沒有一個通用的解決方案。接下來,我們先了解大數據時代主流的數據處理架構是怎樣的。

大數據時代的架構演進

1、大數據時代的開源產品

大數據時代的開源產品種類繁多,消息領域的RocketMQ、Kafka;計算領域的flink、spark、storm;存儲領域的HDFS、Hbase、Redis、ElasticSearch、Hudi、DeltaLake等等。

爲什麼會產生這麼多開源產品呢?首先在大數據時代數據量越來越大,而且每個業務的需求也各不相同,因此就產生出各種類型的產品供架構師選擇,用於支持各類場景。然而衆多的品類產品也給架構師們帶來一些困擾,比如選型困難、試錯成本高、學習成本高、架構複雜等等。

2、當前主流的多層架構

大數據領域的處理處理場景包含數據分析、BI、科學計算、機器學習、指標監控等場景,針對不同場景,業務方會根據業務特點選擇不同的計算引擎和存儲引擎;例如交易指標可以採用binlog + CDC+ RocketMQ + Flink + Hbase + ELK組合,用於BI和Metric可視化。

(1)多層架構的優點:支持廣泛的業務場景;

(2)多層架構的缺點:

  • 處理鏈路長,延遲高;
  • 數據副本多,成本翻倍;
  • 學習成本高;

造成多層架構缺點主要原因是存儲鏈路和計算鏈路太長。

  • 我們真的需要如此多的解決方案來支持廣泛的業務場景嗎?Lakehouse架構是否可以統一解決方案?
  • 多層架構的存儲層是否可以合併?Hudi產品是否能夠支持多種存儲需求?
  • 多層架構的計算層是否可以合併?RocketMQ stream是否能夠融合消息層和計算層?

當前主流的多層架構

3、Lakehouse架構產生

Lakehouse架構是多層架構的升級版本,將存儲層複雜度繼續降低到一層。再進一步壓縮計算層,將消息層和計算層融合,RocketMQ stream充當計算的角色。我們得到如下圖所示的新架構。新架構中,消息出入口通過RocketMQ connector實現,消息計算層由RocketMQ stream實現,在RocketMQ內部完成消息計算中間態的流轉;計算結果通過RocketMQ-Hudi-connector收口落庫Hudi,Hudi支持多種索引,並提供統一的API輸出給不同產品。

Lakehouse架構

下面我們分析下該架構的特點。

(1)Lakehouse架構的優點:

  • 鏈路更短,更適合實時場景,數據新鮮感高;
  • 成本可控,降低了存儲成本;
  • 學習成本低,對程序員友好;
  • 運維複雜度大幅降低;

(2)Lakehouse架構的缺點

對消息產品和數據湖產品的穩定性、易用性等要求高,同時消息產品需要支持計算場景,數據湖產品需要提供強大的索引功能。

(3)選擇

在Lakehouse架構中我們選擇消息產品RocketMQ和數據湖產品Hudi。

同時,可以利用RocketMQ stream在RocketMQ集羣上將計算層放在其中集成,這樣就將計算層降低到一層,能夠滿足絕大部分中小型大數據處理場景。

接下來我們逐步分析RocketMQ和Hudi兩款產品的特點。

RocketMQ Connector & Stream

RocketMQ 發展歷程圖

RocketMQ從2017年開始進入Apache孵化,2018年RocketMQ 4.0發佈完成雲原生化,2021年RocketMQ 5.0發佈全面融合消息、事件、流。

1、業務消息領域首選

RocketMQ作爲一款“讓人睡得着覺的消息產品”成爲業務消息領域的首選,這主要源於產品的以下特點:

(1)金融級高可靠

經歷了阿里巴巴雙十一的洪峯檢驗;

(2)極簡架構

如下圖所示, RocketMQ的架構主要包含兩部分包括:源數據集羣NameServer Cluster和計算存儲集羣Broker Cluster。

RocketMQ 構架圖

NameServer節點無狀態,可以非常簡單的進行橫向擴容。Broker節點採用主備方式保證數據高可靠性,支持一主多備的場景,配置靈活。

搭建方式:只需要簡單的代碼就可以搭建RocketMQ集羣:

Jar:

 nohup sh bin/mqnamesrv &
 nohup sh bin/mqbroker -n localhost:9876 &

On K8S:

kubectl apply -f example/rocketmq_cluster.yaml

(3)極低運維成本

RocketMQ的運維成本很低,提供了很好的CLI工具MQAdmin,MQAdmin提供了豐富的命令支持,覆蓋集羣健康狀態檢查、集羣進出流量管控等多個方面。例如,mqadmin clusterList一條命令可以獲取到當前集羣全部節點狀態(生產消費流量、延遲、排隊長度、磁盤水位等);mqadmin updateBrokerConfig命令可以實時設置broker節點或topic的可讀可寫狀態,從而可以動態摘除臨時不可用節點,達到生產消費的流量遷移效果。

(4)豐富的消息類型

RocketMQ支持的消息類型包括:普通消息、事務消息、延遲消息、定時消息、順序消息等。能夠輕鬆支持大數據場景和業務場景。

(5)高吞吐、低延遲

壓測場景主備同步複製模式,每臺Broker節點都可以將磁盤利用率打滿,同時可以將p99延遲控制在毫秒級別。

2、RocketMQ 5.0概況

RocketMQ 5.0是生於雲、長於雲的雲原生消息、事件、流超融合平臺,它具有以下特點:

(1)輕量級SDK

  • 全面支持雲原生通信標準 gRPC 協議;
  • 無狀態 Pop 消費模式,多語言友好,易集成;

(2)極簡架構

  • 無外部依賴,降低運維負擔;
  • 節點間鬆散耦合,任意服務節點可隨時遷移;

(3)可分可合的存儲計算分離

  • Broker 升級爲真正的無狀態服務節點,無 binding;
  • Broker 和 Store節點分離部署、獨立擴縮;
  • 多協議標準支持,無廠商鎖定;
  • 可分可合,適應多種業務場景,降低運維負擔;

如下圖所示,計算集羣(Broker)主要包括抽象模型和相對應的協議適配,以及消費能力和治理能力。存儲集羣(Store)主要分爲消息存儲CommitLog(多類型消息存儲、多模態存儲)和索引存儲Index(多元索引)兩部分,如果可以充分發揮雲上存儲的能力,將CommitLog和Index配置在雲端的文件系統就可以天然的實現存儲和計算分離。

(4)多模存儲支持

  • 滿足不同基礎場景下的高可用訴求;
  • 充分利用雲上基礎設施,降低成本;

(5)雲原生基礎設施

  • 可觀測性能力雲原生化,OpenTelemetry 標準化;
  • Kubernetes 一鍵式部署擴容交付。

RocketMQ 5.02021年度大事件及未來規劃

3RocketMQConnector

a、傳統數據流

(1)傳統數據流的弊端
  • 生產者消費者代碼需要自己實現,成本高;
  • 數據同步的任務沒有統一管理;
  • 重複開發,代碼質量參差不齊;

(2)解決方案:RocketMQ Connector

  • 合作共建,複用數據同步任務代碼;
  • 統一的管理調度,提高資源利用率;

b、RocketMQ Connector數據同步流程

相比傳統數據流,RocketMQ connector數據流的不同在於將 source 和 sink 進行統一管理,同時它開放源碼,社區也很活躍。

4、RocketMQ Connector架構

如上圖所示,RocketMQ Connector架構主要包含Runtime和Worker兩部分,另外還有生態Source&Sink。

(1)標準:OpenMessaging

(2)生態:支持ActiveMQ、Cassandra、ES、JDBC、JMS、MongoDB、Kafka、RabbitMQ、Mysql、Flume、Hbase、Redis等大數據領域的大部分產品;

(3)組件:Manager統一管理調度,如果有多個任務可以將所有任務統一進行負載均衡,均勻的分配到不同Worker上,同時Worker可以進行橫向擴容。

5、RocketMQ Stream

RocketMQ Stream是一款將計算層壓縮到一層的產品。它支持一些常見的算子如window、join、維表,兼容Flink SQL、UDF/UDAF/UDTF。

Apache Hudi

Hudi 是一個流式數據湖平臺,支持對海量數據快速更新。內置表格式,支持事務的存儲層、一系列表服務、數據服務(開箱即用的攝取工具)以及完善的運維監控工具。Hudi 可以將存儲卸載到阿里雲上的 OSS、AWS 的S3這些存儲上。

Hudi的特性包括:

  • 事務性寫入,MVCC/OCC併發控制;
  • 對記錄級別的更新、刪除的原生支持;
  • 面向查詢優化:小文件自動管理,針對增量拉取優化的設計,自動壓縮、聚類以優化文件佈局;

Apache Hudi是一套完整的數據湖平臺。它的特點有:
  • 各模塊緊密集成,自我管理;
  • 使用 Spark、Flink、Java 寫入;
  • 使用 Spark、Flink、Hive、Presto、Trino、Impala、
    AWS Athena/Redshift等進行查詢;
  • 進行數據操作的開箱即用工具/服務。

Apache Hudi主要針對以下三類場景進行優化:

1、流式處理棧

(1) 增量處理;

(2) 快速、高效;

(3) 面向行;

(4) 未優化掃描;

2、批處理棧

(1) 批量處理;

(2) 低效;

(3) 掃描、列存格式;

3、增量處理棧

(1) 增量處理;

(2) 快速、高效;

(3) 掃描、列存格式。

構建 Lakehouse 實操

該部分只介紹主流程和實操配置項,本機搭建的實操細節可以參考附錄部分。

1、準備工作

RocketMQ version:4.9.0

rocketmq-connect-hudi version:0.0.1-SNAPSHOT

Hudi version:0.8.0

2、構建RocketMQ-Hudi-connector

(1) 下載:

 git clone GitHub - apache/rocketmq-externals: Mirror of Apache RocketMQ (Incubating)

(2) 配置:

/data/lakehouse/rocketmq-externals/rocketmq-connect/rocketmq-connect-runtime/target/distribution/conf/connect.conf 中connector-plugin 路徑

(3) 編譯:

cd rocketmq-externals/rocketmq-connect-hudi
mvn clean install -DskipTest -U

rocketmq-connect-hudi-0.0.1-SNAPSHOT-jar-with-dependencies.jar就是我們需要使用的rocketmq-hudi-connector

3、運行

(1) 啓動或使用現有的RocketMQ集羣,並初始化元數據Topic:

connector-cluster-topic (集羣信息) connector-config-topic (配置信息)

connector-offset-topic (sink消費進度) connector-position-topic (source數據處理進度 並且爲了保證消息有序,每個topic可以只建一個queue)

(2) 啓動RocketMQ connector運行時

cd /data/lakehouse/rocketmq-externals/rocketmq-connect/rocketmq-connect-runtime
  sh ./run_worker.sh    ##  Worker可以啓動多個

(3) 配置並啓動RocketMQ-hudi-connector任務

請求RocketMQ connector runtime創建任務

curl http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-hudi-sink-connector-name} ?config='{"connector-class":"org.apache.rocketmq.connect.hudi.connector.HudiSinkConnector","topicNames":"topicc","tablePath":"file:///tmp/hudi_connector_test","tableName":"hudi_connector_test_table","insertShuffleParallelism":"2","upsertShuffleParallelism":"2","deleteParallelism":"2","source-record-converter":"org.apache.rocketmq.connect.runtime.converter.RocketMQConverter","source-rocketmq":"127.0.0.1:9876","src-cluster":"DefaultCluster","refresh-interval":"10000","schemaPath":"/data/lakehouse/config/user.avsc"\}’
  啓動成功會打印如下日誌:
2021-09-06 16:23:14 INFO pool-2-thread-1 - Open HoodieJavaWriteClient successfully

(4) 此時向source topic生產的數據會自動寫入到1Hudi對應的table中,可以通過Hudi的api進行查詢。

4、配置解析

(1) RocketMQ connector需要配置RocketMQ集羣信息和connector插件位置,包含:connect工作節點id標識workerid、connect服務命令接收端口httpPort、rocketmq集羣namesrvAddr、connect本地配置儲存目錄storePathRootDir、connector插件目錄pluginPaths 。

RocketMQ connector配置表

(2) Hudi任務需要配置Hudi表路徑tablePath和表名稱tableName,以及Hudi使用的Schema文件。

Hudi任務配置表

點擊此處即可查看Lakehouse構建實操視頻

附錄:在本地Mac系統構建Lakehouse demo

涉及到的組件:rocketmq、rocketmq-connector-runtime、rocketmq-connect-hudi、hudi、hdfs、avro、spark-shell0、啓動hdfs

下載hadoop包

Apache Downloads

cd /Users/osgoo/Documents/hadoop-2.10.1
vi core-site.xml
<configuration>
<property>
 <name>fs.defaultFS</name>
 <!-- 可以通過命令hostname 查看主機名字  這裏的主機名字是hadoop1-->
 <value>hdfs://localhost:9000</value>
</property>
<!--覆蓋掉core-default.xml中的默認配置-->
</configuration>

vi hdfs-site.xml
<configuration>
<property>
        <name>dfs.replication</name>
        <value>1</value>
  </property>
</configuration>

./bin/hdfs namenode -format
./sbin/start-dfs.sh 
jps 看下namenode,datanode
lsof -i:9000
./bin/hdfs dfs -mkdir -p /Users/osgoo/Downloads

1、啓動rocketmq集羣,創建rocketmq-connector內置topic
QickStart:https://rocketmq.apache.org/docs/quick-start/
sh mqadmin updatetopic -t connector-cluster-topic -n localhost:9876 -c DefaultCluster
sh mqadmin updatetopic -t connector-config-topic -n localhost:9876 -c DefaultCluster
sh mqadmin updatetopic -t connector-offset-topic -n localhost:9876 -c DefaultCluster
sh mqadmin updatetopic -t connector-position-topic -n localhost:9876 -c DefaultCluster

2、創建數據入湖的源端topic,testhudi1
sh mqadmin updatetopic -t testhudi1 -n localhost:9876 -c DefaultCluster

3、編譯rocketmq-connect-hudi-0.0.1-SNAPSHOT-jar-with-dependencies.jar
cd rocketmq-connect-hudi
mvn clean install -DskipTest -U

4、啓動rocketmq-connector runtime
配置connect.conf
--------------
workerId=DEFAULT_WORKER_1
storePathRootDir=/Users/osgoo/Downloads/storeRoot

## Http port for user to access REST API
httpPort=8082

# Rocketmq namesrvAddr
namesrvAddr=localhost:9876

# Source or sink connector jar file dir,The default value is rocketmq-connect-sample
pluginPaths=/Users/osgoo/Downloads/connector-plugins
---------------
拷貝 rocketmq-hudi-connector.jar 到 pluginPaths=/Users/osgoo/Downloads/connector-plugins

sh run_worker.sh

5、配置入湖config
curl http://localhost:8082/connectors/rocketmq-connect-hudi?config='\{"connector-class":"org.apache.rocketmq.connect.hudi.connector.HudiSinkConnector","topicNames":"testhudi1","tablePath":"hdfs://localhost:9000/Users/osgoo/Documents/base-path7","tableName":"t7","insertShuffleParallelism":"2","upsertShuffleParallelism":"2","deleteParallelism":"2","source-record-converter":"org.apache.rocketmq.connect.runtime.converter.RocketMQConverter","source-rocketmq":"127.0.0.1:9876","source-cluster":"DefaultCluster","refresh-interval":"10000","schemaPath":"/Users/osgoo/Downloads/user.avsc"\}'

6、發送消息到testhudi1


7、## 利用spark讀取

cd /Users/osgoo/Downloads/spark-3.1.2-bin-hadoop3.2/bin

./spark-shell \
  --packages org.apache.hudi:hudi-spark3-bundle_2.12:0.9.0,org.apache.spark:spark-avro_2.12:3.0.1 \
  --conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer'

import org.apache.hudi.QuickstartUtils._
import scala.collection.JavaConversions._
import org.apache.spark.sql.SaveMode._
import org.apache.hudi.DataSourceReadOptions._
import org.apache.hudi.DataSourceWriteOptions._
import org.apache.hudi.config.HoodieWriteConfig._

val tableName = "t7"
val basePath = "hdfs://localhost:9000/Users/osgoo/Documents/base-path7"

val tripsSnapshotDF = spark.
  read.
  format("hudi").
  load(basePath + "/*")
tripsSnapshotDF.createOrReplaceTempView("hudi_trips_snapshot")

spark.sql("select * from hudi_trips_snapshot").show()

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章