搭建基於springboot輕量級讀寫分離開發框架

Grissom007發表於2021-02-08

何為讀寫分離

讀寫分離是指對資源的修改和讀取進行分離,能解決很多資料庫瓶頸,以及程式碼混亂難以維護等相關的問題,使系統有更好的擴充套件性,維護性和可用性。

一般會分三個步驟來實現:
一. 主從資料庫搭建
資訊管理系統的絕大部分瓶頸在資料庫,通過搭建主從資料庫,寫到主資料庫,讀取從資料庫,提高資料庫的吞吐量,根據業務需求可以搭建一主一從、一主多從的資料庫同步架構。如果報表多的系統,可以搭個一主多從架構,一個從資料庫供普通查詢,另一個從資料庫供報表查詢,這樣能夠避免報表的複雜查詢影響客戶正常操作。

二. 讀寫程式碼分離
程式碼上對讀寫進行分離。讀的邏輯相對簡單,幾乎不需要做過多的分層封裝。大部分業務邏輯在寫操作,所以我們需要專注於對寫程式碼的分層、抽象封裝。注意: 在寫模組涉及到業務資料讀取,幾乎要實時的,而且基於高內聚的原則,應該封裝進寫程式碼類中,讀取主資料庫。

三. 程式分離
將讀和寫的程式碼封裝到不同的程式,從程式級別避免相互影響,其實就是分散式。實現從程式上解耦,程式執行期間的效能、異常錯誤不會相互影響,所以系統有相對高的可用性。

這裡多說一句, 如果對寫業務按領域拆分到不同的程式,會涉及到分散式事務,在未涉及到高併發、大資料的系統,其實沒必要從程式上拆分,分散式對事務不友好,為了處理分散式事務,你需要付出更多的時間和金錢成本。考慮程式拆分,一定要基於實際業務需求再三權衡利弊。很多時候,也許你只需要多一個從資料庫、一個快取、多一臺伺服器、多幾G記憶體、多幾核cpu、優化一下sql 即可解決很多效能上的問題。

如何搭建

現在我們搭建一主一從資料庫架構, 並且實現從程式碼上進行讀寫分離的開發框架,但不涉及程式分離。

1. 搭建主從資料庫

mariadb 可參考搭建 mariadb 資料庫主從同步 或者 https://mariadb.com/kb/en/setting-up-replication/

2. 基於springboot 搭建開發框架

2.1 專案結構

畫一下框架的模組結構

  1. api 模組相當於 gateway, 接收和響應請求,還包括鑑權, 引數的校驗和組裝,呼叫 command, query 的介面。
  2. common 模組封裝一些和業務無關的通用功能類。
  3. query 是讀模組,封裝非實時的查詢介面,查詢"從資料庫"。
  4. 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 請求

至此,我們們的專案結構已經搭建好。
下一篇再寫如何訪問資料庫。

原始碼

https://github.com/grissomlau/cqrs-springboot

相關文章