Spring Boot 優雅實現多租戶架構,so easy~!

来源:https://www.cnblogs.com/javastack/archive/2023/06/19/17490344.html
-Advertisement-
Play Games

## 一、概述 ### 1.什麼是多租戶架構? 多租戶架構是指在一個應用中支持多個租戶(Tenant)同時訪問,每個租戶擁有獨立的資源和數據,並且彼此之間完全隔離。通俗來說,多租戶就是把一個應用按照客戶的需求“分割”成多個獨立的實例,每個實例互不幹擾。 ### 2. 多租戶架構的優勢 - 更好地滿足 ...


一、概述

1.什麼是多租戶架構?

多租戶架構是指在一個應用中支持多個租戶(Tenant)同時訪問,每個租戶擁有獨立的資源和數據,並且彼此之間完全隔離。通俗來說,多租戶就是把一個應用按照客戶的需求“分割”成多個獨立的實例,每個實例互不幹擾。

2. 多租戶架構的優勢

  • 更好地滿足不同租戶的個性化需求。
  • 可以降低運維成本,減少硬體、網路等基礎設施的投入。
  • 節約開發成本,通過復用代碼,快速上線新的租戶實例。
  • 增強了系統的可擴展性和可伸縮性,支持水平擴展,每個租戶的數據和資源均可管理和控制。

3. 實現多租戶架構的技術選擇

對於實現多租戶架構技術不是最重要的最重要的是正確的架構思路。但是選擇正確的技術可以更快地實現多租戶架構。

二、設計思路

1. 架構選型

基於Java開發多租戶應用推薦使用Spring Boot和Spring Cloud。Spring Boot能快速搭建應用並提供許多成熟的插件。Spring Cloud則提供了許多實現微服務架構的工具和組件。

1.1 Spring Boot

使用Spring Boot可以簡化項目的搭建過程自動配置許多常見的第三方庫和組件,減少了開發人員的工作量。

@RestController
public class TenantController {

    @GetMapping("/hello")
    public String hello(@RequestHeader("tenant-id") String tenantId) {
        return "Hello, " + tenantId;
    }
}

1.2 Spring Cloud

在架構多租戶的系統時Spring Cloud會更加有用。Spring Cloud提供了一些成熟的解決方案,如Eureka、Zookeeper、Consul等,以實現服務發現、負載均衡等微服務功能。

2. 資料庫設計

在多租戶環境中資料庫必須為每個租戶分別存儲數據並確保數據隔離。我們通常使用以下兩種方式實現:

  • 多個租戶共用相同的資料庫,每個表中都包含tenant_id這一列,用於區分不同租戶的數據。
  • 為每個租戶創建單獨的資料庫,每個資料庫內的表結構相同,但數據相互隔離。

3. 應用多租戶部署

為了實現多租戶在應用部署時我們需要考慮以下兩個問題。

3.1 應用隔離

在多租戶環境中不同租戶需要訪問不同的資源,因此需要進行應用隔離。可以通過構建獨立的容器或虛擬機、使用命名空間等方式實現。Docker就是一種非常流行的隔離容器技術。

3.2 應用配置

由於每個租戶都有自己的配置需求因此需要為每個租戶分別設置應用配置信息,例如埠號、SSL證書等等。這些配置可以存儲在資料庫中,也可以存儲在雲配置中心中。

4. 租戶管理

在多租戶系統中需要能夠管理不同租戶的數據和資源,同時需要為每個租戶分配相應的許可權。解決方案通常包括以下兩部分。

4.1 租戶信息維護

租戶信息的維護包括添加、修改、刪除、查詢等操作,要求能夠根據租戶名稱或租戶ID快速查找對應的租戶信息。

CREATE TABLE tenant (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(255),
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

4.2 租戶許可權控制

在多租戶應用中必須為每個租戶分別設置對系統資源的訪問許可權。例如,A租戶和B租戶不能訪問彼此的數據。

@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/tenant/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(new BCryptPasswordEncoder())
                .and()
                .inMemoryAuthentication()
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("ADMIN");
    }
}

三、技術實現

1. Spring Boot中的多租戶實現

在Spring Boot中可以通過多數據源和動態路由來實現多租戶機制。

Spring Boot 基礎就不介紹了,推薦看這個實戰項目:

https://github.com/javastacks/spring-boot-best-practice

1.1 多數據源實現

多數據源是指為不同的租戶配置不同的數據源,使得每個租戶都可以訪問自己的獨立數據。具體實現方法如下:

