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

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.xdnf.cn/news/592795.html

相关文章:

  • 为什么mosquitto 禁用了 topic “#“后,无法使用主题中包含%c client_id了?
  • python容器
  • PTA刷题笔记
  • 浏览器原生 Web Crypto API 实现 SHA256 Hash 加密
  • 六:操作系统虚拟内容之内存文件映射
  • DeepSeek的进阶应用场景大全
  • poppler_path 是用于 Python 库如 pdf2image 进行 PDF 转换时
  • 《告别单一智能:神经符号混合系统驱动推理能力的跨界融合》
  • 哈希表和容器中添加元素的方法
  • 什么是CDN(Content Delivery Network,内容分发网络)
  • ubunt配置本地源
  • Linux开发板串口终端会限制命令字符数并且循环覆盖
  • 并发编程 之 TreeMap ConcurrentSkipListMap set queue源码分析
  • 自动化测试报告工具
  • 【八股战神篇】Redis高频面试题
  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月22日第85弹
  • 数据结构知识点汇总
  • 速卖通关键词搜索API开发指南
  • 简单说一下px和ex 的区别......
  • 测试文章1
  • ATGM336H-6N_GNSS 单频多模定位导航模块
  • IEEE Wireless Communications 2025年1月-4月论文速览
  • 二十一、面向对象底层逻辑-scope作用域接口设计
  • 05算法学习_59. 螺旋矩阵 II
  • 如何测试JWT的安全性:全面防御JSON Web Token的安全漏洞
  • 第34节:迁移学习中的特征提取方法
  • 落石滑坡倒树自然灾害检测数据集VOC+YOLO格式958张3类别
  • Linux 搭建FTP服务器(vsftpd)
  • 操作系统结构
  • C++23中std::span和std::basic_string_view可平凡复制提案解析