最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot?多租戶SaaS搭建方案

 更新時間:2022年06月15日 14:43:21   作者:soldier_n  
這篇文章主要介紹了Springboot?多租戶SaaS方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

正文

相信大家對SaaS架構(gòu)都有所了解,這里也不過多介紹,讓我們直奔主題。

技術(shù)框架

springboot版本為2.3.4.RELEASE

持久層采用JPA

租戶Model設(shè)計

因為saas應(yīng)用所有租戶都使用同個服務(wù)和數(shù)據(jù)庫,為隔離好租戶數(shù)據(jù),這里創(chuàng)建一個BaseSaasEntity

public abstract class BaseSaasEntity {
    @JsonIgnore
    @Column(nullable = false, updatable = false)
    protected Long tenantId;
    }

里面只有一個字段tenantId,對應(yīng)的就是租戶Id,所有租戶業(yè)務(wù)entity都繼承這個父類。最后通過tenantId來區(qū)分?jǐn)?shù)據(jù)是哪個租戶。

sql租戶數(shù)據(jù)過濾

按往常,表建好就該接著對應(yīng)的模塊的CURD。但saas應(yīng)用最基本的要求就是租戶數(shù)據(jù)隔離,就是公司B的人不能看到公司A的數(shù)據(jù),怎么過濾呢,這里上面我們建立的BaseSaasEntity就起作用了,通過區(qū)分當(dāng)前請求是來自那個公司后,在所有tenant業(yè)務(wù)sql中加上where tenant=?就實現(xiàn)了租戶數(shù)據(jù)過濾。

Hibernate filter

如果讓我們在業(yè)務(wù)中都去加上租戶sql過濾代碼,那工作量不僅大,而且出錯的概率也很大。理想是過濾sql拼接統(tǒng)一放在一起處理,在租戶業(yè)務(wù)接口開啟sql過濾。因為JPA是有hibernate實現(xiàn)的,這里我們可以利用hibernate的一些功能

@MappedSuperclass
@Data
@FilterDef(name = "tenantFilter", parameters = {@ParamDef(name = "tenantId", type = "long")})
@Filter(condition = "tenant_id=:tenantId", name = "tenantFilter")
public abstract class BaseSaasEntity {
    @JsonIgnore
    @Column(nullable = false, updatable = false)
    protected Long tenantId;


    @PrePersist
    public void onPrePersist() {
        if (getTenantId() != null) {
            return;
        }
        Long tenantId = TenantContext.getTenantId();
        Check.notNull(tenantId, "租戶不存在");
        setTenantId(tenantId);
    }
}

Hibernate3 提供了一種創(chuàng)新的方式來處理具有“顯性(visibility)”規(guī)則的數(shù)據(jù),那就是使用Hibernate 過濾器。Hibernate 過濾器是全局有效的、具有名字、可以帶參數(shù)的過濾器,對于某個特定的 Hibernate session 您可以選擇是否啟用(或禁用)某個過濾器。

這里我們通過@FilterDef和@Filter預(yù)先定義了一個sql過濾條件。然后通過一個@TenantFilter注解來標(biāo)識接口需要進(jìn)行數(shù)據(jù)過濾

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional
public @interface TenantFilter {

    boolean readOnly() default true;

}

可以看出這個接口是放在方法上,對應(yīng)的就是Controller層。@Transactional增加事務(wù)注解的意義是因為激活hibernate filter必須要開啟事務(wù),這里默認(rèn)是只讀事務(wù)。 最后定義一個切面來激活filter

@Aspect
@Slf4j
@RequiredArgsConstructor
public class TenantSQLAspect {
    private static final String FILTER_NAME = "tenantFilter";
    private final EntityManager entityManager;
    @SneakyThrows
    @Around("@annotation(com.lvjusoft.njcommon.annotation.TenantFilter)")
    public Object aspect(ProceedingJoinPoint joinPoint) {
        Session session = entityManager.unwrap(Session.class);
        try {
            Long tenantId = TenantContext.getTenantId();
            Check.notNull(tenantId, "租戶不存在");
            session.enableFilter(FILTER_NAME).setParameter("tenantId", tenantId);
            return joinPoint.proceed();
        } finally {
            session.disableFilter(FILTER_NAME);
        }
    }
}

這里切面的對象就是剛才自定義的@TenantFilter注解,在方法執(zhí)行前拿到當(dāng)前租戶id,開啟filter,這樣租戶數(shù)據(jù)隔離就大功告成了,只需要在租戶業(yè)務(wù)接口上增加@TenantFilter注解即可, 開發(fā)只用關(guān)心業(yè)務(wù)代碼。上圖中的TenantContext是當(dāng)前線程租戶context,通過和前端約定好,接口請求頭中增加租戶id,服務(wù)端利用攔截器把獲取到的租戶id緩存在ThreadLocal中

public class IdentityInterceptor extends HandlerInterceptorAdapter {
    public IdentityInterceptor() {
        log.info("IdentityInterceptor init");
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader(AuthConstant.USER_TOKEN_HEADER_NAME);
        UserContext.setToken(token);
        String tenantId = request.getHeader(AuthConstant.TENANT_TOKEN_HEADER_NAME);
        TenantContext.setTenantUUId(tenantId);
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        UserContext.clear();
        TenantContext.clear();
    }
}