@Configuration
public class DataSourceConfig {
    @Bean(name = "dataSourceA")
    @ConfigurationProperties(prefix = "spring.datasource.a")
    public DataSource dataSourceA() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceB")
    @ConfigurationProperties(prefix = "spring.datasource.b")
    public DataSource dataSourceB() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceC")
    @ConfigurationProperties(prefix = "spring.datasource.c")
    public DataSource dataSourceC() {
        return DataSourceBuilder.create().build();
    }
}

以上代碼是配置了三個數據源分別對應三個租戶。然後在使用時,可以使用註解標記需要連接的數據源。

@Service
public class ProductService {
    @Autowired
    @Qualifier("dataSourceA")
    private DataSource dataSource;

    // ...
}

1.2 動態路由實現

動態路由是指根據請求的URL或參數動態地切換到對應租戶的數據源。具體實現如下:

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContextHolder.getTenantId();
    }
}

@Configuration
public class DataSourceConfig {
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(DynamicDataSource.class).build();
    }
}

以上是動態路由的核心代碼DynamicDataSource繼承自AbstractRoutingDataSource,通過determineCurrentLookupKey()方法動態獲得租戶ID,然後切換到對應的數據源。

2. Spring Cloud中的多租戶實現

在Spring Cloud中可以通過服務註冊與發現、配置中心、負載均衡等方式實現多租戶機制。

2.1 服務註冊與發現

使用Spring Cloud中的Eureka實現服務註冊與發現。每個租戶的服務都在註冊中心以不同的應用名稱進行註冊,客戶端可以通過服務名稱來訪問對應租戶的服務。

2.2 配置中心

使用Spring Cloud Config作為配置中心。配置文件以租戶ID進行區分,客戶端通過讀取對應租戶的配置文件來獲取配置信息。

2.3 負載均衡

使用Spring Cloud Ribbon作為負載均衡器。根據請求的URL或參數選擇對應租戶的服務實例進行請求轉發。

2.4 API

在API網關層面實現多租戶機制根據請求的URL或參數判斷所屬租戶,並轉發到對應租戶的服務實例。

四、 應用場景

1. 私有雲環境

私有雲環境指的是由企業自行搭建的雲環境,不對外提供服務,主要應用於企業內部的數據存儲、管理、共用和安全控制。相較於公有雲,私有雲的優點在於可以更好地保護企業核心數據,同時也能夠滿足企業對於數據安全性和可控性的要求。

2. 公有雲環境

公有雲環境指的是由雲服務商搭建並對外提供服務的雲環境,用戶可以根據需要購買相應的雲服務,如雲存儲、雲計算、雲資料庫等。相較於私有雲,公有雲的優點在於具有成本低廉、彈性伸縮、全球化部署等特點,能夠更好地滿足企業快速發展的需求。

3. 企業級應用

企業級應用是指面向企業客戶的應用程式,主要包括ERP、CRM、OA等一系列應用系統。這類應用的特點在於功能強大、流程複雜、數據量大,需要滿足企業的高效率、高可靠性、高安全性和易維護性等要求。在雲計算環境下,企業可以將這些應用部署在私有雲或公有雲上,減少了硬體設備的投入和維護成本,提高了管理效率。

五、實現步驟

1. 搭建Spring Boot和Spring Cloud環境

首先需要在Maven項目中引入以下依賴:

<!-- Spring Boot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Cloud -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2020.0.3</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

然後需要在application.yml中配置相應的參數,如下所示:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/appdb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

