自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Rec:一個項目的誕生

開發(fā) 開發(fā)工具
Rec是一個用來驗證和轉(zhuǎn)換數(shù)據(jù)文件的Java應(yīng)用。從第一行代碼到v1版本成形,僅僅經(jīng)歷了一個半月的時間,作為一個開源項目,在很多方面都有著各種各樣的糾結(jié)。

Rec是一個用來驗證和轉(zhuǎn)換數(shù)據(jù)文件的Java應(yīng)用。從第一行代碼到v1版本成形,僅僅經(jīng)歷了一個半月的時間,作為一個開源項目,在很多方面都有著各種各樣的糾結(jié)。

[[194531]]

需求

Rec的需求源自于我們團隊所做項目的特殊性:遺留系統(tǒng)遷移。在工作中,我們需要跟各種團隊打交道,每天處理各種來自ETL(Extract、Transform、Load)過程中的數(shù)據(jù)和程序問題,而整個ETL程序運行起來過于笨重,并且還要考慮準備后端數(shù)據(jù)和各種驗證問題,非常不方便。

其實在此之前,只要有一些簡單的程序跑起來、能夠進行一些簡單的檢查,比如唯一性(uniqueness)、關(guān)聯(lián)關(guān)系等等,就可以在很大程度上減少我們在ETL過程中花費的時間。并且,這半年多來的實踐也證實了這一點。

最初,同事的建議是寫一個腳本文件來解決這個問題,這對于程序員來說當然不是什么大問題。但隨著使用次數(shù)的增加,我漸漸發(fā)現(xiàn)一套Python腳本并不能勝任:一方面,面對復(fù)雜的業(yè)務(wù)場景,很難有一套靈活的模式去匹配所有的數(shù)據(jù)格式;另一方面,隨著數(shù)據(jù)量的增長,性能也成了一個大問題。

于是我開始著手設(shè)計和實現(xiàn)Rec。

[[194532]]

設(shè)計

Rec第一個可用版本的設(shè)計共花了七天的時間,基本上具備了我期望的各種能力:

  • 可以自定義數(shù)據(jù)格式
  • 能夠進行簡單的唯一性和關(guān)聯(lián)關(guān)系驗證
  • 支持一些擴展的查詢語法:比如,可以驗證多字段組合的唯一性
  • 性能上基本能夠勝任

Rec面向的數(shù)據(jù)文件格式是類CSV的文件,包括其他的一些使用分號(;)或者豎線(|)來做分隔符的文件。出于習慣,文件的Parser并沒有選取現(xiàn)成的庫,而是我自己按照Wikipedia和RFC4180的規(guī)范寫出來的,基本上能夠解析所有類似的文件。而且還有一個意外的發(fā)現(xiàn):用空格做分隔符的文件(比如,某些日志)也是可以支持的。

對于每一條數(shù)據(jù),Rec提供了兩部分組件,一部分是數(shù)據(jù)本身,另一部分是該數(shù)據(jù)的訪問器(accessor)。訪問器提供把字段名轉(zhuǎn)換成對應(yīng)數(shù)據(jù)項下標的功能:跟Spring Batch中的FieldSetMapper很像,當然在其之上還多了一層語法糖。

一個典型的accessor format如下:

  1. first name, last name, {5}, phone, …, job title,{3} 

其中,“…”表示中間的字段全部可以忽略,{3}和{5}是占位符,表示在這些字段之間有如此多個字段也是可以忽略的。而由“…”分割成的兩部分也是有差異的:在其后的字段使用的是類似Python的負數(shù)下標;換句話說,我并不需要知道本來的數(shù)據(jù)有多少個字段,只需要知道我要獲取的倒數(shù)第幾個是什么就可以了。

[[194533]]

Rec的驗證規(guī)則也是從簡設(shè)計。由于最初的需求只有唯一性檢查和關(guān)聯(lián)關(guān)系檢查,所以第一個版本里面就只加入了這兩個功能,語法如下:

  1. unique: Customer[id] 
  2. unique: Order[cust_id, prod_id] 
  3. exist: Order.prod_id, Product.id 

每一行表示一個規(guī)則,冒號前面是規(guī)則的名字,后面是規(guī)則所需要驗證的數(shù)據(jù)查詢表達式。對于查詢表達式,這里需要提一點,本來是設(shè)計了更多的功能,比如過濾和組合等等,在后面擴展的時候發(fā)現(xiàn)在語法上很難實現(xiàn)得更直觀而且方便使用,于是就決定改用嵌入腳本引擎的方式來解決。

