分佈式、服務化的ERP系統架構設計

ERP之痛java

       曾幾什麼時候,我混跡於電商、珠寶行業4年多,爲這兩個行業開發過兩套大型業務系統(ERP)。做爲一個ERP系統,系統主要功能模塊無非是訂單管理、商品管理、生產採購、倉庫管理、物流管理、財務管理等等。做爲一個管理系統,你們的通常開發習慣就是使用.Net或Java技術,創建一個單塊(單進程)架構的應用,只有一個SQLServer或MySql數據庫。而後在項目文件中分一下各個模塊,三層結構方式組織代碼編寫開發。最後測試,交付上線。web

        起初,由於數據量不大,系統性能還不錯,各類列表查詢,報表查詢,Excel數據導出功能等用的都很流暢。可是隨着公司業務發展,訂單量日積月累,後期各類業務部門的報表查詢、數據導出需求不斷增多,咱們漸漸就感受系統運行愈來愈慢。因而咱們可能最早想到的解決方案就是,優化系統瓶頸數據庫這個大頭。咱們可能的一種嘗試就是將數據庫單獨放置到一個服務器,實現數據庫和應用程序分離,或者是創建各類數據庫表索引,優化程序代碼等方法。通過這樣一番研究優化,系統某些功能可能性能的確大大提升,可是咱們仍是發現某些功能列表的數據查詢導出依然很慢,或者隨着數據量繼續積累,原來較快的列表導出功能,也越來越變得緩慢了。咱們用盡各類辦法,最後也達不到理想的系統性能速度。數據庫

        爲了提升系統性能,咱們也許會主動學習一些互聯網公司的技術經驗,什麼高併發、高性能、大數據、讀寫分離等方案,發現本身根本無從下手。咱們會以爲由於系統業務特色不同。ERP系統併發量不高,主要是業務複雜,各類業務耦合度遠高於那些互聯網應用,很差作拆分,數據查詢邏輯要遠比互聯網系統複雜,一個列表頁查詢出來的數據,每每須要關聯四、5張表才能獲得結果。有些報表類的甚至更多。加上各類業務操做事務性、數據一致性要求很高,不少時候致使咱們措不及手,沒法進一步優化系統。緩存

        曾幾什麼時候,我也被這樣或那樣的理由所挫敗,認爲ERP系統很是特殊,無藥可救,但是後來。。。服務器

        我如今已經不這麼認爲了,彷佛有了新的解決方案O(∩_∩)O哈哈~restful

曙光乍現網絡

       在敘述具體方案前,先說下本身的想法。我首先以爲咱們作ERP系統前,就得有當今互聯網思惟。咱們不要再去作一個大一統的系統了。咱們要分拆一個大系統,作成一個個小系統。而後經過系統接口讓這些小系統相互通訊。這樣來組成一個大系統,具體來講就是「分佈式」、「服務化」的互聯網思惟。讓系統在架構設計上就是一個先天支持高度可擴展的系統。架構

       怎麼作呢?具體來講就是要將訂單管理、商品管理、生產採購、倉庫管理、物流管理、財務管理拆分紅一個個子系統。這些子系統能夠單獨設計開發,對外暴露出各類其餘子系統需求的數據接口便可。每一個子系統都有單獨的數據庫。甚至這些子系統能夠交由不一樣的團隊去開發和維護,使用不一樣的技術體系,使用不一樣的數據庫。而不是再像之前那樣,都集成在同一個大而全的系統中,一個大而全的數據庫。併發

       對於新架構的系統他有什麼優勢呢?負載均衡

       首先,也是最重要的就是解決系統的性能問題。以往數據庫實例只有一個,無法擴展出多個實例,以便在性能受限的狀況下依靠增長數據庫實例來達到負載均衡。也許有人會說可使用讀寫分離方案,可是由於ERP系統的特色,這個方案不少時候不現實。好比說操做庫存的時候,你不能從讀庫裏讀庫存,而後在寫庫裏寫入庫存。由於主從複製會有時效性,寫入的庫存並不能立刻寫入從庫。這樣的場景在ERP中也有多處。況且寫庫不能擴展,只能有一個。而新設計方案是寫庫是分離的,每一個子系統有本身的數據庫。

       其次,就是更新很是方便,各個子系統之後臺微服務的方式存在。前臺一個單獨的web項目,這個web項目調用後臺這些子系統的服務接口。這樣的設計,在某個業務子系統須要更新的時候,能夠單獨更新。不用像之前那種單進程架構時,一個小更新須要整個系統重啓,致使用戶會話也丟失,用戶須要新登陸。而如今的這種設計就不會有這個問題。

