.NetCore 結合微服務項目設計總結下實踐心得


以下內容全是在項目中的體驗,個人理解心得

起源

2017年7月開始接觸.NetCore,當時還是因為Idr4的原因,之前的項目都是用的Idr3做,后面接觸到Idr4后,決定以后所有項目都使用.NetCore來搭建項目架構,隨后我開始研究Idr4的相關使用,后面又接觸到了Ocelot、Cap、Consul、Skywalking、AspectCore、MediatR等優秀庫,從此我決定搭建微服務項目,從此就走上了一條不歸路,接下來我闡述下我在在架構思想上的心得。

項目介紹

項目是一個學生體檢體檢項目,整體分為了認證中心、用戶服務、體檢服務三個部分

我建立了三個服務,這里各自的項目人員開發自己的項目功能模塊,分布開發,多個項目組同時協作,用戶服務,體檢服務分別建立2個數據庫,當然這里業務會存在服務調用服務的情況,前面的文章我也有說過,如果只是查詢,其實在UI端訪問2個不同服務接口就行了,但是如果存在內部業務需要調用另外一個服務的情況,這里保證數據庫最終數據一致就行了,采用了消息隊列RabbitMQ來處理,接口調用接口會出現的網絡原因我也結合了Polly重試來處理,並記錄操作日志,操作日志采用面向切面是來實現(AOP),結合Exceptionless保存日志信息,記錄參數詳細信息

接下來就看是搭建項目結構基礎了,大體上我想的是下面這個結構,把所有的配置單獨出來,Domain層添加領域實體,這里參見了eshopscontainer

領域層

大體結構已經建立好了后,根據業務添加好領域模型,確定了領取模型中領域職責事件,這是我對DDD的個人理解,這里涉及到 聚合根(AggregateRoot)、值對象(ValueObject)等一些東西,為什么要要說聚合根以及值對象,這可能涉及到一個認知改變,就是把EFCore模型中建立的關系型思想轉化為領域對象的認知思維,在以前的項目中記得經常有一個Model層一樣的東東,經常被用來承載數據,穿梭在各個結構層之間,這里要說的Model在思想上可以認為是一個領域或者子領域,我個人是這么理解 領域對象模型:定義或描述一個領域對象自身屬性(模型)、附屬關系(邊界)及領域行為(事件)的對象。

聚合根其實個人覺得跟EF中的上下文對象DbContext類似,打個比方:在DbContext中有很多 DbSet<T> 的對象,其實也可認為這個就是 DbContext下的邊界,而DbContext本生看作一個聚合根,所有的訪問都是DbContext下的 DbSet<T>實現對不同實體操作,定義這個邊界就是為了不讓從外部訪問它,需要通過在聚合根中的定義的邊界訪問,在實際的使用的過程中根據業務定義,這里我在代碼中為了不讓這個關系呈現在我的數據中,所以基本上我的每個領域模型我都是聚合根,這樣是我不想 因為聚合根中的邊界的依賴關系產生生成數據庫關聯關系成強主外鍵關系,所以在這個項目中我 聚合根及值對象基本就被忽略了,從而我更加關注領域模型本生及領域模型中行為。

基礎層

基礎層我封裝了Exceptionless日志處理,使用EFCore生產數據庫的Mapper配置及業務實現處理,以及上下問對象處理,這里需要說的領域中的界限上下文,這里我一個領域所以我只有一個上下文,這個根據業務划分存在多個領域上下文對象,沒有划分核心領域,子領域,一般一個領域(或子領域)對應有一個界限上下文,在服務上我已經分開了用戶服務於體檢服務,實際每個服務都是一個子領域,在結構上已經區分開了。在基礎層做了對IRepositry的倉儲以及MediatR的擴展從而實現消費領域事件。

應用層

應用層相對比較容易了,采用MediatR實現了命令式處理,在這中間我使用AOP封裝了操作日志記錄操作信息,查詢使用了Dapper實現了讀寫分離查詢,在MySql上做了一個主從,這里特別害怕數據同步延遲,所以這里的查詢我只用了那些只單單是查詢的方法,如果跟業務相關的查詢還是使用了EFCore,比如要根據查詢某一條結果然后某一條數據,使用MediatR 中的 INotification 去消費領域事件,另外我還在引用層添加了AggregateServices聚合服務相關處理,關於文件存儲這塊使用了AliyunOSS,目前關於緩存使用Redis做簡單的權限信息緩存。

WebAPI

采用Autofac處理了接管了.NetCore的 DI,封裝了相關注入方式簡化了代碼,另外對驗證做了擴展操作,采用Swagger構建API接口說明文檔,處理了WebAPI的版本信息以及采用RabbitMQ發布訂閱消息,處理來之服務之間相互調用的問題

GateWay網關

采用了Ocelot來做網關相關處理,具體在前面博客中都有介紹了

分布式目標

做了上面的架構后以實現下面的部署結構

 

但是我發現現在最難做的一些事情反而出現在了 數據庫這塊,數據的一致性,容災、目前只是一個主從而已,如果數據量一大,並發高,前面服務的壓力可以通過多個Node來處理算是通過硬件擴展,如果要把服務分的足夠小,在實際業務中復雜度會上升,我想得是一主多從,或者多主多從的情況。

主從數據庫集群

通過程序業務處理做讀寫分離CQRS,在一主一從任然不能滿足我們實際業務需要的時候,我們又該怎么來做呢,這引起了我的思考,一般情況下讀的次數比寫的多,開始考慮一主多從,或者多主多從的情況,這塊就設計了數據庫的高可用負載均衡集群,下面就是數據庫的變化 ,從這樣的結構上來緩解壓力,實際上在業務上我們在數據庫上已經有了拆分,現在無論是垂直或者水平都已經處理,在結構上看起來好像還不錯,但是實際技術細節處理還是非常多的

單獨一個數據庫

 

主從數據庫

 

一主多從

多主多從

有了這些結構,其實還不夠,在一些大型系統中都存在一些報表統計,這些統計來之不同的服務不同的數據庫,這些數據需要聚合查詢,前期設計可以通過分析需求業務制定好統計這一塊的數據冗余,比如最基本的用戶信息等,此外我們還可以通過數據庫同步服務將來自不同業務服務的數據同步到一個庫中單獨做報表查詢,但是這些數據非常多,統計速度會存在問題,其實可以發現就算前面通過數據多主多從的情況下,如果數據量夠大一樣查詢會出現問題,這種結構只分攤降低了每台數據庫服務的查詢次數,但對於每個從庫中的數據依然還是非常巨大,除了我們需要在代碼中寫高效率的查詢語句,建立索引等,我們還需要做什么呢?

對於數據庫統計報表而言,對一個單獨的統計讀庫操作各種聚合查詢還是非常慢,其實這在業務上也是細節處理,可以分離聚合點,先統計一定時間內的生成一個統計數據,最后在聚合多個時間段....統計段位可以按照數據量去設計,比如每天、每月、每年、統計一次,舉個例子最后處理這個月的報表,聚合的就只有30條左右數據

對於讀庫中的數據大表采用分表操作,按照指定的算法來處理分表,試想一個表中有10億條數據,查詢語句已經沒有了優化空間,這個時候按照一定的規則拆分好表不失為一種好的處理方式,如果這些都做完了,我們就需要在硬件上升級處理了

當然還有一部分高並發通過先寫到Redis中,然后在寫到數據庫中的這種

好了,就說這么多了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM