当前位置: 首页 > news >正文

天猫返利app的多租户架构设计:数据隔离与资源共享方案

天猫返利app的多租户架构设计:数据隔离与资源共享方案

大家好,我是阿可,微赚淘客系统及省赚客APP创始人,是个冬天不穿秋裤,天冷也要风度的程序猿!

在天猫返利APP的商业化运营中,多租户架构是支撑“品牌代理、区域合作”模式的核心技术底座——例如为不同品牌代理商提供独立的返利规则配置、用户数据视图和财务报表,同时需共享天猫商品库、支付接口等公共资源。本文将从数据隔离模型、资源共享机制、租户路由实现三个维度,结合代码案例详解天猫返利APP的多租户架构设计。
天猫返利app

一、数据隔离模型:混合隔离策略的落地

天猫返利APP的租户数据分为“核心敏感数据”(如租户用户信息、财务流水)和“非敏感共享数据”(如商品基础信息、返利规则模板),采用“schema隔离+字段隔离”的混合方案。

1.1 数据库schema隔离实现(核心敏感数据)

为每个租户创建独立的数据库schema,通过cn.juwatech.datasource.TenantDataSourceRouter实现动态数据源路由:

package cn.juwatech.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import cn.juwatech.tenant.TenantContextHolder;public class TenantDataSourceRouter extends AbstractRoutingDataSource {// 动态获取当前租户的数据源key@Overrideprotected Object determineCurrentLookupKey() {String tenantId = TenantContextHolder.getTenantId();// 默认租户(公共资源库)return tenantId == null ? "default_tenant" : tenantId;}
}

数据源配置类(初始化租户数据源):

