在現代系統中,特別是互聯網軟件,一般會涉及到大量用戶的併發訪問,咱們的系統必定要在架構上支持高性能、大併發的訪問。一個高性能的系統一般由不少的方面組成,包括數據庫高性能、Web服務器高性能、負載均衡、緩存、軟件架構等。咱們這篇文章先從軟件開發架構的角度做爲切入點來介紹如何構建高性能的系統。前端
傳統架構性能的問題數據庫
咱們先來看看DDD經典架構中,在多用戶、大併發訪問的狀況下,對性能產生不利影響的因素。先來看看簡單架構圖:緩存
1.一般會在當前界限上下文中只有一個領域模型,這個領域模型既會用於領域邏輯,同時也會用於持久化。服務器
2.領域模型既會用於用例、也會用於查詢。微信
3.當前界限上下文一般只會有一個WebApi項目,這個項目中不一樣的Action Api用於不一樣的功能,有查詢的,有用例的。架構
從上面幾點你們能夠看出,有如下幾個緣由帶來性能的瓶頸:併發
1.上下文的查詢和用例經過一個模型來作,對應到數據庫來說,就是增、刪、改、查針對同一個數據庫的相關表,經過阻塞會形成性能問題;雖然經過創建好的索引能夠進行緩解,但解決問題不完全。負載均衡
2.領域模型一般是根據業務須要進行設計的,也就是用於用例。若是用於查詢需求的話,可能會鏈接多個業務表才能完成查詢,查詢性能出現問題。微服務
3.一般經典DDD是完成領域邏輯後,經過應用服務協調領域邏輯與倉儲來將領域對象持久化到數據存儲中,而後經過WebApi返回用戶結果。若是領域複雜,用戶併發量大的話,這個過程反饋到前端有必定的時間,用戶體驗很差。性能
4.當前界限上下文是一個WebApi項目,不管是用例仍是查詢;這樣也沒法對性能進行擴展,好比用例的在一些主機上,查詢的在另外一些主機上。
爲了有效的解決上述出現的性能問題,業界總結了一種架構風格,也就是CQRS(命令查詢職責分離)。經過CQRS的理念,能夠有效的提升系統對大併發的支持。
命令指的是要更改對象狀態的行爲,對系統有反作用;查詢指的是不更改對象狀態的行爲,對系統無反作用。其實CQRS不只僅用於大併發的處理,在平常開發中,其實也是能夠利用這種理念的。
命令與查詢混在一塊兒的狀況:
private int Add(int a,int b) { int result = a + b; return result; }
從上面代碼能夠看出,更改狀態與查詢是混在一塊兒的;咱們能夠改形成命令與查詢分離的方式:
private int result; private void Add(int a,int b) { result = a + b; } private int QueryResult() { return result; }
基於上述代碼的思路與經典DDD架構的問題,咱們就引入CQRS架構風格來解決性能問題。
CQRS架構
咱們先來看看CQRS總體的架構圖,而後再說它是怎麼解決性能問題的。
咱們來看看總體架構圖的流程以及它是如何解決性能問題的:
1.首先能夠看到命令與查詢走不一樣的WebApi服務,這樣能夠將更改系統狀態的行爲與查詢的行爲作很好的隔離,並能夠部署微服務到不一樣的服務器上,固然還能夠經過NLB作進一步的擴展。
2.命令端的WebApi並不直接處理調用用例完成,而是接收到用戶命令時,將命令消息發佈到消息總線,而後馬上返回一個操做信息給用戶,這樣用戶體驗很好,不須要等待業務邏輯完成與持久化完成。
3.命令處理器WebApi從消息隊列偵聽到消息,而後進行處理,處理的主要內容是完成領域邏輯調用,直接添加事件數據到事件存儲中。這裏須要注意的是,並非持久化到業務數據庫中。首先完成領域邏輯調用,能夠獲得用例最終正確的領域對象,而後存儲事件時,存儲此次領域對象的狀態,而且是直接添加。這樣作的好處有:一是加快持久化,二是可以保存領域對象每次變化的信息,將來能夠用於歷史追蹤、事件溯源與最終一致性。
4.命令處理器將領域對象發送到消息總線中,事件處理器會偵聽隊列,並最終將消息信息涉及到的領域對象持久化到業務數據庫中。
5.在查詢WebApi中,能夠直接查詢業務庫,若是業務庫並不適合多表鏈接查詢時,能夠再單獨作個拉平的爲查詢提供服務的查詢庫。查詢庫的內容能夠經過業務庫更新成功後,發佈消息到另外一個隊列中,而後經過處理器來處理這些數據到查詢庫中。
QQ討論羣:309287205
微服務實戰視頻請關注微信公衆號: