天天看點

Spark最佳實踐-項目規範

前言

大資料開發的日常工作中,開發人員經常需要使用 Spark、Flink 等計算引擎作為工具來實作一些 業務邏輯 的計算。

以 Spark 為例,開發人員會使用 SparkSQL、DataFrame、RDD 等不同形式的API來實作業務需求。

通常情況下,簡單的需求都可以通過 SparkSQL、DataFrame 很友善的實作,其簡潔的API也是其深受資料分析師青睐的原因之一。

但是正是因為 SparkSQL、DataFrame 的高層次封裝,在 複雜度較高的計算需求 實作中,可能會出現 實作複雜或者API的功能性無法滿足,或者千方百計實作需求之後 性能表現低下,代碼段複雜而龐大 的情況。

盡管Spark允許開發人員通過UDF、UDAF等形式提供自定義的函數功能,但是此時很多人會選擇使用較為底層的RDD接口進行開發:可控性好、開發與調試友善、性能強勁。

但是使用RDD接口來開發業務需求時,很多小的項目團隊并沒有一個統一的項目規範,需求開發完全由開發人員個人自己發揮。

各個業務項目的大緻流程基本是相同的:

  1. 建立SparkSession
  2. 用 spark.table or spark.textFile 等API讀取資料源
  3. 進行RDD的各種 Transformation 和 Action 操作
  4. 得到資料結果之後再調用 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 提供的資料操作接口 讀出資料源 後 調用業務計算邏輯 處理完畢後 寫入資料源 中。

通過對項目子產品的标準化規範,我們可以以一個 比較統一和簡單易懂的開發方式 來進行需求落地。

雖然剛開始使用規範的時候會有人覺得繁瑣與不耐煩,如果是手動開發的話誰都會煩,都是一些重複性的苦力活兒,這就是架構規範的缺點:特别繁瑣。

但是配套做一些自動化工具來使用的話,相信大部分開發人員都會覺得很酸爽,某種程度上标準化項目就是這麼來提升開發效率的。