前言
大數據開發的日常工作中,開發人員經常需要使用 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 提供的數據操作接口 讀出數據源 後 調用業務計算邏輯 處理完畢後 寫入數據源 中。
通過對項目模塊的標准化規範,我們可以以一個 比較統一和簡單易懂的開發方式 來進行需求落地。
雖然剛開始使用規範的時候會有人覺得繁瑣與不耐煩,如果是手動開發的話誰都會煩,都是一些重複性的苦力活兒,這就是框架規範的缺點:特別繁瑣。
但是配套做一些自動化工具來使用的話,相信大部分開發人員都會覺得很酸爽,某種程度上標准化項目就是這麽來提升開發效率的。