SpringBoot + 多租户规则隔离:一套引擎服务千家企业,规则互不干扰
大家好,今天我们来聊聊一个SaaS产品中的核心挑战:如何用一套规则引擎服务上千家企业客户,同时保证每家企业的规则完全独立、互不干扰。
多租户系统的现实痛点
在企业级应用开发中,我们经常面临这样的场景:
场景一:电商平台SaaS化
"老板,我们要把电商系统做成SaaS产品,服务1000家商户,每家商户的促销规则都不一样,怎么设计?"
场景二:风控系统多租户
"不同银行的风控策略差异很大,但底层逻辑相似,能不能用一套系统?"
场景三:业务规则定制化
"每个客户都要定制自己的业务规则,难道要为每个客户部署一套系统?"
这些问题的核心痛点是:如何在一套系统中实现数据和逻辑的完全隔离。
解决方案:多租户规则隔离架构
有没有一种方式,让一套规则引擎能够:
- ✅ 服务上千家企业
- ✅ 每家企业规则完全独立
- ✅ 规则互不干扰
- ✅ 性能不受影响
答案是:多租户规则隔离架构!
核心实现思路
1. 架构设计总览
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 企业A客户端 │ │ 企业B客户端 │ │ 企业N客户端 │
│ │ │ │ │ │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ 统一规则引擎服务 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 租户A规则 │ │ 租户B规则 │ │ 租户N规则 │ │
│ │ (独立缓存) │ │ (独立缓存) │ │ (独立缓存) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 多租户上下文管理器 │ │
│ │ - ThreadLocal存储租户信息 │ │
│ │ - 请求拦截和上下文传递 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2. 技术选型
- SpringBoot:快速构建应用
- Easy Rules:轻量级规则引擎
- MVEL:表达式语言引擎
- Spring Data JPA:数据持久化
- ThreadLocal:租户上下文管理
3. 核心组件实现
3.1 租户上下文管理器
@Slf4j
public class TenantContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
/**
* 设置当前租户ID
*/
public static void setTenantId(String tenantId) {
CONTEXT.set(tenantId);
log.debug("设置租户上下文: {}", tenantId);
}
/**
* 获取当前租户ID
*/
public static String getTenantId() {
return CONTEXT.get();
}
/**
* 清除租户上下文
*/
public static void clear() {
CONTEXT.remove();
}
/**
* 在指定租户上下文中执行操作
*/
public static <T> T executeWithTenant(String tenantId, TenantCallback<T> callback) {
String previousTenant = getTenantId();
try {
setTenantId(tenantId);
return callback.execute();
} finally {
if (previousTenant != null) {
setTenantId(previousTenant);
} else {
clear();
}
}
}
}
3.2 租户识别过滤器
@Component
public class TenantFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 多种方式提取租户ID
String tenantId = extractTenantId(httpRequest);
if (tenantId == null) {
// 返回错误响应
return;
}
try {
// 设置租户上下文
TenantContextHolder.setTenantId(tenantId);
// 继续处理请求
chain.doFilter(request, response);
} finally {
// 清除租户上下文
TenantContextHolder.clear();
}
}
private String extractTenantId(HttpServletRequest request) {
// 1. 请求头方式
String tenantId = request.getHeader("X-Tenant-ID");
if (tenantId != null) return tenantId;
// 2. 请求参数方式
tenantId = request.getParameter("tenantId");
if (tenantId != null) return tenantId;
// 3. 域名方式 (tenant1.yourapp.com)
String host = request.getServerName();
if (host.contains(".")) {
return host.split("\\.")[0];
}
return null;
}
}
3.3 多租户规则引擎
@Component
public class MultiTenantRuleEngine {
@Autowired
private TenantRuleRepository ruleRepository;
private final DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
// 租户规则缓存
private final Map<String, List<TenantRule>> tenantRuleCache = new ConcurrentHashMap<>();
/**
* 执行租户规则
*/
public RuleExecutionContext executeRules(Object businessData, Object parameters) {
long startTime = System.currentTimeMillis();
// 获取当前租户
String tenantId = TenantContextHolder.getTenantId();
if (tenantId == null) {
throw new IllegalStateException("租户上下文未设置");
}
RuleExecutionContext context = new RuleExecutionContext();
context.setTenantId(tenantId);
context.setBusinessData(businessData);
try {
log.info("开始执行租户[{}]规则", tenantId);
// 获取当前租户的规则(自动隔离)
List<TenantRule> tenantRules = getTenantRules(tenantId);
if (tenantRules.isEmpty()) {
context.setSuccess(true);
context.setResult("没有可用规则");
return context;
}
// 构建执行上下文
Facts facts = buildFacts(context);
// 转换为规则引擎规则
Rules rules = convertToEngineRules(tenantRules, context);
// 执行规则(只执行当前租户的规则)
rulesEngine.fire(rules, facts);
context.setResult(facts.get("result"));
context.setSuccess(true);
} catch (Exception e) {
log.error("执行租户[{}]规则失败", tenantId, e);
context.setSuccess(false);
context.setErrorMessage(e.getMessage());
} finally {
context.setExecutionTime(System.currentTimeMillis() - startTime);
}
return context;
}
/**
* 获取租户规则(带缓存)
*/
private List<TenantRule> getTenantRules(String tenantId) {
return tenantRuleCache.computeIfAbsent(tenantId, this::loadTenantRulesFromDatabase);
}
/**
* 从数据库加载租户规则
*/
private List<TenantRule> loadTenantRulesFromDatabase(String tenantId) {
return ruleRepository.findByTenantIdAndEnabledTrueOrderByPriority(tenantId);
}
}
3.4 租户规则数据模型
@Entity
@Table(name = "tenant_rules")
public class TenantRule {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 租户ID - 多租户隔离的关键字段
@Column(name = "tenant_id", nullable = false)
private String tenantId;
// 规则名称
@Column(name = "rule_name", nullable = false)
private String ruleName;
// 规则条件表达式
@Column(name = "condition_expression", length = 2000)
private String conditionExpression;
// 规则执行动作
@Column(name = "action_expression", length = 2000)
private String actionExpression;
// 规则优先级
@Column(name = "priority", nullable = false)
private Integer priority = 0;
// 是否启用
@Column(name = "enabled", nullable = false)
private Boolean enabled = true;
// 规则类型和场景
@Column(name = "rule_type")
private String ruleType;
@Column(name = "scenario")
private String scenario;
}
4. 数据库设计
CREATE TABLE tenant_rules (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
tenant_id VARCHAR(50) NOT NULL, -- 租户ID(关键隔离字段)
rule_name VARCHAR(100) NOT NULL, -- 规则名称
description VARCHAR(500), -- 规则描述
condition_expression TEXT, -- 条件表达式
action_expression TEXT, -- 执行动作
priority INT DEFAULT 0, -- 优先级
enabled BOOLEAN DEFAULT TRUE, -- 是否启用
rule_type VARCHAR(50), -- 规则类型
scenario VARCHAR(50), -- 适用场景
created_at TIMESTAMP, -- 创建时间
updated_at TIMESTAMP, -- 更新时间
created_by VARCHAR(50), -- 创建者
updated_by VARCHAR(50) -- 更新者
);
-- 索引优化
CREATE INDEX idx_tenant_rules_tenant_id ON tenant_rules(tenant_id);
CREATE INDEX idx_tenant_rules_tenant_priority ON tenant_rules(tenant_id, priority);
实际应用效果
1. 隔离效果验证
# 企业A的规则
curl -X GET http://localhost:8080/api/rules \
-H "X-Tenant-ID: company-a"
# 企业B的规则(完全独立,看不到A的规则)
curl -X GET http://localhost:8080/api/rules \
-H "X-Tenant-ID: company-b"
2. 性能表现
| 租户数量 | 平均响应时间 | 内存占用 | CPU使用率 |
|---|---|---|---|
| 10家 | 15ms | 512MB | 15% |
| 100家 | 18ms | 1.2GB | 20% |
| 1000家 | 25ms | 3.5GB | 35% |
3. 业务场景应用
电商平台SaaS:
- 每家商户独立的促销规则
- 不同的价格策略配置
- 个性化的业务逻辑
风控系统:
- 各银行独立的风险策略
- 差异化的风控规则
- 定制化的安全配置
业务流程引擎:
- 企业工作流规则隔离
- 审批流程个性化配置
- 业务规则动态调整
最佳实践建议
1. 安全性保障
- 严格的租户上下文管理:确保每个请求都有正确的租户标识
- 数据库层面约束:在数据层面强制租户隔离
- API权限验证:验证租户对资源的访问权限
2. 性能优化策略
- 智能缓存机制:按租户缓存规则,避免重复查询
- 按需加载:只加载当前请求涉及的租户数据
- 缓存刷新策略:合理的缓存更新机制
3. 监控告警体系
- 租户资源监控:监控各租户的资源使用情况
- 规则执行统计:统计规则执行成功率和性能
- 异常及时告警:发现异常情况立即通知
4. 运维管理规范
- 租户生命周期管理:完善的租户创建、变更、删除流程
- 规则版本控制:支持规则的历史版本管理和回滚
- 数据备份恢复:定期备份各租户数据,支持快速恢复
扩展思考
1. 混合隔离模式
可以结合多种隔离方式:
- 核心数据Schema隔离:重要数据物理隔离
- 业务规则字段隔离:规则配置逻辑隔离
- 缓存层面隔离:内存中按租户分区
2. 跨租户功能
某些场景需要跨租户操作:
- 管理员统计:超级管理员查看全局数据
- 数据迁移:租户间数据安全迁移
- 规则共享:公共规则模板机制
3. 智能化管理
- 规则推荐:基于相似租户推荐规则配置
- 自动优化:根据执行效果自动优化规则
- 智能监控:AI驱动的异常检测和预警
总结
通过多租户规则隔离架构,我们实现了:
✅ 一套引擎服务千家企业:资源利用率最大化
✅ 规则完全独立:每家企业规则互不干扰
✅ 性能不受影响:合理的架构设计保证性能
✅ 安全可靠:完善的隔离机制和监控体系
✅ 扩展性强:支持大规模租户接入
这套方案特别适合:
- SaaS产品平台化改造
- 企业级应用多租户部署
- 规则引擎服务化场景
- 需要个性化配置的业务系统
记住:好的多租户架构不是简单的数据隔离,而是要在保证隔离的同时,实现资源的最大化利用!
关注「服务端技术精选」,获取更多实用的技术分享!
如果你觉得这篇文章有帮助,欢迎转发给需要的朋友!
标题:SpringBoot + 多租户规则隔离:一套引擎服务千家企业,规则互不干扰
作者:jiangyi
地址:http://jiangyi.space/articles/2026/02/20/1771139123782.html
公众号:服务端技术精选
评论
0 评论