首页 > 其他分享 >淘客APP的多租户架构设计与实现

淘客APP的多租户架构设计与实现

时间:2024-09-29 22:19:10浏览次数:3  
标签:架构设计 cn 淘客 APP tenantId 租户 import public juwatech

淘客APP的多租户架构设计与实现

大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们要讨论的是淘客APP中的多租户架构设计与实现。在淘客系统中,多租户架构是为了支持多个商户(租户)使用同一个应用实例,但彼此数据隔离,同时能够共享应用资源。接下来,我们将介绍如何使用 Java 实现多租户架构,尤其是通过不同的数据隔离和策略设计来实现高效、安全的系统。

一、多租户架构的基本概念

多租户架构是一种软件架构模式,允许多个租户共享同一个软件实例,但每个租户的数据必须是隔离的。对于淘客系统来说,多租户架构的主要设计目标是:

  1. 数据隔离: 确保每个租户的数据互不影响。
  2. 资源共享: 不同租户可以共享计算资源,如服务器、内存等,降低运营成本。
  3. 灵活性: 支持租户的动态扩展与管理,方便维护和更新。

常见的多租户架构实现方式包括:

  1. 单库单表:每个租户拥有独立的数据库。
  2. 单库多表:所有租户共享一个数据库,但不同租户的数据存储在不同的表中。
  3. 单库单表多租户:所有租户共享一个数据库和表,通过租户ID进行数据隔离。

在实际开发中,可以根据业务规模和性能需求选择合适的方式。下面我们通过代码展示如何在 Java 中实现多租户支持。

二、Java 多租户架构的实现

在 Java 项目中,我们可以通过 Spring 框架来实现多租户架构。首先,定义一个租户上下文(Tenant Context),用于在应用程序中动态切换租户。

package cn.juwatech.multitenancy;

public class TenantContext {
    private static ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setCurrentTenant(String tenantId) {
        currentTenant.set(tenantId);
    }

    public static String getCurrentTenant() {
        return currentTenant.get();
    }

    public static void clear() {
        currentTenant.remove();
    }
}

TenantContext 使用 ThreadLocal 来存储当前租户的ID,确保每个线程在处理请求时能够正确识别当前的租户。

接下来,我们需要配置一个拦截器,在每次请求时根据请求头或参数设置当前租户信息:

package cn.juwatech.multitenancy;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class TenantFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String tenantId = request.getHeader("X-Tenant-ID");

        if (tenantId != null) {
            TenantContext.setCurrentTenant(tenantId);
        }

        try {
            filterChain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

在这个过滤器中,我们从请求头中提取 X-Tenant-ID,并将其设置到 TenantContext 中。在每个请求处理完毕后,我们会清理 ThreadLocal 中的租户信息,避免线程复用导致租户数据混乱。

三、数据源的动态切换

为了实现多租户架构的数据库隔离,我们可以根据 TenantContext 中的租户ID来动态切换数据源。在 Spring 中,这可以通过配置动态数据源来实现。

首先,定义一个数据源路由器:

package cn.juwatech.multitenancy;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class TenantRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getCurrentTenant();
    }
}

TenantRoutingDataSource 通过重写 determineCurrentLookupKey 方法,根据当前租户ID选择数据源。

然后,在 Spring 配置中,将不同租户的数据源注册到路由器中:

package cn.juwatech.config;

import cn.juwatech.multitenancy.TenantRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();

        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("tenant1", createDataSource("jdbc:mysql://localhost:3306/tenant1_db"));
        dataSourceMap.put("tenant2", createDataSource("jdbc:mysql://localhost:3306/tenant2_db"));

        routingDataSource.setTargetDataSources(dataSourceMap);
        routingDataSource.setDefaultTargetDataSource(createDataSource("jdbc:mysql://localhost:3306/default_db"));

        return routingDataSource;
    }

    private DataSource createDataSource(String url) {
        return DataSourceBuilder.create()
                .url(url)
                .username("root")
                .password("password")
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .build();
    }
}

在这个配置类中,我们定义了 TenantRoutingDataSource,并为每个租户配置不同的数据库连接。通过这种方式,每当系统接收到请求时,会根据当前租户的ID动态选择相应的数据源进行操作。

四、基于租户ID的数据隔离

在有些场景下,多个租户可以共享一个数据库,但需要通过 tenant_id 进行数据隔离。在这种情况下,可以通过在查询中动态添加租户ID过滤条件来实现数据隔离。

首先,定义一个基础的实体类,并为其添加租户ID字段:

package cn.juwatech.model;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class BaseTenantEntity {

    @Column(name = "tenant_id")
    private String tenantId;

    public String getTenantId() {
        return tenantId;
    }

    public void setTenantId(String tenantId) {
        this.tenantId = tenantId;
    }
}

然后,在执行数据库操作时,可以通过拦截器或查询构建器,自动将当前租户ID添加到查询条件中:

package cn.juwatech.repository;

import cn.juwatech.model.BaseTenantEntity;
import cn.juwatech.multitenancy.TenantContext;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;

@Repository
public class TenantAwareRepository {

    @Autowired
    private EntityManager entityManager;

    public List<BaseTenantEntity> findAllByTenant(String jpql) {
        Query query = entityManager.createQuery(jpql + " WHERE tenant_id = :tenantId");
        query.setParameter("tenantId", TenantContext.getCurrentTenant());
        return query.getResultList();
    }
}

TenantAwareRepository 中,每次查询时,我们都会自动为JPQL添加 tenant_id 的过滤条件,确保不同租户的数据互不干扰。

五、租户管理和动态扩展

为了支持动态扩展租户,系统需要提供租户管理功能。可以通过一个专门的租户管理服务来注册、更新和删除租户信息,并将相应的数据库连接信息同步到系统的路由器中。

package cn.juwatech.service;

import cn.juwatech.multitenancy.TenantRoutingDataSource;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class TenantManagementService {

    private final TenantRoutingDataSource routingDataSource;

    public TenantManagementService(TenantRoutingDataSource routingDataSource) {
        this.routingDataSource = routingDataSource;
    }

    public void addTenant(String tenantId, String dbUrl) {
        DataSource newTenantDataSource = DataSourceBuilder.create()
                .url(dbUrl)
                .username("root")
                .password("password")
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .build();

        Map<Object, Object> dataSourceMap = routingDataSource.getTargetDataSources();
        dataSourceMap.put(tenantId, newTenantDataSource);

        routingDataSource.setTargetDataSources(dataSourceMap);
        routingDataSource.afterPropertiesSet();  // 刷新数据源
    }
}

通过 TenantManagementService,我们可以动态添加新的租户,并将其数据库信息注册到路由器中。

六、总结

本文讨论了如何在淘客APP中设计和实现多租户架构,涵盖了数据源动态切换、数据隔离和租户管理的相关技术细节。使用 Java 结合 Spring 框架,我们能够轻

松地实现高效的多租户系统。

本文著作权归聚娃科技微赚淘客系统开发者团队,转载请注明出处!

标签:架构设计,cn,淘客,APP,tenantId,租户,import,public,juwatech
From: https://blog.csdn.net/weixin_44626980/article/details/142338352

相关文章