系統總體設計

      系統物理部署視圖

詳細設計

   拆分應用層

        拆分應用層,是踐行「微服務」架構的理念。將原來大而全的單進程架構按照業務模塊拆分紅可獨立部署的應用程序,以此來達到平滑系統更新、升級、方便負載擴展的目的。具體來講,技術上可使用restfull風格的接口,也可使用像java中dubbo框架方式來簡化開發複雜度。ERPWeb端或其餘移動端也是一個單獨的應用充當表現層。很是薄,只是簡單的接受參數,調取後臺其餘各類微服務程序的接口獲取所需展現的數據。微服務充當業務邏輯層,每一個微服務都是可獨立部署上線的程序,對外提供數據訪問接口。

        微服務可使用流行的各類RPC框架,好比dubbo,能夠支持多種調用協議Http、TCP等,這些框架使得編碼比較容易,框架封裝底層數據通訊細節,使得客戶端執行遠程方法如同執行本地方法同樣簡單。

        dubbo微服務架構,還支持服務治理,負載均衡等功能。這樣不只能夠提升系統的可用性,還能動態提高系統應用層的性能。好比倉庫管理中入庫業務很是繁忙,佔用很是多的CPU和內存資源,咱們能夠另外加一臺機器,單獨再部署一個倉庫管理服務上去。這樣使得整個系統,有兩個倉庫管理服務在同時工做,平衡負載。而這一切都是在服務註冊中心,好比Zookeeper下自動完成的。

        微服務結構,天生很好的支持系統更新升級操做。好比財務模塊有個新需求須要上線,咱們只須要替換財務模塊的服務重啓便可。這對已經登陸系統的用戶來講,沒有多少影響,不用從新登錄系統,其餘模塊服務使用也不受影響。

    拆分數據層

        數據庫瓶頸是ERP系統的永久之傷。大量複雜的數據查詢錶鏈接邏輯充斥着整個系統。數據庫垂直拆分紅功的關鍵就是如何從新設計系統數據層各個模塊相互耦合的問題。能解決這個問題,永久之傷即可以解決了。

咱們先來看一個典型數據層模塊耦合問題。需求是展現物料庫存,列表字段:物料編號、物料名稱、品類、倉庫、數量

物料表:

物料ID

名稱

品類ID

Z0001

Iphone6紅色手機殼

Z

Z0002

iPhone6黑色手機殼

Z

庫存表:

物料ID

倉庫ID

數量

Z0001

W1

10

Z0002

W1

20

       品類和倉庫表省略。。。

        很顯然,傳統一個數據庫中,咱們只須要簡單的join操做,便可關聯這兩張表,外加關聯品類和倉庫表便可查詢出咱們所要的數據。可是如今咱們的架構中,物料表和商品表不在同一個數據庫實例中,咱們不能使用join操做了,那咱們該怎麼實現需求呢?

        新的架構,只容許咱們經過對方的服務接口來獲取數據,不能直接關聯對方服務的私有數據庫。至少從架構上,服務化角度來講不能直接訪問對方服務的數據庫。這種狀況下,假設web模塊子系統調用倉庫子系統來獲取數據,則咱們須要在倉庫模塊中建立一個service方法來裝配這些數據。而後返回給web子系統。以下圖所示,倉庫管理方法首先獲取本地庫存表的物料編碼、和倉庫表的倉庫名稱字段信息,而且分頁完後最終準備返回20條數據到Web模塊前,將這20條數據中的物料ID做爲參數請求商品模塊子系統,商品子系統返回這20個物料ID相關的商品信息給到倉庫管理模塊,而後倉庫管理模塊從新組裝上列表所需的物料名稱和品類兩個字段數據,實現最終要返回給Web子系統的數據。

        也許你會說,這太麻煩了,這種方法的性能確定沒有直接join來的高,解決不了性能問題。咋看起來好像是這麼回事,可是仔細考慮看看,在系統併發量低、數據量小、業務不算繁忙的環境下,的確性能還不如傳統一個數據中join方式來的快速。但咱們想一想之後吧!咱們如今的架構設計是將一個數據庫拆成多個數據庫,每一個數據庫能夠運行在單獨的服務器上去,這樣之後就能負載數據庫的壓力了。總體來講這樣才能不會讓數據庫成爲將來業務繁忙時候的性能瓶頸了。想一想都以爲讓人興奮不已,是否是?

       這時候有人又會問,那之後系統數據量、業務更大了,連你這個拆分紅幾個數據庫還不夠用怎麼辦呢?個人方法是,能夠基於拆分的數據庫,單獨每一個庫能夠作讀寫分離、使用緩存等。甚至能夠繼續拆分下去,將子系統再次拆分紅多個孫子系統。視業務模塊繁忙程度而定。

