前言
大資料開發的日常工作中,開發人員經常需要使用 Spark、Flink 等計算引擎作為工具來實作一些 業務邏輯 的計算。
以 Spark 為例,開發人員會使用 SparkSQL、DataFrame、RDD 等不同形式的API來實作業務需求。
通常情況下,簡單的需求都可以通過 SparkSQL、DataFrame 很友善的實作,其簡潔的API也是其深受資料分析師青睐的原因之一。
但是正是因為 SparkSQL、DataFrame 的高層次封裝,在 複雜度較高的計算需求 實作中,可能會出現 實作複雜或者API的功能性無法滿足,或者千方百計實作需求之後 性能表現低下,代碼段複雜而龐大 的情況。
盡管Spark允許開發人員通過UDF、UDAF等形式提供自定義的函數功能,但是此時很多人會選擇使用較為底層的RDD接口進行開發:可控性好、開發與調試友善、性能強勁。
但是使用RDD接口來開發業務需求時,很多小的項目團隊并沒有一個統一的項目規範,需求開發完全由開發人員個人自己發揮。
各個業務項目的大緻流程基本是相同的:
- 建立SparkSession
- 用 spark.table or spark.textFile 等API讀取資料源
- 進行RDD的各種 Transformation 和 Action 操作
- 得到資料結果之後再調用 saveAsTable or saveAsTextFile 寫入外部資料源中
雖然看起來流程挺一緻的,但是其中仍然存在以下問題:
- 業務代碼混亂
- 團隊成員代碼風格不一,有的喜歡一長串一長串的寫,有的喜歡将過程封裝
- 即使将過程封裝了,但是封裝的邊界沒有明确定義,仍然會有人不小心“越界”
- 讀寫資料源的API使用不統一
- 各個計算引擎對各個資料源都有不同的讀寫API接口提供使用,一些比較繁雜的API可能會被人“錯誤”使用
- 同時也會有人時常忘記對應接口如何使用,反複查閱資料
- 重複的編碼工作
- 理論上所有業務項目,除了業務邏輯是變化的之外,其餘應該都是一個不變的模闆
- 開發人員應該專注于變化的業務邏輯,而不是每次都要分一些精力出來處理其他“邊邊角角”的事情
沒有規範任由團隊成員發揮的話,盡管有些成員能寫一手漂亮的代碼,但是你并不能保證所有人都這麼優秀。
時間一久項目中代碼的 壞味道 會越來越多,最後混亂的程度可能會超出你的想象。
為了解決以上問題,我們建議:定義一個項目規範,所有業務項目都需要遵守這個規範。
俗話說,有規矩成方圓。
有了項目規範,所有人都遵守這個标準來開發。
有了這個标準,我們就可以在标準化的基礎上做很多事情,比如 定義自動化工具來幫助開發人員解放雙手。
本文讨論的項目規範可以作為一種參考,以供讀者與相關開發人員翻閱。
一、項目規範
和Java項目規範類似,以 子產品化項目 的結構來定義項目規範可以為業務項目提供 結構化标準,其可以規整所有 混亂的業務項目結構。
項目結構标準化的重要性:
- 項目統一管理與生成
- 友善快速搭架構
- 所有開發人員遵守相同的編碼規範
- 易于交接與維護
以下子產品劃分和Java項目類似,略微有些細節差異。
1.1 api子產品
業務計算邏輯子產品,不應該出現任何 Spark等執行架構的API 以 保持子產品獨立性與可移植。
理論上該子產品可以獨立構成一個單機程式執行,這樣可以将最重要的業務邏輯根據需要遷移到任意計算引擎中,如 Spark 到 Spark Streaming、Flink 甚至 Hive UDF 等。
- 對外隻提供接口調用,不可直接在外部執行個體化具體類(工廠模式)
- 所有service業務邏輯需要有對應的測試用例
- 事務控制、所有異常捕獲和處理
- 依賴common
1.2 common子產品
項目内通用的常量、枚舉、POJO實體類、工具函數等,視情況分離,可內建到 context 中
- 不包含任何業務邏輯
- 不依賴其他子產品
- 相關工具保持單例
1.3 context子產品
Spark或者其他程式 執行入口,負責初始化各種計算引擎的環境變量。
- 系統 全局配置(conf)與腳本(bin) 集中管理
- 依賴server、api、common
- 程式關鍵點需要列印日志以便後續debug使用
1.4 server子產品
整個項目中整合了業務邏輯調用、資料源讀寫等操作的子產品,需求簡單的情況下可以直接內建到 context 中。
該子產品中根據不同的接口操作類,還劃分了 dal、service與manager三個包。
1.4.1 dal包
主要是對資料進行操作,如讀寫常用的庫:Hive、MySQL、HBase;以及讀寫檔案系統:HDFS。
dal中的所有使用都由接口來定義,不同的接口實作使用不同的應用架構API,如Spark、Flink應該為兩個獨立的dal實作,在後續service使用過程中可以自由切換。
需要遵循以下原則:
- 所有bean對象,定義在dal
- 不得在dal寫各種業務邏輯、資料清洗邏輯
- 一張表對應一個dal接口、一個bean,對應多個獨立的dao實作
- 不允許在1個dao中同時操作多個表
包結構如下:
- basic: basic包下主要放一些基礎對象,如BaseDao,所有dao都需要完善 TABLE_NAME
- bean: 定義資料源表結構,不同的資料源可以定義在不同的包中,如hive、hbase、mysql等
- dao: 接口具體實作,用來操作資料表。如:增删改查
1.4.2 service包
和dao對接,一個service對應一個dao,service的使用都由接口來定義。
一個service下有兩個實作包:
- 正常實作包:直接對接dao,簡單處理一些判斷:如參數不合法校驗等。
- 測試實作包:模拟資料,可以不通過dao擷取,從本地檔案生成或代碼中生成。
不同的計算架構有不同的service實作,如spark、flink等(需要傳入其環境變量)。
1.4.3 manager包
- 調用service包實作資料增删改查
- 調用api子產品進行業務邏輯組合
- 提供函數接口給context子產品調用執行
二、代碼架構
基于以上項目子產品的劃分,我們可以看到,api、common是 每次都會變化的業務邏輯和通用屬性的抽取,而 context 是根據業務需要的計算引擎和運作環境設定的 執行入口。
以上三個子產品都是 根據業務需求變化比較大的,而server子產品則是負責對 其他各個子產品的調用與整合,最後通過 manager 提供統一的函數接口給 context 入口調用執行。
是以 server 子產品是這個項目規範中可以 自動化 起來的重點目标。
基于這個目标,我們開發了一個 大資料業務開發 基準項目的雛形,開發人員能夠做到開箱即用,不必再花太多精力在研究計算引擎與各個資料源的接口和API如何調用,專注于業務邏輯的實作,提升開發效率。
項目位址:
https://github.com/chubbyjiang/aisql-bigdata-base使用介紹
org.aisql.bigdata.base.framework
包中提供了幾種常見大資料項目需要用到的資料源。
framework 以 子產品化項目 的結構提供了 各個資料源基礎的Dao、Service接口與預設實作。
配合自動化的代碼生成工具,可以一鍵生成 server 子產品的代碼檔案直接使用。
現在我們來看一下規範+自動化的威力,例如現有 default.t_users 表需要讀取。
開發人員僅需要生成代碼檔案并複制到項目中,寫代碼如下:
val service = new UsersService
//讀取整個表
val allRdd:RDD[Users] = service.selectAll()
//字段篩選
val allRdd:RDD[Users] = service.selectAllWithCols(Seq("name","age"))
//條件過濾
val allRdd:RDD[Users] = service.selectAllByWhere("age>10")
//讀取1000條資料
val demoRdd:RDD[Users] = service.selectDemo()
//寫入表
service.insertInto(demoRDD)
service.createTable(demoRDD)
是不是 so easy?
其實所做的内容也就是在 server 子產品中封裝了 常用的不同計算引擎對不同資料源的讀寫操作API,并 自動化了 bean、dal、service 三個部分的代碼生成。
使得開發人員可以直接使用 service 提供的資料操作接口 讀出資料源 後 調用業務計算邏輯 處理完畢後 寫入資料源 中。
通過對項目子產品的标準化規範,我們可以以一個 比較統一和簡單易懂的開發方式 來進行需求落地。
雖然剛開始使用規範的時候會有人覺得繁瑣與不耐煩,如果是手動開發的話誰都會煩,都是一些重複性的苦力活兒,這就是架構規範的缺點:特别繁瑣。
但是配套做一些自動化工具來使用的話,相信大部分開發人員都會覺得很酸爽,某種程度上标準化項目就是這麼來提升開發效率的。