主頁 > 後端開發 > Spring Boot 優雅實作多租戶架構,so easy~!

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

2023-06-20 07:43:30 後端開發

一、概述

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開發手冊(嵩山版)》最新發布,速速下載!

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555545.html

標籤:其他

上一篇:Scala高階語法

下一篇:返回列表

標籤雲
其他(161266) Python(38242) JavaScript(25505) Java(18249) C(15237) 區塊鏈(8271) C#(7972) AI(7469) 爪哇(7425) MySQL(7258) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4603) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2436) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) HtmlCss(1968) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Spring Boot 優雅實作多租戶架構,so easy~!

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

    uj5u.com 2023-06-20 07:43:30 more
  • Scala高階語法

    # 高階函式 ## 函式可以作為引數進行傳遞和回傳值進行回傳 ```Scala //傳一個a乘b 就回傳一個函式,邏輯是實作兩數相乘 //傳一個a*b 回傳一個函式,邏輯是實作兩數相乘 //傳一個axb 回傳一個函式,邏輯是實作兩數相乘 def funTest6(str:String,fun:(St ......

    uj5u.com 2023-06-20 07:43:05 more
  • Python 標準類別庫-并發執行之multiprocessing-基于行程的并行

    ### 實踐環境 Python3.6 ### 介紹 `multiprocessing`是一個支持使用類似于執行緒模塊的API派生行程的包。該包同時提供本地和遠程并發,通過使用子行程而不是執行緒,有效地避開了全域解釋器鎖。因此,`multiprocessing`模塊允許程式員充分利用給定機器上的多個處理器 ......

    uj5u.com 2023-06-20 07:40:46 more
  • 【python基礎】函式-值傳遞

    為了更好的認識函式,我們還要研究值傳遞問題,再研究這個問題之前,我們已經知道了函式之間的值傳遞,是實參變數值傳遞給形參變數,然后讓形參變數在函式內完成相應的功能。但是因為資料型別的不同,這里的值傳遞產生的對實參變數的效果是不同的 # 1.傳遞資料本質 引數傳遞之間傳遞的肯定是資料,而這種資料本質上是 ......

    uj5u.com 2023-06-20 07:40:30 more
  • JAVA SE基礎《一》----JAVA入門

    初識Java 1.Java背景知識 java是美國sun公司(Stanford University Network)在1995年推出的一門計算機高級編程語言。 Java早期稱為Oak(橡樹),后期改名為Java。 Java之父:詹姆斯·高斯林(James Gosling)。 2009年sun公司被 ......

    uj5u.com 2023-06-20 07:39:41 more
  • 尚醫通-day14【創建訂單】(內附原始碼)

    typora-copy-images-to: upload # 頁面預覽 ## 訂單詳情 ![image-20230227071834134](https://s2.loli.net/2023/06/19/8rXsPWOn3MdlRNx.png) ![image-20230227071900964] ......

    uj5u.com 2023-06-20 07:39:15 more
  • InnoDB 緩沖池

    緩沖池是主存盤器中的一個區域,在訪問 table 和索引資料時 InnoDB 會對其進行快取。緩沖池允許直接從記憶體中訪問頻繁使用的資料,從而加快處理速度。在專用服務器上,通常將高達 80% 的物理記憶體分配給緩沖池。 ......

    uj5u.com 2023-06-20 07:33:00 more
  • 深入理解Go語言介面

    # 1. 引言 介面是一種定義了軟體組件之間互動規范的重要概念,其促進了代碼的解耦、模塊化和可擴展性,提供了多型性和抽象的能力,簡化了依賴管理和替換,方便進行單元測驗和集成測驗。這些特性使得介面成為構建可靠、可維護和可擴展的軟體系統的關鍵工具之一。 在現代編程語言中,介面是不可或缺的一個重要特性。本 ......

    uj5u.com 2023-06-19 08:33:43 more
  • 【QCustomPlot】使用方法(動態庫方式)

    使用 QCustomPlot 繪圖庫輔助開發時整理的學習筆記。本篇介紹 QCustomPlot 的一種使用方法,通過動態庫的方式進行使用,示例中使用的 QCustomPlot 版本為 Version 2.1.1。 ......

    uj5u.com 2023-06-19 08:33:38 more
  • 深入理解Go語言介面

    # 1. 引言 介面是一種定義了軟體組件之間互動規范的重要概念,其促進了代碼的解耦、模塊化和可擴展性,提供了多型性和抽象的能力,簡化了依賴管理和替換,方便進行單元測驗和集成測驗。這些特性使得介面成為構建可靠、可維護和可擴展的軟體系統的關鍵工具之一。 在現代編程語言中,介面是不可或缺的一個重要特性。本 ......

    uj5u.com 2023-06-19 08:32:27 more