package cn.juwatech.datasource.config;import cn.juwatech.datasource.TenantDataSourceRouter;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;@Configuration
public class TenantDataSourceConfig {// 初始化默认数据源(公共资源库)private DataSource createDefaultDataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/default_tenant?useSSL=false");dataSource.setUsername("root");dataSource.setPassword("123456");dataSource.setMaximumPoolSize(20);return dataSource;}// 初始化租户1数据源(示例:品牌A租户)private DataSource createTenant1DataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/tenant_1001?useSSL=false");dataSource.setUsername("root");dataSource.setPassword("123456");dataSource.setMaximumPoolSize(15);return dataSource;}// 注册动态数据源@Beanpublic DataSource tenantDataSource() {TenantDataSourceRouter dynamicDataSource = new TenantDataSourceRouter();Map<Object, Object> dataSourceMap = new HashMap<>();// 添加默认数据源和租户数据源dataSourceMap.put("default_tenant", createDefaultDataSource());dataSourceMap.put("tenant_1001", createTenant1DataSource());dynamicDataSource.setTargetDataSources(dataSourceMap);dynamicDataSource.setDefaultTargetDataSource(createDefaultDataSource());return dynamicDataSource;}// 配置事务管理器@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

1.2 字段隔离实现(非敏感共享数据)

对于天猫商品库等共享数据,在表中添加tenant_id字段标识租户归属,通过MyBatis拦截器自动注入租户ID:

package cn.juwatech.mybatis.interceptor;import cn.juwatech.tenant.TenantContextHolder;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;
import java.util.Properties;@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class TenantSqlInterceptor implements Interceptor {// 需要添加租户过滤的表名前缀private static final String[] TENANT_TABLE_PREFIX = {"rebate_rule_", "tenant_config_"};@Overridepublic Object intercept(Invocation invocation) throws Throwable {String tenantId = TenantContextHolder.getTenantId();if (tenantId == null) {return invocation.proceed();}// 获取StatementHandlerStatementHandler statementHandler = (StatementHandler) invocation.getTarget();MetaObject metaObject = SystemMetaObject.forObject(statementHandler);String sql = (String) metaObject.getValue("delegate.boundSql.sql");// 判断SQL是否操作租户相关表,若是则添加tenant_id条件for (String prefix : TENANT_TABLE_PREFIX) {if (sql.toLowerCase().contains("from " + prefix) || sql.toLowerCase().contains("update " + prefix)) {sql = addTenantFilter(sql, tenantId);metaObject.setValue("delegate.boundSql.sql", sql);break;}}return invocation.proceed();}// 拼接租户过滤条件private String addTenantFilter(String sql, String tenantId) {// 处理SELECT语句(添加WHERE条件)if (sql.toLowerCase().startsWith("select")) {int whereIndex = sql.toLowerCase().indexOf("where");if (whereIndex > 0) {return sql.substring(0, whereIndex + 5) + " tenant_id = '" + tenantId + "' and " + sql.substring(whereIndex + 5);} else {return sql + " where tenant_id = '" + tenantId + "'";}}// 处理UPDATE语句else if (sql.toLowerCase().startsWith("update")) {int whereIndex = sql.toLowerCase().indexOf("where");if (whereIndex > 0) {return sql.substring(0, whereIndex + 5) + " tenant_id = '" + tenantId + "' and " + sql.substring(whereIndex + 5);} else {throw new RuntimeException("Update语句必须包含WHERE条件,租户表:" + sql);}}return sql;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

二、资源共享机制:公共服务的租户适配

天猫返利APP需共享天猫商品接口、支付网关、短信服务等公共资源,设计“租户适配层”实现资源的统一调度与隔离控制。

2.1 商品接口共享与租户权限控制

通过cn.juwatech.goods.service.TenantGoodsService封装天猫商品接口,根据租户等级控制商品访问权限:

package cn.juwatech.goods.service;import cn.juwatech.goods.client.TmallGoodsClient;
import cn.juwatech.goods.dto.GoodsDTO;
import cn.juwatech.tenant.TenantContextHolder;
import cn.juwatech.tenant.dto.TenantDTO;
import cn.juwatech.tenant.service.TenantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class TenantGoodsService {@Autowiredprivate TmallGoodsClient tmallGoodsClient;@Autowiredprivate TenantService tenantService;// 租户获取天猫商品列表(带权限过滤)public List<GoodsDTO> getTenantGoodsList(Integer page, Integer size, String categoryId) {String tenantId = TenantContextHolder.getTenantId();// 获取租户信息(含商品访问权限等级)TenantDTO tenant = tenantService.getTenantById(tenantId);// 调用天猫商品接口(传入租户标识,用于天猫侧流量统计)List<GoodsDTO> allGoods = tmallGoodsClient.getGoodsList(page, size, categoryId, tenantId);// 根据租户等级过滤商品(如VIP租户可访问高返利商品)if ("VIP".equals(tenant.getLevel())) {return allGoods;} else {return allGoods.stream().filter(goods -> goods.getRebateRate() <= 0.15)  // 普通租户仅显示返利≤15%的商品.toList();}}
}

2.2 配置中心实现租户资源共享

使用Nacos作为配置中心,通过“公共配置+租户个性化配置”实现资源共享,配置类示例:

package cn.juwatech.config;import cn.juwatech.tenant.TenantContextHolder;
import com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Configuration
@NacosConfigurationProperties(dataId = "rebate_common_config", groupId = "DEFAULT_GROUP", autoRefreshed = true)
public class CommonConfig {// 公共配置:天猫接口超时时间(所有租户共享)private Integer tmallApiTimeout = 3000;// 公共配置:短信发送频率限制(所有租户共享)private Integer smsSendLimitPerHour = 100;// 租户个性化配置:返利结算周期(通过租户ID动态获取)public Integer getRebateSettlementCycle() {String tenantId = TenantContextHolder.getTenantId();// 从Nacos获取租户个性化配置(dataId格式:rebate_tenant_{tenantId}_config)String dataId = "rebate_tenant_" + tenantId + "_config";return cn.juwatech.config.NacosConfigUtil.getConfigProperty(dataId, "rebateSettlementCycle", Integer.class, 7);}// getter/setter省略
}

三、租户路由与上下文管理

3.1 租户上下文持有器(ThreadLocal实现)

package cn.juwatech.tenant;public class TenantContextHolder {private static final ThreadLocal<String> TENANT_CONTEXT = new ThreadLocal<>();// 设置当前租户IDpublic static void setTenantId(String tenantId) {TENANT_CONTEXT.set(tenantId);}// 获取当前租户IDpublic static String getTenantId() {return TENANT_CONTEXT.get();}// 清除租户上下文(防止内存泄漏)public static void clear() {TENANT_CONTEXT.remove();}
}

3.2 接口租户路由拦截器

通过Spring MVC拦截器从请求头中获取租户ID,注入上下文:

package cn.juwatech.web.interceptor;import cn.juwatech.tenant.TenantContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class TenantRouteInterceptor implements HandlerInterceptor {// 租户ID请求头名称private static final String TENANT_ID_HEADER = "X-Tenant-Id";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从请求头获取租户IDString tenantId = request.getHeader(TENANT_ID_HEADER);if (tenantId == null || tenantId.isEmpty()) {response.setStatus(400);response.getWriter().write("Tenant ID is required (header: X-Tenant-Id)");return false;}// 验证租户有效性(从数据库或缓存查询)boolean tenantValid = cn.juwatech.tenant.service.TenantService.checkTenantValid(tenantId);if (!tenantValid) {response.setStatus(403);response.getWriter().write("Invalid Tenant ID");return false;}// 设置租户上下文TenantContextHolder.setTenantId(tenantId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清除租户上下文TenantContextHolder.clear();}
}

拦截器注册:

package cn.juwatech.web.config;import cn.juwatech.web.interceptor.TenantRouteInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 所有接口(除公共接口)都需要租户路由registry.addInterceptor(new TenantRouteInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/public/**");}
}

基于上述架构,天猫返利APP已支撑200+租户同时运营,租户数据隔离率100%,公共资源复用率提升70%,且新增租户时仅需创建schema并配置Nacos参数,实现“分钟级”租户开通。后续可引入租户资源配额管理,进一步优化多租户场景下的资源分配效率。

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


文章转载自:

http://VdqlxuRQ.ctpfq.cn
http://Ng7rNs8q.ctpfq.cn
http://oyN3MKEl.ctpfq.cn
http://ZpAqPM4m.ctpfq.cn
http://B5m8TLED.ctpfq.cn
http://0hmq0qDb.ctpfq.cn
http://7L5yAH5N.ctpfq.cn
http://DmAanOiU.ctpfq.cn
http://s6W7FIKs.ctpfq.cn
http://G4JtMMls.ctpfq.cn
http://PALENgY4.ctpfq.cn
http://uvl3s8Bf.ctpfq.cn
http://9Xob8VGj.ctpfq.cn
http://sOERmf9R.ctpfq.cn
http://lbPwAbtZ.ctpfq.cn
http://EUu7RYvd.ctpfq.cn
http://hKS6Hx8R.ctpfq.cn
http://ive2KuzN.ctpfq.cn
http://oy6gyL0R.ctpfq.cn
http://xktzCdaw.ctpfq.cn
http://1g6TjDDW.ctpfq.cn
http://Zg0Yr8EL.ctpfq.cn
http://KYtPOCSt.ctpfq.cn
http://nv52e4W3.ctpfq.cn
http://Vfh3dMRX.ctpfq.cn
http://oDEJi9gP.ctpfq.cn
http://4ddaLlnb.ctpfq.cn
http://gQvzQR2V.ctpfq.cn
http://QoZkvxdD.ctpfq.cn
http://Aj7zGB6m.ctpfq.cn
http://www.dtcms.com/a/383030.html

相关文章:

  • 数据库造神计划第六天---增删改查(CRUD)(2)
  • AI 赋能内容创作:从文案生成到视频剪辑,创作者的工具革命已至
  • 如何使用“线程级微内核架构”打造应用
  • [硬件电路-219]:自由电子与空穴导电的比较(异同)
  • 系统编程完结整理
  • 阿里云视觉多模态理解大模型开发训练部署
  • leetcode_21 合并两个有序链表
  • Node.js实时截屏实现方案
  • 01数据结构-01背包问题
  • 20250914-01: Langchain概念:流式传输(Streaming)
  • 初步认识 Spring Boot 自动装配
  • 《突破Unity+腾讯云联机瓶颈:多人游戏同步延迟与数据安全的双维度优化》
  • 计算机算术9-浮点乘法
  • 第24课:项目实战与总结
  • 【深度学习|学习笔记】从背景→公式→性质→梯度→何时用哪一个→数值稳定性与常见坑方面描述sigmoid和softmax函数!(一)
  • C++宽度优先搜索算法:队列与优先级队列
  • 同步降压转换器原理
  • 人工智能训练师三级备考笔记
  • <基于深度学习的条纹图分析及其不确定性估计>-论文总结
  • 【愚公系列】《人工智能70年》020-语音识别的历史性突破(深度学习带来历史性突破)
  • 网络操作系统与分布式操作系统的区别
  • Spring Cloud Alibaba 实战:从 0 到 1 构建可监控的微服务体系
  • 分布式推理与量化部署
  • Kafka 实现从网络层到日志与位点的“全景拆解”
  • Python入门教程之赋值运算符
  • 机器学习系统设计:从需求分析到模型部署的完整项目流程
  • SpringMVC架构解析:从入门到精通(1)
  • Why Language Models Hallucinate 论文翻译
  • 从 WPF 到 Avalonia 的迁移系列实战篇5:Trigger、MultiTrigger、DataTrigger 的迁移
  • easyExcel动态应用案例