搭建基於springboot輕量級讀寫分離開發框架
何為讀寫分離
讀寫分離是指對資源的修改和讀取進行分離,能解決很多數據庫瓶頸,以及代碼混亂難以維護等相關的問題,使系統有更好的擴展性,維護性和可用性。
一般會分三個步驟來實現:
一. 主從數據庫搭建
信息管理系統的絕大部分瓶頸在數據庫,通過搭建主從數據庫,寫到主數據庫,讀取從數據庫,提高數據庫的吞吐量,根據業務需求可以搭建一主一從、一主多從的數據庫同步架構。如果報表多的系統,可以搭個一主多從架構,一個從數據庫供普通查詢,另一個從數據庫供報表查詢,這樣能夠避免報表的複雜查詢影響客戶正常操作。
二. 讀寫代碼分離
代碼上對讀寫進行分離。讀的邏輯相對簡單,幾乎不需要做過多的分層封裝。大部分業務邏輯在寫操作,所以我們需要專註於對寫代碼的分層、抽象封裝。注意: 在寫模塊涉及到業務數據讀取,幾乎要實時的,而且基於高內聚的原則,應該封裝進寫代碼類中,讀取主數據庫。
三. 進程分離
將讀和寫的代碼封裝到不同的進程,從進程級別避免相互影響,其實就是分佈式。實現從進程上解耦,程序運行期間的性能、異常錯誤不會相互影響,所以系統有相對高的可用性。
這裡多說一句, 如果對寫業務按領域拆分到不同的進程,會涉及到分佈式事務,在未涉及到高並發、大數據的系統,其實沒必要從進程上拆分,分佈式對事務不友好,為了處理分佈式事務,你需要付出更多的時間和金錢成本。考慮進程拆分,一定要基於實際業務需求再三權衡利弊。很多時候,也許你只需要多一個從數據庫、一個緩存、多一台服務器、多幾G內存、多幾核cpu、優化一下sql 即可解決很多性能上的問題。
如何搭建
現在我們搭建一主一從數據庫架構, 並且實現從代碼上進行讀寫分離的開發框架,但不涉及進程分離。
1. 搭建主從數據庫
mariadb 可參考搭建 mariadb 數據庫主從同步 或者 //mariadb.com/kb/en/setting-up-replication/ 。
2. 基於springboot 搭建開發框架
2.1 項目結構
畫一下框架的模塊結構
- api 模塊相當於 gateway, 接收和響應請求,還包括鑒權, 參數的校驗和組裝,調用 command, query 的接口。
- common 模塊封裝一些和業務無關的通用功能類。
- query 是讀模塊,封裝非實時的查詢接口,查詢”從數據庫”。
- command 是寫模塊,封裝領域的業務邏輯,操作”主數據庫”。
按照上圖,用 idea 創建項目結構如下
對 command 和 query 模塊再進行細化
因為 command 模塊我們使用領域驅動開發,所以拆分成服務(Service), 倉儲(Repository), ORM, 聚合根(Aggregate)。
Query 只是簡單的查詢,我們直接用 Dao 訪問數據庫,然後把數據轉成 DTO 返回。
根據上圖,再細化項目的文檔結構
2.2 配置文件
假設我們有三個環境, 分別是開發(dev), 測試(uat), 生產(prod)。每個模塊都有單獨的配置文件。
api:
application-api-dev.yml
application-api-uat.yml
application-api-prod.yml
command:
application-command-dev.yml
application-command-uat.yml
application-command-prod.yml
query:
application-query-dev.yml
application-query-uat.yml
application-query-prod.yml
在系統啟動時,指定使用的環境, api 作為啟動項目,添加 bootstrap.yml, 因為 bootstrap.yml 優先於 application.yml 生效,所以可以在 bootstrap.yml 配置啟動環境。
我們啟用 dev 環境, bootstrap.yml 內容如下:
spring:
profiles:
active: common-dev,command-dev,query-dev,api-dev
那麼,對應的 application-common-dev.yml, application-command-dev.yml, application-query-dev.yml, application-api-dev.yml 配置文件將起效。
2.3 運行
在 query 項目添加一個接口
public interface UserQueryService {
String getName(Long id);
}
並實現它
@Service
public class UserQueryServiceImpl implements UserQueryService {
@Override
public String getName(Long id) {
return "my name is grissom" + id;
}
}
在 api 中調用該接口
@RestController
@RequestMapping("user")
public class UserController {
private final UserQueryService userQueryService;
public UserController(UserQueryService userQueryService) {
this.userQueryService = userQueryService;
}
@GetMapping("/name/{id}")
public String name(@PathVariable("id") Long id) {
return this.userQueryService.getName(id);
}
}
將 api 作為啟動項,配置 application-api-dev.yml, 開放 8003 端口
server:
port: 8003
用 postman 請求
至此,咱們的項目結構已經搭建好。
下一篇再寫如何訪問數據庫。
源碼
//github.com/grissomlau/cqrs-springboot