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

Spring Boot 多租户架构实现:基于上下文自动传递的独立资源隔离方案

一、核心设计思想
通过线程上下文自动传递租户ID,结合动态数据源路由和中间件连接工厂,实现MySQL、Redis、RocketMQ的完全自动化资源隔离。关键设计如下:

Client Gateway TenantContext MySQL Redis RocketMQ 携带X-Tenant-ID请求 解析并设置租户ID 自动路由到tenantA数据源 自动选择tenantA连接池 自动绑定tenantA Topic 释放连接 关闭连接 移除监听 Client Gateway TenantContext MySQL Redis RocketMQ

二、关键实现方案

  1. 租户上下文管理(核心)
public class TenantContext {// 使用TransmittableThreadLocal支持异步传递private static final TransmittableThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>();public static void set(String tenantId) {CONTEXT.set(tenantId);DynamicDataSourceContextHolder.push(tenantId); // 同步MySQL上下文}public static String get() {return CONTEXT.get();}public static void clear() {CONTEXT.remove();DynamicDataSourceContextHolder.poll(); // 清理MySQL上下文}
}
  1. MySQL动态数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.tenantA")public DataSource tenantADatasource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.tenantB")public DataSource tenantBDatasource() {return DataSourceBuilder.create().build();}@Beanpublic DataSource routingDataSource(@Qualifier("masterDataSource") DataSource master,@Qualifier("tenantADatasource") DataSource tenantA,@Qualifier("tenantBDatasource") DataSource tenantB) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("tenantA", tenantA);targetDataSources.put("tenantB", tenantB);DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();routingDataSource.setDefaultTargetDataSource(master);routingDataSource.setTargetDataSources(targetDataSources);return routingDataSource;}
}public class DynamicRoutingDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceKey();}
}
  1. Redis连接工厂隔离
@Configuration
public class RedisConfig {@Beanpublic RedisConnectionFactory tenantARedisFactory() {RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();config.setDatabase(1); // 租户A专用DBreturn new LettuceConnectionFactory(config);}@Beanpublic RedisConnectionFactory tenantBRedisFactory() {RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();config.setDatabase(2); // 租户B专用DBreturn new LettuceConnectionFactory(config);}@Beanpublic RedisTemplate<String, Object> redisTemplate(@Qualifier("tenantARedisFactory") RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);template.setKeySerializer(new TenantAwareStringRedisSerializer());return template;}
}public class TenantAwareStringRedisSerializer implements StringRedisSerializer {@Overridepublic String serialize(String string) {String tenantId = TenantContext.get();return (tenantId == null) ? string : tenantId + ":" + string;}
}
  1. RocketMQ生产者/消费者自动绑定
@Configuration
public class RocketMQConfig {@Beanpublic DefaultMQProducer tenantProducer() throws MQClientException {DefaultMQProducer producer = new DefaultMQProducer("tenant_producer");producer.setNamesrvAddr("127.0.0.1:9876");producer.start();return producer;}@Beanpublic RocketMQListener<String> tenantListener() {return new TenantAwareListener();}
}public class TenantAwareListener implements RocketMQListener<String> {@Overridepublic void onMessage(String message) {String tenantId = TenantContext.get();// 自动路由到租户专属TopicString targetTopic = "topic_" + tenantId;// 处理消息...}
}

三、自动化上下文传播

  1. 请求拦截器
@Component
public class TenantInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String tenantId = request.getHeader("X-Tenant-ID");TenantContext.set(tenantId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) {TenantContext.clear();}
}
  1. 异步任务支持
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setTaskDecorator(new ContextCopyingDecorator());executor.initialize();return executor;}private static class ContextCopyingDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {String tenantId = TenantContext.get();return () -> {try {TenantContext.set(tenantId);runnable.run();} finally {TenantContext.clear();}};}}
}