另外Rec第一個版本發(fā)布只有Kotlin運行時的依賴,所以完整的Jar文件只有2MB。同時,只要給對應(yīng)的數(shù)據(jù)文件提供.rec格式的描述文件,再在同一目錄創(chuàng)建一個default.rule來加入各種檢驗規(guī)則,就可以運行、然后得到你想要的結(jié)果了。

擴展

Rec的第一個版本在某些方面是達到預(yù)期結(jié)果了的。但在那之后就發(fā)現(xiàn)了一些很重要的問題:首先,我們另一層的需求并沒有得到滿足:Rec能夠幫我們驗證并且找到有問題的數(shù)據(jù),但是不能夠按需來選擇我們想要的內(nèi)容;其次,在檢查數(shù)據(jù)的同時,我們也隱含地有集成和轉(zhuǎn)換數(shù)據(jù)的需求,Rec也不能夠滿足。

于是第一個星期以后我開始考慮對Rec進行擴展。首先是在同事的建議下把亂成一坨的代碼分成多個module;其次考慮加入前面提到的過濾和格式轉(zhuǎn)換的功能。

[[194534]]

第一個步驟勉強算是完成了,但是卡在了第二步上:對于轉(zhuǎn)換的規(guī)則,要不要和驗證的規(guī)則放在一起?如何對這兩種規(guī)則做區(qū)分?如何在過濾器中設(shè)計變量引用等細節(jié)?每一個問題都讓我糾結(jié)了很多,直到最后決定放棄這一步,直接通過引入腳本引擎來實現(xiàn):從最初hack Kotlin編譯器的嵌入版,到?jīng)Q定用JavaScript,到放棄Nashorn轉(zhuǎn)而用Rhino,中間雖然輾轉(zhuǎn)幾次又遭遇了不少坑,但畢竟有成熟的社區(qū)經(jīng)驗輔以指導,還是順利地走了下來。

Test Driven Development vs Test Driven Design

其實直到現(xiàn)在Rec的測試也只有少量的一些。而且在拆分模塊的時候,因為測試代碼之間的依賴比較多,并沒有做拆分,所以基本上還是集中在一個模塊中。當然這也是很多時候我自己做項目時的一個習慣:并不會完全以TDD的方式來開發(fā),而是把單元測試作為一個驗證設(shè)計思路的手段。因為很多時候思路轉(zhuǎn)變的太突然,不實現(xiàn)的話估計下一秒鐘就完全變了。而且,作為一個簡單的工具類程序,并不需要重度面向?qū)ο蟮脑O(shè)計,如何規(guī)劃和設(shè)計流暢易用的接口就成了必須考慮的一個問題。這個時候測試的設(shè)計性變得更明顯。

另外,對于Parser這種東西,測試是必不可少的,但是要TDD一個Parser出來,基本上就是在給自己找活干了。所以這種時候,我會先加一些基本的case,來確保能夠正常的實現(xiàn)功能,然后再引入一些比較corner的case來確保實際的可用性。對我來說,這是完全沒有問題的:當然后面的實踐驗證了這一點,Rec沒在解析文件方面出現(xiàn)過任何問題。

[[194535]]

Kotlin vs Java(Script)

最初采用Kotlin就是因為它有很多優(yōu)點,而且這些優(yōu)點也確實影響了Rec的設(shè)計,但是因為各種原因,還是被替換了兩次。首先遲遲不發(fā)布的1.1版本和編碼兼容性的諸多問題,導致我決定用原生Java換掉Kotlin。當然,這也導致了不得不強行舍棄很多好用的編譯期檢查和語法糖,以及一個用來做bean mapping的組件。

至于采用JavaScript,則是另外一個問題。

眾所周知,JSR223定義了一套JVM平臺的腳本引擎規(guī)范,但是作為一個強靜態(tài)類型的編譯型語言,Kotlin想要契合這套規(guī)范還是很困難的,于是無論是官方的實現(xiàn)還是Rec的解決方法,都不是很好:

首先你要啟動一個JVM來執(zhí)行這個腳本的動作;在這個動作里面,啟動第二個JVM要調(diào)用Kotlin的編譯器來將該腳本編譯成class;然后這個編譯器會再去利用自定義的classloader來加載和執(zhí)行這個class文件。當所有的功能都集中在一個Jar文件里面的時候,每次都要選擇指定classpath等選項,實現(xiàn)非常復(fù)雜。而且,由于第二次執(zhí)行的Kotlin編譯器是識別不到你已引入的kotlin-reflect類庫的(因為已經(jīng)統(tǒng)一包裝到rec的jar包里面去了),就會導致腳本中bean mapper的一些功能根本不能使用。萬般無奈,選擇采用更成熟的JS引擎。

當然選擇JS帶來的一個好處就是,有更多人可以拿來就用了,而且,最新的Rhino提供了CommonJS擴展,能夠順手require所需的JS文件,在復(fù)用和模塊化方面也能夠有不少提升。

