欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot?多租戶SaaS搭建方案

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

正文

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

技術(shù)框架

springboot版本為2.3.4.RELEASE

持久層采用JPA

租戶Model設(shè)計(jì)

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

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

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

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

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

Hibernate filter

如果讓我們?cè)跇I(yè)務(wù)中都去加上租戶sql過濾代碼,那工作量不僅大,而且出錯(cuò)的概率也很大。理想是過濾sql拼接統(tǒng)一放在一起處理,在租戶業(yè)務(wù)接口開啟sql過濾。因?yàn)镴PA是有hibernate實(shí)現(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ù)的過濾器,對(duì)于某個(gè)特定的 Hibernate session 您可以選擇是否啟用(或禁用)某個(gè)過濾器。

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

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

    boolean readOnly() default true;

}

可以看出這個(gè)接口是放在方法上,對(duì)應(yīng)的就是Controller層。@Transactional增加事務(wù)注解的意義是因?yàn)榧せ頷ibernate filter必須要開啟事務(wù),這里默認(rèn)是只讀事務(wù)。 最后定義一個(gè)切面來激活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);
        }
    }
}

這里切面的對(duì)象就是剛才自定義的@TenantFilter注解,在方法執(zhí)行前拿到當(dāng)前租戶id,開啟filter,這樣租戶數(shù)據(jù)隔離就大功告成了,只需要在租戶業(yè)務(wù)接口上增加@TenantFilter注解即可, 開發(fā)只用關(guān)心業(yè)務(wù)代碼。上圖中的TenantContext是當(dāng)前線程租戶context,通過和前端約定好,接口請(qǐng)求頭中增加租戶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ù)肯定會(huì)達(dá)到瓶頸,這里只采用分庫的手段。利用多數(shù)據(jù)源,將租戶和數(shù)據(jù)源進(jìn)行多對(duì)一的映射。

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();
    }
}

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

結(jié)尾

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

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

相關(guān)文章

  • SpringBoot使用Redis方式

    SpringBoot使用Redis方式

    文章介紹了SpringDataRedis操作本地Redis數(shù)據(jù)庫的方法,包括Redis的基本概念、數(shù)據(jù)類型、命令以及在Java中的使用
    2024-11-11
  • Maven Optional依賴屬性的含義及妙用

    Maven Optional依賴屬性的含義及妙用

    這篇文章主要為大家介紹了Maven Optional依賴屬性的含義及妙用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 一文詳細(xì)解析Java?8?Stream?API中的flatMap方法

    一文詳細(xì)解析Java?8?Stream?API中的flatMap方法

    這篇文章主要介紹了Java?8?Stream?API中的flatMap方法的相關(guān)資料,flatMap方法是Java?StreamAPI中的重要中間操作,用于將流中的每個(gè)元素轉(zhuǎn)換為一個(gè)新的流,并將多個(gè)流合并為一個(gè)單一的流,常用于處理嵌套集合和一對(duì)多映射,需要的朋友可以參考下
    2024-12-12
  • 16個(gè)SpringBoot擴(kuò)展接口的總結(jié)和實(shí)例

    16個(gè)SpringBoot擴(kuò)展接口的總結(jié)和實(shí)例

    Spring Boot是一個(gè)開源的Java框架,它簡化了基于Spring的應(yīng)用程序的開發(fā)和部署,它提供了許多強(qiáng)大的特性和擴(kuò)展接口,本文給大家介紹了16個(gè)常用的Spring Boot擴(kuò)展接口,需要的朋友可以參考下
    2023-09-09
  • Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier使用探索

    Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier使用探索

    這篇文章主要介紹了Java多線程之循環(huán)柵欄技術(shù)CyclicBarrier,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2024-01-01
  • 老生常談Java String字符串(必看篇)

    老生常談Java String字符串(必看篇)

    下面小編就為大家?guī)硪黄仙U凧ava String字符串(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • springboot2.5.6集成RabbitMq實(shí)現(xiàn)Topic主題模式(推薦)

    springboot2.5.6集成RabbitMq實(shí)現(xiàn)Topic主題模式(推薦)

    這篇文章主要介紹了springboot2.5.6集成RabbitMq實(shí)現(xiàn)Topic主題模式(推薦),pom.xml引入依賴和常量類創(chuàng)建,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • Java中Klass模型與類加載的詳細(xì)機(jī)制

    Java中Klass模型與類加載的詳細(xì)機(jī)制

    這篇文章主要介紹了Java中Klass模型與類加載的詳細(xì)機(jī)制,java語言是在jvm中運(yùn)行而jvm是不認(rèn)識(shí)java代碼的我們使用javac編譯的class文件jvm是不認(rèn)識(shí)的 所以有一個(gè)類加載的動(dòng)作 這個(gè)動(dòng)作就是把class字節(jié)碼拼裝成一個(gè)klass類型,需要的朋友可以參考下
    2023-08-08
  • SpringBoot基本web開發(fā)demo過程解析

    SpringBoot基本web開發(fā)demo過程解析

    這篇文章主要介紹了SpringBoot基本web開發(fā)demo過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • spring boot中的靜態(tài)資源加載處理方式

    spring boot中的靜態(tài)資源加載處理方式

    這篇文章主要介紹了spring boot中的靜態(tài)資源加載處理方式,需要的朋友可以參考下
    2017-04-04

最新評(píng)論