本篇是企業服務架構演進系列的第六篇,本篇我打算從另外一個角度去說一下企業服務架構演進的過程中我個人的一些積累。我從正式工作的第二年開始有造輪子的想法,然后從最簡單最基礎的方式去做,慢慢的可以做一些復雜的工具去提高開發效率,提高程序性能等。到19年我幾乎每個季度都會尋找一個合適的場景用業余時間去做一些創新性的東西,這些創新雖然不是那么重要,但是對于企業發展來說,這樣的方式正是企業競爭力的一種體現。
- 企業服務架構演進-引言
- 企業服務架構演進-單體架構的變遷
- 企業服務架構演進-從jquery到vue的工程實踐
- 企業服務架構演進-單庫多服務的尷尬
- 企業服務架構演進-第三方系統與自研之道
6. 企業服務架構演進-走上造輪子之路
- 企業服務架構演進-重復開發之殤
工具類
- DAOUtils工具類
我從16年底開始做電子合同系統,有些列表頁面需要開發,比較特別的地方是這些列表頁的顯示查詢條件慢慢增加到20+個,而整張表的字段大概40多個。在當時這個系統還沒有集成spring,orm框架也是公司自研的DAO框架,支持直接寫sql執行,代碼里動態拼sql。于是整個daoimpl的查詢sql都是下面這樣的:
if(a!= null){sql.append(a)}
if(b!= null){sql.append(b)}
if(c!= 0){sql.append(c)}
這樣的if語句大概有10+條,如果每個查詢條件不加注釋則幾乎看不懂sql是怎么拼的,拼的什么字段。
另外一個問題就是我們的表里基本上都有create_time,create_user,update_time,update_user這樣的字段,每個表幾乎都有修改狀態等更新操作,要更新操作人等。
因此我琢磨了下,自己寫了一個DAOUtils工具類。上面第一個問題的解決方案如下:
在DAOUtils實現類中增加一個@ColName(name),這個注解用在dto上面,準確的說這個dto是querydto,根據業務列表的查詢模型來定的,查詢dto可以直接作為DAO接口中的參數傳進去,name屬性值就是查詢表的查詢字段。在執行sql之前先通過DAOUtils里面的方法動態生成where 后面的查詢條件子句,然后再拼接上select from tableName作為最終的sql。實現原理就是自定義注解+反射,后期基于這種場景我也做了一些升級。第一次升級是因為有同事反饋說這種動態拼的sql是存在sql注入的,不推薦使用。于是我提供了一個新的重載方法,返回一個Map,兩個k-v,一個是返回拼的sql,一個是返回對應的paramsList。第二次升級是因為在招聘系統中有些列表頁的查詢涉及到了多表查詢,因此在查詢dto上面基于單表的動態生成顯然不滿足,因此重構了一版,只要在注解上聲明表的別名.字段名就行,使用方式跟原來一樣。
對于上面提到的第二個問題就比較簡單了,抽象了一個生成update 的語句,比如update tableName set status=0,update_user=1,update_time=now() where id=?。因此在后期的權限系統中經常有這種操作。那么在寫daoimpl實現類中就不用顯示的聲明這樣的sql了,傳入幾個參數返回sql,調用DAO框架提供DAOOperator.updateBySql()就可以了。
目前這個工具類已經在多個工程中應用,極大的解決了一些重復代碼和穩定性問題。
- EnumsUtils工具類
痛點:這個工具類是為了解決當時招聘系統和corehr系統中存在的大量枚舉(30+)而針對性的實現的一種統一的解決方案。招聘系統和corehr系統中的一些配置類的數據有些放到了公共數據配置平臺里面以k-v的形式存儲,有些就放在了代碼里面,由于工程模塊眾多,很多枚舉兩個系統都要共用,一方面存在代碼重復,另一方面對業務升級,前后端關于枚舉數據的交互都復雜很多。
實現原理:java類加載和反射。通過指定要掃描的枚舉包掃描枚舉類,然后通過反射讀取枚舉中的屬性值信息,并以Map<String,Map<String,String>>作為方法的返回值。
使用說明:該工具類有兩個方法,用來解決從key到value的映射和value到key的映射。舉個例子,比如我有員工類型枚舉(StaffTypeEnum)0:正式員工,1:外包員工,2:顧問 這些數據。傳入當前工程的包名,工具類自動掃描到這個枚舉類,讀取數據返回Map<String,Map<String,String>>格式的數據。從key到value的查找就是首先通過StaffTypeEnum類名去Map<String,Map<String,String>>找到這個枚舉的子map,比如我想知道1代表什么類型的員工,直接從map中取就行了。相應的,從value到key也是同樣的過程,只不過要調用另外一個方法。那么如果枚舉中有多個屬性數據呢,比如id,code,codeName這種三元組作為一個枚舉元素的話,使用上面的方法怎么解決呢?其實在獲取這個枚舉元素的話方法里已經判斷了枚舉元素的個數,如果大于2個的話,那么默認第一個值是key,比如id是key,code和codeName就是value了,相反的,從value-key的映射也是以codeName作為key,id,code是value。多個屬性值通過-符號拼接,業務代碼自己處理需要的屬性。不過這種枚舉中存在多個值的情況比較少,一般都是k-v結構,或者單純只有value. - 報表工具類
這里的報表工具類之前也做過一些,在一開始的物料管理系統中做過實踐,當時我的項目組長使用了另外一個工具類基于JXL開發的。只是當時jxl有些限制,數據超過65535條之后就寫不了了,也不支持高版本的excel.因此后期統一使用POI了。再后來基于easyexcel組件在corehr系統中解決了報表導入的問題。
maven插件&代碼生成器
痛點:企業服務系統大量立項開發,眾多項目開發需要手寫大量entity,dto。光寫這些基本半天時間過去了。
解決方案:當時想的一個解決方案就是基于maven插件去做,程序自動讀取db.properties配置讀取測試環境中的表數據,然后去動態生成。
實現:實際上實現是有點曲折的,由于maven本身版本的問題,導致插件在某些版本是無法運行的,另外一方面這種代碼生成器實際上不應該放在pom.xml中被項目引用。因此后期又重新改版了一下。基于java jar+命令的方式實現。自定義xml配置,先讀xml中的自定義配置,比如包,類的前后綴等。然后再初始化db鏈接。當時做了兩個版本一個版本是基于公司DAO框架的,另外一個版本是基于JDBCUtils工具類的,向開源靠近。這個工具經過3次迭代才感覺讓人滿意,然后效果也不錯。后期也跟架構部提供的這種代碼生成工具做了一些對比,總體來說各有優勢。雖然最后應用的次數不多,但是也成長許多。這個工具后來催生了部門領導和同事想搭建一個整體的代碼生成器服務的想法,并正式立項開發了。我也合并了一些自己的代碼~~。
orm框架daoclient
背景:寫這個daoclient組件實際上是興趣使然,當時花了一些時間看了公司的DAO框架,不同于mybatis和Hibernate,這有點新奇。簡單點來說就是比較高級的JDBCUtils,加入了entity注解解析,結果集映射和編程式事務。
實現方案:基于java注解+反射實現,實現的過程中對java中的大多數數據類型做了處理,同時實現了相對簡單的jdbc鏈接池。
git地址:https://gitee.com/codergit.com/daoclient
寫文檔生成器+接口自動化測試服務
背景:文檔生成器其實很早就想動手去寫了,主要是前后端分離和項目代碼人員流動之后很多代碼變得不容易懂了,業務文檔也不多。另外一方面就是項目也比較多,有個統一的文檔生成器和管理工具會更有效率。當時第一版本已經實現了掃描源碼獲取代碼中的文檔api信息了,后期由于項目時間緊張同時部門領導也不太支持這么干,有其他前端團隊在搞,因此這個開發到了一半就停滯了。前端團隊提供的接口api管理工具也比較原始各種手動輸入,也沒有自動化測試,被部門里很多開發同事吐槽,大概過了一年多后,我又重新設計了一版,將文檔生成器和接口自動化測試整合到一起形成從開發到測試到文檔維護等的整體功能閉環。
實現過程:在考慮實現的時候也考慮了下面三種方案1.maven插件2.java jar 3.直接讀打包的class jar和源碼Jar。經過各方面的考量決定使用第三種方式。于是在項目不是很忙的時候我又找回了第一版的代碼,重新基于公司的RPC框架和WEB框架做了一些調研,在開始立項之前先咨詢了各個技術部門的老大以及多位自身測試開發大佬。雖然有些負面反饋,但是我覺得還是可以嘗試搞一搞的。
實現原理:
1.通過Maven編譯插件打出class包,然后上傳到文檔生成器界面中,由程序自動通過類加載和反射的機制獲取接口的接口包名,接口名,參數和返回類型等元數據,存儲到數據庫中。
2.通過maven 源碼包插件打出源碼包,然后上傳到文檔生成器界面上,由程序自動解析代碼中的注釋,存入數據庫中。
3.將上述解析到的數據通過界面展示,然后錄入對應的參數發起接口測試調用。
最終結果:項目的核心代碼基本完成,多個服務的文檔注釋,元數據等都已存入數據庫中。
fastjson升級工具
痛點:我所在的部門有眾多工程服務,大量引用了fastjson jar包,由于fastjson從19年開始暴露出多個版本的嚴重bug導致部門內工程服務升級困難,升級時間延長。因此想通過一種方式去解決多個工程引入fastjson pom的問題,將低版本fastjson升級到高版本。
實現方案:調研java 通過jgit連接到遠程git庫,然后掃描本地項目的工作目錄,將pom.xml讀取出來,顯示替換fastjson 為高版本,然后自動提交到遠程倉庫。
最終成果:我多次負責了部門內fastjson的升級工作,因此這個升級工具也跟著升級了2次。但是最終的使用效果卻不太好。手動狗頭~~
輕量級監控平臺
痛點:我在企業信息部門的中后期做了一些系統調優的工作,發現在監控方面公司做的還不太夠,雖然有了比較全的監控和報警。但是針對測試環境,針對線上環境的特定維度都還有些欠缺,也跟負責監控的同學聊了一下,另外跟運維的同事溝通了自己的想法。決定基于shell做一些輕量級監控的腳本和數據收集工作。
實現方案:基于Linux shell腳本實現機器ip,機器負載,機器內存磁盤,java服務,進程,線程,gc等信息的收集和上報,使用一個全局shell做腳本下發和腳本更新的工作。收集上報之后通過服務端的定時任務去掃數據結合釘釘機器人做報警。
最終成果:產出多個針對性的監控腳本,在測試環境中多次準確報警fgc,磁盤占滿等情況,基于公司框架和springboot做了兩套。雖然不夠生產級的水準,但是可作為入門監控的一種選擇。
說明:git地址中的代碼沒有監控腳本,為了安全考慮將涉及公司信息和公司代碼的地方做了簡化處理。相應的腳本已經在之前的博客中發布了,可以做對應的看。
git地址:https://gitee.com/codergit.com/lightmonitor
aop集成工具組件
痛點:我們部門負責的多個服務,在維護的過程中出現了一些排查困難,性能優化效率比較低,線上動態sql不知道怎么拼的問題。另外一方面接口參數調用校驗也存在一些硬編碼問題。
實現方案:1.集成公司架構部的DAO組件,RPC框架,Spring AOP,使用aop的多種通知方式結合自定義注解做方法執行攔截,聲明式事務的提交回滾,sql打印等功能。
- 聲明事務
- 編程事務
- 日志打印
- 參數校驗
- 接口執行時間
- 內部方法執行時間
- sql打印
最終效果:組件整體實現了2個版本,同時在3個項目中實踐,后期也規劃了一些整合redis等功能的需求。
git地址:https://gitee.com/codergit.com/aop-scale
數據庫表結構規范設計檢測工具
痛點:這個工具源于一次關于數據庫表設計的坑,當時要做動態報表的導出,報表涉及8張表,150+字段,任意選字段,任意導數據。在進行動態報表導出的時候需要對一些配置類的,枚舉類的數據做一些變換,比如staffid換姓名,xxType換xxName這種的,因此實現之前需要對8張表的所有字段全部摸清分類,但是由于表字段和注釋等不規范,整體方案落地過程中經常掉坑里。
實現方案:基于mysql的數據庫表和字段的元數據做分析,并將不符合設計規范的地方收集起來,然后通過easyexcel或者輸出控制臺的方式去展現這些問題。
最終效果:整體功能基本完成,結合阿里巴巴java開發手冊-數據庫設計章節和公司數據庫設計規范文檔做基礎,完成了多個場景的檢測。目前通過開源框架重新實現了一個版本,已開源。
git地址:https://gitee.com/codergit.com/dbmodelcheck
高性能數據導出服務
背景:這個導出服務源于一次和同事的討論,有個財務單據的導出需求,說要導出10萬單據,貌似字段挺多的樣子,意思是實現起來可能會有性能問題,先把需求懟回到產品吧。我這邊想著能否寫個工具去并行導出,提高導出性能。于是就自己搗鼓去了
實現方案:基于公司框架和java 8的并行api做異步導出和數據轉換,另外整合easyexcel做報表的生成。
最終效果:整體功能基本完成,經過多輪測試30萬數據最快5秒導出完成,雖然沒有最終用到項目中,但是這確實是一個解決報表導出比較好方案。目前通過開源框架重新實現了一個版本,已開源。
git地址:https://gitee.com/codergit.com/commonreport
整體來說我可能并不是那么安分的去做分內之事,也不想在公司里真正的去摸魚,總要追求一些東西,不斷擴展自己的邊界和能力。上面的工具,服務等雖然有些成功的應用到了企業系統中,有些失敗了,甚至根本沒有被實戰過,但是至少我去嘗試了,也算一種成長吧。對于上面的工具如果有你認可的,歡迎給個star哈。后續會另外創建系列文章講述基于分布式微服務的租房架構設計實踐,歡迎關注。
架構設計@工程設計@服務穩定性之路