本篇是企業服務架構演進系列的第二篇,副主題是單體架構的變遷。短短2年,我在進行工程系統開發的時候不知不覺已經踩上了單體架構服務的尾巴,從而邁向分布式微服務的大道。
本系列主要講解我在之前公司的一些工程設計見聞和思考,整個系列大概有7篇文章。
- 企業服務架構演進-引言
2.企業服務架構演進-單體架構的變遷
- 企業服務架構演進-從jquery到vue的工程實踐
- 企業服務架構演進-單庫多服務的尷尬
- 企業服務架構演進-第三方系統與自研之道
- 企業服務架構演進-走上造輪子之路
- 企業服務架構演進-重復開發之殤
背景:我2016年就職于北京一家互聯網公司,從實習到正式工作都在這家公司度過,技術棧基本基于自研框架,從事企業信息系統開發。為了更清楚的描述下文中的一些信息,我先說明公司的WEB框架,RPC框架,Scheduler框架,MQ框架是不同的技術框架,當然包括DAO組件也是獨立的開發包。這些技術中間件由專門的技術平臺負責維護。最后我們的工程其實是按集群劃分的,也就是說一個WEB/RPC/Scheduler工程都是一個獨立的集群,有唯一的集群名和域名。
一、WEB瀏覽器下的物料管理系統
我剛進入公司實習有幸轉到剛籌建成立的信息技術部,并開始做部門成立后的第一個項目。轉正之后依然在繼續維護并開發新的功能模塊。毫無疑問,這個系統是一個單體系統,或者基于一個WEB框架+一個DAO組件即可完成整個后端的業務邏輯。前端采用的是JQuery.js。我們的模板渲染引擎是WEB框架自帶的velocity組件。這在當時算是前后端一體的,一個開發人員從表結構設計,DAO接口,Service接口,Controller層,前端JS和HTML等都需要依次開發完成,然后完成自測,最后跟其他功能模塊做集成測試。當時的我們還有線上機器的部署權限和線上庫的讀寫權限,也就是說我們也承擔著運維的一部分工作。
下面看一下這個單體系統的架構圖:
從物理部署上來講,我們的物料管理系統實際上有2臺機器,定時任務有1臺機器,后端直接連數據庫,由運維部門的人負責部署Nginx集群,另外我們只是用了很少的redis的緩存功能。這里Redis其實也是由數據庫部門的人負責運維Redis-Sentinel集群。
下面從包結構上來說,service,controller,repository,util,bean,entity等都放在同一個父包路徑下,這里用一個簡單的圖來標示一下:
最后上一張該物料系統的業務功能大圖:
上面這個圖展示了這個系統所呈現的基本業務功能,從上圖中我們可以看出這個系統跟進銷存系統差不多,只是我們當時是針對于公司業務線需要用的一些物料進行采購,調撥,出庫,入庫等進行管理。
二、走向RPC的電子合同系統
公司其實在創立之初就引入了RPC框架,由于部門的發展,我負責維護物料系統很長時間,然后慢慢隨著業務的調整這個系統最后正常走向下線的道路。在這段時間里部門內很多其他同事已經嘗試使用自研RPC框架進行項目開發了,因此我也在正常的項目調整中加入了一個新的項目組,第一次在項目實戰中使用RPC框架。新的項目是一個電子合同系統,用來幫助業務線在簽署合同方面降本提效。這個項目采用標準的工程架構:1個web工程,1個RPC工程。我當時負責web工程的整體建設,項目組長負責電子合同的設計和架構以及對整個項目的把控,包括與業務方聯調,跨部門配合等。此時還沒有引入前后端分離的架構思想,因此還是一個人負責整個web端的接口和前端,這里從RPC接口,到web端接口,到前端基本是一個人搞定。前端當時已經摒棄了JQuery.js,而是選擇了當時比較火的AngularJS。做這樣的決定也是因為當時財務系統已經大量采用了AngularJS做前端的數據綁定和表單渲染,另一方面也幫忙踩了一些坑,我們可以引用一些基于AngularJS封裝的組件。
這里我也大概畫一下業務流程:
三、分布式定時任務與容器
在公司的3年多,做過很多系統的定時任務,也大概看了一些定時任務的源碼,這里針對定時任務框架其實也有一個演進的過程。定時任務框架依賴zookeeper集群和Quartz,啟動一個容器進程,連接到zookeeper集群。我們通過定時任務管控平臺設置要執行的類和方法,設置cron表達式,設置執行的機器IP即可配置完成。經過架構設計變遷以及架構部的升級要求我們后面申請新的定時任務工程將以web容器為基礎,而不是單獨一個jar或者一個lib容器作為啟動環境。相當于對整個技術棧進行了升級,后面的更新和維護會更加方便。
如上圖是未改造之前的部署形態,每個job中可以執行多個任務方法,每個job相當于一個定時任務工程。當我們升級之后scheduler容器本質上就是一個web容器了。
四、企業服務系統的生態
上面僅僅列舉了兩個系統來說明我從單體系統到分布式系統的變遷之路,在這個路上也有很多其他系統,這些系統慢慢組成了企業服務系統的一個雛形,這里我列舉一下大概有哪些系統(已下線和正在運行的系統):
1.work系統(各個子系統的入口,公司公告,feed,消息,審批等功能模塊)
工程架構簡述:僅有WEB工程和少量的表+少量的RPC接口服務組成
2.inpass系統(SSO單點登錄系統,服務于全公司內部系統)
工程架構簡述:2個WEB工程,1個RPC工程,一個客戶端jar包
3.會議室系統(服務于全公司進行會議室預定,權限設置,搶占等功能)
工程架構簡述:僅有WEB工程,前后端不分離+AngularJS,定時任務代碼當時也在WEB工程中,沒有遷移。
4.行政系統
工程架構簡述:僅有WEB工程,前后端分離+VUE
5.HR系統
工程架構簡述:多個WEB工程,多個RPC工程,前后端分離+VUE
6.招聘系統
工程架構簡述:多個WEB工程,多個RPC工程,前后端分離+VUE
7.權限系統
工程架構簡述:多個WEB工程,多個RPC工程,前后端分離+VUE
8.流程平臺
工程架構簡述:多個WEB工程,多個RPC工程,前后端不分離+AngularJS
9.電子合同系統
工程架構簡述:3個WEB工程,1個RPC工程,前后端不分離+AngularJS
10.feed流系統
工程架構簡述:1個WEB工程,1個RPC工程,前后端分離+VUE
11.工單系統
工程架構簡述:3個WEB工程,1個RPC工程,前后端分離+VUE
12.工程研發效能系統
13.財務系統
工程架構簡述:1個WEB工程,少量RPC接口,VUE&AngularJS
14.代碼生成服務系統
工程架構簡述:1個WEB工程+VUE
15.請假系統
工程架構簡述:2個WEB工程+1個RPC+VUE
16.釘釘端微應用(移動審批,會議室預定,請假,單點登錄驗證,招聘,hr入轉調離)
工程架構簡述:1個WEB 工程+VUE
五、服務拆分怎么拆分?
從上面看大部分系統里的內容都是CRUD,但是這些CRUD工程怎么設計,是獨立還是整合為一個工程還是有講究的,假如我們把hr或者招聘看做一個服務的話,那么該拆分幾個工程來表示呢?該怎么梳理工程之間的關系呢?一個工程搞定一切還是說以微服務的思想來一個子模塊或者大功能整一個新的工程?又或者說有第三方(其他業務線部門或者外部系統)調用企業內部系統是否需要新建工程去隔離核心業務呢?
基于公司的技術棧和當時組長制定的策略,我這邊總結了一些常見的工程套路如下:
1.WEB->DB
第一種:這里從工程的角度來說,就是一個web系統包括了service,controller,dao,bean,entity,是最簡單的一種工程設計了。
2.RPC->DB
第二種:根據需求或者場景如果不需要WEB工程(可視化管理界面),或者僅僅需要一些RPC接口訪問DB就可以提供服務,這也是RPC中最簡單的一種工程設計。RPC中包括了client jar,server jar,根據dto,entity,service,dao做了模塊和包的分層。核心思想就是將業務邏輯下沉到server中的service包中,通過client包里的接口對外發布。防止WEB或者API工程涉及過多業務邏輯導致代碼不好維護。
3.WEB->RPC-DB
4.API->RPC-DB
第三種和第四種:這兩種本質上其實是一樣的,為啥分為兩種呢?區別就在于API只有HTTP 接口和文檔,WEB除了接口和文檔之外還有可視化界面(囊括了前后端分離的場景)UI等,另外一方面WEB工程也在接口內容上跟API不同,WEB可以指定一些跳轉策,業務上可以聚合多個RPC接口為頁面提供數據。
5.Scheduler->RPC-DB
第五種:這種方式在做過一些項目之后我是比較推薦采用的,雖然跟第三種一樣寫代碼麻煩了些,但是在工程設計和業務邏輯下沉方面會好很多,RPC工程會聚合實現整個服務所需要的各種接口,升級包或者dto/entity均以RPC client為準會讓工程迭代更為順暢,當然也不是沒有缺點。就是一點點小的改動可能都要動RPC工程。
6.Scheduler->DB
第六種:這種方式其實有點另類,一般情況下Scheduler工程很少會單獨作為一個服務,除非是任務調度類的。Scheduler工程中主要有service,job,entity,dao這幾個包。當這種情況存在于一個服務中的時候就要小心了,除非scheduler工程很少改動否則建議使用第五種方式。因為RPC->DB這一層其實也解決了訪問數據源的問題。上層API和WEB都不需要單獨申請數據源。另外,如果一個scheduler中有很多定時任務方法要執行,涉及到比較多的業務邏輯的話那么勢必會有代碼冗余,最后一個缺點就是一旦有新功能升級很大可能就是兩個工程都要拉分支去改,無意增加了維護成本。
六、如何建設一個分布式系統?
基于上面的討論內容,我們來解決一個實際的問題:如何建設一個分布式系統?
不管什么規模,基于現有的技術底座:WEB,RPC,Scheduler,DAO等其他技術中間件我們如何更好更合理的設計不同工程模塊。是將A模塊放到WEB中合理還是放到RPC中合理。假設我們有一個業務系統需要建設,涉及到管理后臺,定時任務,移動端,RPC服務,與公司其他部門系統進行交互,與外部第三方系統進行交互來完成整個業務系統的功能。那么我們如何像搭積木一樣更好更有效率的去完成這些工程呢?這里有一些我個人的思考和需要著重注意的地方:
- 需要有WEB,RPC,移動端API,對外調用API,Scheduler這幾個工程。
- 與公司內其他部門系統交互可以在RPC&移動端API中提供交互接口。
- WEB工程需要接入公司的單點登錄系統&權限系統等基礎服務。
- RPC工程承擔了CRUD的核心操作,因此,如果涉及敏感接口,則建議增加鑒權機制。
- 對外調用API工程,一般情況下我們對外調用的API基本上還是以HTTP協議為主,此時就要考慮到一些安全性問題,比如接口限流,防爬蟲,數據加密等。
- 移動端API通常與對外調用API有一樣的問題,但是一般情況下基于釘釘微應用或者其他小程序載體等的企業信息化操作都需要先登錄鑒權。
- Scheduler工程其實在一個稍微復雜的業務系統中肯定會存在,如果需要定時任務的地方比較少,業務也比較簡單那么可以集成在WEB工程或者RPC工程中,通過架構部提供的scheduler插件可以解決定時任務容器集成到WEB框架或者RPC框架的問題。如果業務比較復雜那么建議單獨建立工程,初始化集群去搞。
- 上面設計的核心思想就是要將業務邏輯下沉到RPC service中去,通過合理的分包,合理復用接口和設計模式等降低RPC service的復雜度提高RPC工程的質量。將RPC工程作為整個服務的核心,其他工程作為服務的外延,呈現出一個分布式的服務系統。