報表系統

        有人又會問,有些列表查詢邏輯很是複雜,關聯十多張表,若是按上述方法拆分數據,那簡直是災難啊!是的,你說的沒有錯。這種狀況下個人方案是將這種更加複雜的報表級別的數據查詢展現需求,能夠單獨作個報表系統。報表數據庫設計採用數據倉庫方式。爲了更高的讀取性能,咱們能夠將數據庫表設計成不少冗餘字段方式也就是反範式設計,以及創建很是多的組合索引。

       這種系統成功的關鍵就是數據和主ERP系統業務庫的同步問題了。通常能夠寫一個定時同步程序,將ERP主業務系統的數據通過帥選、轉化等方式直接生成報表視圖所需的最終或中間數據,簡化關聯查詢。報表系統也能夠採用微服務架構設計。以下圖所示:

        若是報表所需的數據要求實時的,咱們可讓ERP系統業務操做時,觸發同步數據的請求,實時同步至報表庫。

分佈式事務

       也許有人又又問了,ERP系統不少操做都要求事務性,你拆分系統後怎麼實現事務性,保障數據一致性呢?

這個問題很好,也是我決定寫這篇文章前思考的最後一個問題。在微服務架構中,實現誇服務的事務並不容易,至少不像本地應用使用本地數據庫事務那樣方便,性能高效,數據一致性好。

        也許你聽過度布式事務這個概念。有兩種情景,一種是一個應用中使用多個數據庫,爲保障數據一致性,須要使用分佈式事務。還有一種狀況就是針對咱們這個架構而言的。微服務環境下的分佈式事務,具體來講打個比方。採購入庫這個操做設計在倉庫管理服務中。入庫後,須要更新採購子系統中的採購單中的入庫數量。這個過程要求數據一致性,也就是採購單入庫成功後寫入了庫存表中的數量,同時要更新採購單表中的入庫數量。咱們不能直接在倉庫服務中去訪問採購服務中的數據庫,必須經過採購服務提供的服務接口才行。若是這樣,咱們怎麼能保證數據一致性呢?由於頗有可能庫存表寫入成功,但調取採購服務寫入採購單數據時失敗了。多是網絡問題緣由致使的,這樣數據就不一致了。

       在分佈式事務技術中,有實現最終一致性這麼一說,意思就是隻要我能保證兩邊數據最終實現了一致性就行,不必定要使用事務。這樣說來就有方案了。如倉庫子系統在處理採購入庫時須要增長入庫單數據和更新庫存數據等多個表。這多個表都在倉庫子系統中,咱們可使用一個本地事務來保證倉庫子系統中的表數據一致性。而後調用採購子系統更新採購單裏的入庫數量。爲了防止這個過程忽然中斷致使調用失敗,咱們考慮增長一個消息隊列中間件如ActiveMQ。若是接口返回失敗咱們就往MQ裏寫入這個處理請求,等到採購子系統恢復正常後,MQ通知採購子系統處理這個更新操做。因爲消息消費掉之後不會再有通知了,採購子系統處理過程當中發生異常致使更新失敗,須要將問題寫入本地的日誌庫,以便通知管理員作後續補償處理。就這樣經過各類辦法來達到數據的最終一致性便可。雖然聽上去有點坑,但這就是解決方案。沒有其餘更好的了。或者更新失敗後從新調用倉庫子系統回滾入庫單和庫存數據,達到最終一致性!如圖所示:

       很是有幸能和你們一塊兒分享知識和經驗,正是因爲你們的無私分享,才讓咱們得以成長和進步,我最近幾年來都不多分享東西,有時候是由於工做很忙沒有時間寫點東西,有時候也是由於本身懶或是沒有什麼新東西能夠分享給你們的。最後也但願你們對個人分享不足之處給予批評指正,一塊兒進步!

相關文章
相關標籤/搜索