分庫

隨著租戶數(shù)量的增加,mysql單庫單表的數(shù)據(jù)肯定會達(dá)到瓶頸,這里只采用分庫的手段。利用多數(shù)據(jù)源,將租戶和數(shù)據(jù)源進(jìn)行多對一的映射。

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    private Map<Object, Object> targetDataSources;
    public DynamicRoutingDataSource() {
        targetDataSources =new HashMap<>();
        DruidDataSource druidDataSource1 = new DruidDataSource();
        druidDataSource1.setUsername("username");
        druidDataSource1.setPassword("password");
        druidDataSource1.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false&useUnicode=true&characterEncoding=utf-8");
        targetDataSources.put("db1",druidDataSource1);
        
        DruidDataSource druidDataSource2 = new DruidDataSource();
        druidDataSource2.setUsername("username");
        druidDataSource2.setPassword("password");
        druidDataSource2.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false&useUnicode=true&characterEncoding=utf-8");
        targetDataSources.put("db2",druidDataSource1);
        
        this.targetDataSources = targetDataSources;
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    public void addDataSource(String key, DataSource dataSource) {
        if (targetDataSources.containsKey(key)) {
            throw new IllegalArgumentException("dataSource key exist");
        }
        targetDataSources.put(key, dataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContext.getSource();
    }
}

通過實現(xiàn)AbstractRoutingDataSource來聲明一個動態(tài)路由數(shù)據(jù)源,在框架使用datesource前,spring會調(diào)用determineCurrentLookupKey()方法來確定使用哪個數(shù)據(jù)源。這里的DataSourceContext和上面的TenantContext類似,在攔截器中獲取到tenantInfo后,找到當(dāng)前租戶對應(yīng)的數(shù)據(jù)源key并設(shè)置在ThreadLocal中。

結(jié)尾

到這里一個多租戶的基礎(chǔ)應(yīng)用就搭建好了。

到此這篇關(guān)于Springboot 多租戶SaaS方案的文章就介紹到這了,更多相關(guān)Springboot 多租戶SaaS內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring導(dǎo)入properties配置文件代碼示例

    Spring導(dǎo)入properties配置文件代碼示例

    這篇文章主要介紹了Spring導(dǎo)入properties配置文件代碼示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐

    Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐

    本文主要介紹了Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • SpringBoot實現(xiàn)國密SM4加密解密的使用示例

    SpringBoot實現(xiàn)國密SM4加密解密的使用示例

    在商用密碼體系中,SM4主要用于數(shù)據(jù)加密,本文就來介紹一下SpringBoot實現(xiàn)國密SM4加密解密的使用示例,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 基于Mock測試Spring MVC接口過程解析

    基于Mock測試Spring MVC接口過程解析

    這篇文章主要介紹了基于Mock測試Spring MVC接口過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • 淺析Spring?Cloud?Gateway中的令牌桶限流算法

    淺析Spring?Cloud?Gateway中的令牌桶限流算法

    這篇文章主要為大家淺析了Spring?Cloud?Gateway中的令牌桶限流算法原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • myeclipse安裝Spring Tool Suite(STS)插件的方法步驟

    myeclipse安裝Spring Tool Suite(STS)插件的方法步驟

    這篇文章主要介紹了myeclipse安裝Spring Tool Suite(STS)插件的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Maven 生成打包可執(zhí)行jar包的方法步驟

    Maven 生成打包可執(zhí)行jar包的方法步驟

    這篇文章主要介紹了Maven 生成打包可執(zhí)行jar包的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 淺談SpringCloud的微服務(wù)架構(gòu)組件

    淺談SpringCloud的微服務(wù)架構(gòu)組件

    這篇文章主要介紹了淺談SpringCloud的微服務(wù)架構(gòu)組件,Spring Cloud根據(jù)分布式服務(wù)協(xié)調(diào)治理的需求成立了許多子項目,每個項目通過特定的組件去實現(xiàn),需要的朋友可以參考下
    2023-04-04
  • Java單例模式的8種寫法(推薦)

    Java單例模式的8種寫法(推薦)

    這篇文章主要介紹了Java單例模式的8種寫法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Feign的原理及概念詳解

    Feign的原理及概念詳解

    文章介紹了Feign,一個聲明式、模板化的HTTP客戶端,以及其在Spring Cloud中的增強(qiáng),Feign通過代理類簡化HTTP請求的調(diào)用,并提供了詳細(xì)的調(diào)用流程、優(yōu)化配置和架構(gòu)說明,幫助開發(fā)者更方便地進(jìn)行服務(wù)調(diào)用和管理
    2025-11-11

最新評論

随州市| 临西县| 潼南县| 高密市| 康马县| 芒康县| 内丘县| 略阳县| 西和县| 富源县| 翁源县| 延津县| 莱州市| 南京市| 孝昌县| 长垣县| 牙克石市| 武义县| 岳西县| 丰县| 香港 | 鄂伦春自治旗| 霸州市| 岐山县| 富阳市| 甘南县| 昭通市| 南平市| 靖远县| 武穴市| 柞水县| 定州市| 将乐县| 云龙县| 宁化县| 肇庆市| 雷波县| 迭部县| 屯门区| 武冈市| 尼玛县|