技術(shù)抉擇

除了部分Parser相關(guān)的代碼外,Rec采用的基本都是不可變的數(shù)據(jù)結(jié)構(gòu):一方面是因為使用Kotlin;另一方面,在整個模型里面并沒有特別的需求會涉及更改數(shù)據(jù)。

唯一的擔心是內(nèi)存占用,但是后來發(fā)現(xiàn)這部分擔心也是不必要的,因為所有內(nèi)存的瓶頸只在數(shù)據(jù)文件的Parser上,項目中的數(shù)據(jù)條目動輒數(shù)十個數(shù)據(jù)項,幾十萬條數(shù)據(jù),再加上每次parse都會把一個字符串分割成多個,最后再合并到一個大的集合里面,在最開始設(shè)計的時候沒有考慮這一點,輕輕松松就爆了JVM堆。這也是后期需要著重優(yōu)化的一個方面。

[[194536]]

另外一個點是關(guān)于異常處理。對于Java應(yīng)用來說這是個巨坑:異常本身并沒有問題,但是由于checked和unchecked的區(qū)分以及眾多設(shè)計哲學的不同,所以就成了爭議點所在。在這里我參考了Joe Duffy的做法。對于嚴重的不可重試的錯誤,比如文件找不到,空指針異常,下標錯誤等,直接讓程序die(沒錯,就是PHP中的那個die),至于數(shù)據(jù)格式錯誤等問題,更多的做法是做一條記錄然后選擇繼續(xù)。當然這一套東西并不依賴Java的異常系統(tǒng),只是作為一個設(shè)計原則來應(yīng)用,畢竟這不是一個App server,并不需要高可用的保障,相反這種fail fast的直接反饋更有助于發(fā)現(xiàn)和解決問題。

在類型系統(tǒng)上,最初實現(xiàn)Rec的語言是Kotlin,它提供了一套比Java略微高級一些的類型系統(tǒng)。當然主要的點還是在于nullable:在功能上,nullable與Java 8的Optional類似,用來容納可以為空的值,同時能夠有效避免空指針異常;在實現(xiàn)上,比Java略微高出了一點的是,非nullable的對象必須被初始化并且不容許為null。這直接解決了Optional對象為空的尷尬問題。

當然,由于運行時的依賴還是無法避免地使用JVM,而且沒有自定義值類型的支持,在使用Kotlin,特別是跟Java標準庫和其他框架結(jié)合使用的時候,還是會遇到空指針的坑。但是在這一點上,Kotlin給我們開了個好頭,比如在后面convert到Java的過程中,我也盡量保證各種對象都是final并且被非空初始化了的。

結(jié)語

當然也許很多人會說,Unix那套工具用的很順手的話,上面說的這些都不是問題,其實Rec本來的思路也是來自于它們:accessor來自于awk的列操作模式,scripting中的過濾器來自于sed和grep,鏈式調(diào)用源于管道。Rec也只是在這些思路之上加了一些方便的操作而已。但是對于我個人來說,這種折騰其實是在檢驗我自己的理論和思考,更別說還提升了項目的生產(chǎn)力。也許哪一天實在受不了了,還可以拿C++和Lua重寫了呢。畢竟,生命不息,折騰不止。

【本文是51CTO專欄作者“ThoughtWorks”的原創(chuàng)稿件,微信公眾號:思特沃克,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2013-07-24 15:26:57

MOCO

2012-11-29 09:49:17

軟件項目項目

2011-08-25 09:03:40

2014-08-11 16:32:04

架構(gòu)項目

2014-08-27 10:20:10

項目項目分析

2017-11-07 11:36:57

開源項目代碼

2018-09-13 14:18:20

C語言Java程序員

2019-08-06 13:37:55

微服務(wù)架構(gòu)數(shù)據(jù)

2020-08-13 17:59:20

區(qū)塊鏈區(qū)塊鏈項目數(shù)字貨幣

2021-02-24 13:58:07

區(qū)塊鏈比特幣安全

2022-02-28 08:23:02

開源項目重構(gòu)

2020-11-15 23:23:21

JavaScriptAPI開發(fā)

2019-01-15 10:02:06

Kubernetes開源工具微服務(wù)

2012-06-27 10:16:12

開源項目CodePlex

2009-04-20 23:29:12

Oracle收購Sun甲骨文

2013-07-30 14:06:30

Google中國殺手

2023-01-26 00:54:57

2019-11-26 16:06:59

區(qū)塊鏈去中心化

2019-05-27 08:29:32

啟動項目PMP
點贊
收藏

51CTO技術(shù)棧公眾號