四、完整工作流程

  1. 请求入口
    网关拦截请求 → 解析X-Tenant-ID → 设置到TenantContext

  2. 数据访问层

    @Service
    public class OrderService {@Autowiredprivate OrderMapper orderMapper;public Order getOrder(Long id) {// 自动路由到当前租户的数据源return orderMapper.selectById(id);}
    }
    
  3. Redis操作

    @Service
    public class CacheService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public void setValue(String key, Object value) {// 自动添加租户前缀redisTemplate.opsForValue().set(key, value);}
    }
    
  4. 消息发送

    @Service
    public class MessageService {@Autowiredprivate DefaultMQProducer producer;public void sendOrderCreated(Order order) {Message msg = new Message("order_created", order.toJson());producer.send(msg); // 自动绑定租户Topic}
    }
    

五、技术优势对比

方案上下文传递方式中间件隔离级别代码侵入性线程安全
手动切换显式代码调用应用层需要处理
本方案线程上下文自动传播物理隔离原生支持
动态注解AOP切面逻辑隔离需要配置

六、生产环境优化点

  1. 连接池监控

    spring:datasource:tenantA:hikari:maximum-pool-size: 20leak-detection-threshold: 30000
    
  2. RocketMQ消费者隔离

    @RocketMQMessageListener(topic = "tenant_#{tenantId}_topic",consumerGroup = "tenant_#{tenantId}_group"
    )
    public class TenantConsumer implements RocketMQListener<String> {// 自动注入当前租户的Consumer
    }
    
  3. Redis集群支持

    @Bean
    public RedisClusterConfiguration tenantARedisCluster() {RedisClusterConfiguration config = new RedisClusterConfiguration();config.setClusterNodes(Arrays.asList("127.0.0.1:7001", "127.0.0.1:7002"));config.setPassword("tenantA@2024");return config;
    }
    

通过本方案,可实现完全自动化的多租户资源隔离,核心优势在于:

  1. 零代码侵入:通过上下文自动传播实现资源隔离
  2. 物理级隔离:每个租户拥有独立数据库/消息队列/缓存集群
  3. 动态扩展:新增租户只需添加配置,无需修改代码
http://www.lryc.cn/news/2383859.html

相关文章:

  • 在 JavaScript 中正确使用 Elasticsearch,第二部分
  • 更新nvidia-container-toolkit 1.17.7-1后,运行--gpus all 卡死问题
  • 【Nginx学习笔记】:Fastapi服务部署单机Nginx配置说明
  • 相机标定与图像处理涉及的核心坐标系
  • ​在 ASP.NET 中,HTTP 处理程序(HttpHandler)是处理 HTTP 请求的核心组件​
  • 通义灵码 2.5 版深度评测:智能编程的边界在哪里?
  • 电商项目-商品微服务-规格参数管理,分类与品牌管理需求分析
  • 零基础设计模式——创建型模式 - 工厂方法模式
  • LeetCode 404.左叶子之和的递归求解:终止条件与递归逻辑的深度剖析
  • 蓝桥杯5130 健身
  • 电商虚拟户:重构资金管理逻辑,解锁高效归集与智能分账新范式
  • 腾讯2025年校招笔试真题手撕(二)
  • DeepSeek快速搭建个人网页
  • 安装完dockers后就无法联网了,执行sudo nmcli con up Company-WiFi,一直在加载中
  • 【深度学习新浪潮】2025年谷歌I/O开发者大会keynote观察
  • 小球弹弹弹
  • 案例分享——福建洋柄水库大桥智慧桥梁安全监测
  • 鸿蒙操作系统架构:构建全场景智慧生态的分布式操作系统
  • NBA足球赛事直播源码体育直播M35模板赛事源码
  • 自动化测试报告工具
  • Elasticsearch 实战面试题,每个题目都会单独解析
  • python 中 SchedulerManager 使用踩坑
  • Python后端框架新星Robyn:性能与开发体验的双重革命
  • 人工智能解析:技术革命下的认知重构
  • 【Linux】基础开发工具
  • OpenCV计算机视觉实战(7)——色彩空间详解
  • 体育直播网站如何实现实时数据
  • 【AI模型学习】上/下采样
  • Unity Shader入门(更新中)
  • 嵌入式学习的第二十六天-系统编程-文件IO+目录