mybatis:
  type-aliases-package: com.example.demo.model
  mapper-locations: classpath:mapper/*.xml

server:
  port: 8080

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

management:
  endpoints:
    web:
      exposure:
        include: "*"

其中datasource.url為資料庫連接的URL,username和password為資料庫連接的賬號和密碼;server.port為Spring Boot應用啟動的埠;eureka.client.serviceUrl.defaultZone為Eureka服務註冊中心的URL。

Java指南:java-family.cn

2. 修改資料庫設計

接下來需要對資料庫進行相應的修改,以支持多租戶部署。具體來說,我們需要在資料庫中添加一個與租戶相關的欄位,以便在應用中區分不同的租戶。

3. 實現應用多租戶部署

接著需要在代碼中實現應用的多租戶部署功能。具體來說,我們需要為每個租戶實例化對應的Spring Bean,並根據租戶ID將請求路由到相應的Bean中去處理。

以下是一個簡單的實現示例:

@Configuration
public class MultiTenantConfig {
 
    // 提供對應租戶的數據源
    @Bean
    public DataSource dataSource(TenantRegistry tenantRegistry) {
        return new TenantAwareDataSource(tenantRegistry);
    }
 
    // 多租戶Session工廠
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }
 
    // 動態切換租戶
    @Bean
    public MultiTenantInterceptor multiTenantInterceptor(TenantResolver tenantResolver) {
        MultiTenantInterceptor interceptor = new MultiTenantInterceptor();
        interceptor.setTenantResolver(tenantResolver);
        return interceptor;
    }
 
    // 註冊攔截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(multiTenantInterceptor());
    }
 
    // 註冊租戶信息
    @Bean
    public TenantRegistry tenantRegistry() {
        return new TenantRegistryImpl();
    }
     
    // 解析租戶ID
    @Bean
    public TenantResolver tenantResolver() {
        return new HeaderTenantResolver();
    }
 
}

其中MultiTenantConfig是多租戶部署的核心配置類,它提供了對應租戶數據源、多租戶Session工廠、動態切換租戶等功能。

4. 實現租戶管理

最後需要實現一個租戶管理的功能,以便在系統中管理不同的租戶。具體來說,我們可以使用Spring Cloud的服務註冊與發現組件Eureka來註冊每個租戶的實例,併在管理界面中進行相應的操作。另外,我們還需要為每個租戶提供一個獨立的資料庫,以保證數據隔離性。

六、小結回顧

本文詳細介紹瞭如何使用Spring Boot和Spring Cloud實現一個支持多租戶部署的應用。主要包括搭建Spring Boot和Spring Cloud環境、修改資料庫設計、實現應用多租戶部署、實現租戶管理等方面。

應用場景主要包括SaaS應用、多租戶雲服務等。優劣勢主要體現在提升了應用的可擴展性和可維護性,但也增加了部署和管理的複雜度。未來的改進方向可以考慮進一步提升多租戶管理的自動化程度,減少人工干預和錯誤率。

版權聲明:本文為CSDN博主「格林希爾」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/u010349629/article/details/130737253

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # Rust語言 - 介面設計的建議之顯而易見(Obvious) - [Rust API 指南 GitHub](https://github.com/rust-lang/api-guidelines): - [Rust API 指南 中文](https://rust-chinese-translat ...
  • # 前言 此文章是Java後端接入微信登錄功能,由於項目需要,捨棄瞭解密用戶信息的`session_key`,只保留`openid`用於檢索用戶信息 後端框架:spring boot 小程式框架:uniapp # 流程概括 - 官方流程:通過自定義登錄態與openid,session_key關聯,之 ...
  • ## 1 目標 不在現有查詢代碼邏輯上做任何改動,實現dao維度的數據源切換(即表維度) ## 2 使用場景 節約bdp的集群資源。接入新的寬表時,通常uat驗證後就會停止集群釋放資源,在對應的查詢伺服器uat環境時需要查詢的是生產庫的表數據(uat庫表因為bdp實時任務停止,沒有數據落入),只進行 ...
  • ## 教程簡介 Tapestry並不是一種單純的MVC框架,它更像MVC框架和模板技術的結合,它不僅包含了前端的MVC框架,還包含了一種視圖層的模板技術,使用Tapestry完全可以與Servlet/JSP API分離,是一種非常優秀的設計。 通過使用Tapestry,開發者完全不需要使用JSP技術 ...
  • `Comparable` 介面的 `compareTo` 方法的升序或降序取決於實現該介面的類的具體實現。按照慣例,`compareTo` 方法應該返回負數、零或正數來指示當前對象是小於、等於還是大於傳入的對象。具體來說: - 如果 `this` 對象小於傳入的對象,則 `compareTo` 應該 ...
  • # 1、概述 ## 功能 Elasticsearch 是一個分散式的 RESTful 搜索和分析引擎,可用來集中存儲您的數據,以便您對形形色色、規模不一的數據進行搜索、索引和分析。 例如: - 在電商網站搜索商品 ![image](https://img2023.cnblogs.com/blog/3 ...
  • # Java 運算符的使用 # 1.算術運算符 ## 算術運算符包括: +, -, *, /, %, ++, --,其中需要註意的是%,++,--; ## % 取模運算也叫做取餘,在 Java 中取餘的規則: a % b = a - a / b * b,如果是小數的話是這樣:a % b = a- ( ...
  • 為了更好的認識函數,我們還要研究值傳遞問題,再研究這個問題之前,我們已經知道了函數之間的值傳遞,是實參變數值傳遞給形參變數,然後讓形參變數在函數內完成相應的功能。但是因為數據類型的不同,這裡的值傳遞產生的對實參變數的效果是不同的 # 1.傳遞數據本質 參數傳遞之間傳遞的肯定是數據,而這種數據本質上是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...