← 返回首页
💼

SaaS架构:多租户、数据隔离与计费

📂 architecture ⏱ 3 min 520 words

SaaS架构:多租户、数据隔离与计费

SaaS系统架构概览

SaaS(Software as a Service)是软件即服务的模式,需要支持多租户共享应用实例。核心挑战是多租户隔离、数据安全、按需计费。

SaaS系统架构:

接入层:租户路由、身份认证、权限控制
业务层:
  - 租户管理:租户注册、配置、配额
  - 数据隔离:数据存储、访问控制、备份恢复
  - 计费服务:订阅管理、用量计量、账单生成
  - 资源管理:资源分配、扩缩容、配额控制
数据层:MySQL(共享/独立)、Redis、对象存储
基础设施:Kubernetes、服务网格、监控告警

多租户架构设计

多租户是SaaS的核心,需要在共享资源和租户隔离之间找到平衡。

# 租户管理服务
class TenantService:
    def __init__(self):
        self.tenant_repo = TenantRepository()
        self.config_service = TenantConfigService()
    
    def create_tenant(self, tenant_info):
        """创建租户"""
        # 1. 创建租户记录
        tenant = Tenant(
            id=generate_tenant_id(),
            name=tenant_info.name,
            plan=tenant_info.plan,
            status='ACTIVE',
            create_time=time.time()
        )
        self.tenant_repo.save(tenant)
        
        # 2. 初始化租户配置
        self.config_service.init_tenant_config(tenant.id, tenant_info.plan)
        
        # 3. 分配资源
        self.allocate_resources(tenant)
        
        # 4. 初始化租户数据
        self.init_tenant_data(tenant.id)
        
        return tenant
    
    def allocate_resources(self, tenant):
        """分配资源"""
        # 根据套餐分配资源
        plan_resources = {
            'basic': {'storage': 10, 'users': 10, 'api_calls': 1000},
            'pro': {'storage': 100, 'users': 100, 'api_calls': 10000},
            'enterprise': {'storage': 1000, 'users': 1000, 'api_calls': 100000},
        }
        
        resources = plan_resources.get(tenant.plan, plan_resources['basic'])
        
        # 保存资源配额
        self.save_resource_quota(tenant.id, resources)

数据隔离方案

数据隔离是SaaS的关键,需要根据安全要求和成本考虑选择合适的隔离方案。

// 数据隔离服务
@Service
public class DataIsolationService {
    @Autowired
    private DataSourceRouter dataSourceRouter;
    @Autowired
    private TenantContext tenantContext;
    
    // 获取租户数据源
    public DataSource getTenantDataSource() {
        String tenantId = tenantContext.getTenantId();
        return dataSourceRouter.route(tenantId);
    }
    
    // 执行租户查询
    public <T> List<T> executeTenantQuery(String sql, Class<T> resultType) {
        // 1. 获取租户数据源
        DataSource ds = getTenantDataSource();
        
        // 2. 注入租户过滤条件
        String tenantSql = injectTenantFilter(sql, tenantContext.getTenantId());
        
        // 3. 执行查询
        return jdbcTemplate.query(ds, tenantSql, resultType);
    }
    
    // 注入租户过滤条件
    private String injectTenantFilter(String sql, String tenantId) {
        // 自动添加租户ID过滤
        if (sql.contains("WHERE")) {
            return sql + " AND tenant_id = '" + tenantId + "'";
        } else {
            return sql + " WHERE tenant_id = '" + tenantId + "'";
        }
    }
}

// 数据源路由器
@Component
public class DataSourceRouter {
    private final Map<String, DataSource> dataSources = new HashMap<>();
    
    @PostConstruct
    public void init() {
        // 加载所有租户数据源
        List<Tenant> tenants = tenantRepository.findAll();
        for (Tenant tenant : tenants) {
            dataSources.put(tenant.getId(), createDataSource(tenant));
        }
    }
    
    public DataSource route(String tenantId) {
        return dataSources.computeIfAbsent(tenantId, this::createTenantDataSource);
    }
    
    private DataSource createTenantDataSource(Tenant tenant) {
        // 根据租户配置创建数据源
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(tenant.getDbUrl());
        config.setUsername(tenant.getDbUsername());
        config.setPassword(tenant.getDbPassword());
        config.setMaximumPoolSize(10);
        
        return new HikariDataSource(config);
    }
}

计费系统设计

计费系统是SaaS的商业模式核心,需要支持多种计费模式和准确的用量计量。

# 计费服务
class BillingService:
    def __init__(self):
        self.usage_meter = UsageMeter()
        self.billing_repo = BillingRepository()
        self.payment_service = PaymentService()
    
    def record_usage(self, tenant_id, metric_type, quantity):
        """记录用量"""
        # 1. 记录用量
        usage = UsageRecord(
            tenant_id=tenant_id,
            metric_type=metric_type,
            quantity=quantity,
            timestamp=time.time()
        )
        self.usage_meter.record(usage)
        
        # 2. 检查配额
        quota = self.check_quota(tenant_id, metric_type)
        if quantity > quota.remaining:
            raise QuotaExceededError()
    
    def generate_invoice(self, tenant_id, billing_period):
        """生成账单"""
        # 1. 计算用量费用
        usage_charges = self.calculate_usage_charges(tenant_id, billing_period)
        
        # 2. 获取订阅费用
        subscription_charge = self.get_subscription_charge(tenant_id)
        
        # 3. 生成账单
        invoice = Invoice(
            tenant_id=tenant_id,
            billing_period=billing_period,
            usage_charges=usage_charges,
            subscription_charge=subscription_charge,
            total_amount=usage_charges + subscription_charge,
            status='PENDING'
        )
        
        self.billing_repo.save_invoice(invoice)
        return invoice
    
    def calculate_usage_charges(self, tenant_id, billing_period):
        """计算用量费用"""
        # 获取套餐价格
        plan = self.get_tenant_plan(tenant_id)
        pricing = self.get_pricing(plan)
        
        # 计算各指标费用
        total = 0
        for metric_type, price_per_unit in pricing.items():
            usage = self.usage_meter.get_usage(tenant_id, metric_type, billing_period)
            total += usage * price_per_unit
        
        return total

租户配置管理

每个租户可能有不同的配置需求,需要支持灵活的配置管理。

// 租户配置服务
@Service
public class TenantConfigService {
    @Autowired
    private ConfigRepository configRepo;
    @Autowired
    private CacheManager cacheManager;
    
    // 获取租户配置
    public TenantConfig getTenantConfig(String tenantId, String configKey) {
        // 1. 尝试从缓存获取
        String cacheKey = "config:" + tenantId + ":" + configKey;
        TenantConfig cached = cacheManager.get(cacheKey);
        if (cached != null) {
            return cached;
        }
        
        // 2. 从数据库获取
        TenantConfig config = configRepo.findByTenantIdAndKey(tenantId, configKey);
        
        // 3. 如果租户没有配置,使用默认配置
        if (config == null) {
            config = getDefaultConfig(configKey);
        }
        
        // 4. 缓存结果
        cacheManager.put(cacheKey, config, 3600);
        
        return config;
    }
    
    // 更新租户配置
    public void updateTenantConfig(String tenantId, String configKey, Object value) {
        // 1. 更新数据库
        configRepo.upsert(tenantId, configKey, value);
        
        // 2. 清除缓存
        String cacheKey = "config:" + tenantId + ":" + configKey;
        cacheManager.evict(cacheKey);
        
        // 3. 通知配置变更
        notifyConfigChange(tenantId, configKey, value);
    }
}

SaaS平台监控

SaaS平台需要监控租户级别的指标,支持租户隔离和问题定位。

SaaS监控体系:

租户级别监控:
  - 资源使用率(CPU、内存、存储)
  - API调用量和响应时间
  - 业务指标(活跃用户、数据量)
  - 计费指标(用量、费用)

平台级别监控:
  - 整体可用性
  - 资源利用率
  - 性能指标
  - 安全事件

告警策略:
  - 资源使用率超过阈值
  - API响应时间超标
  - 配额即将用尽
  - 安全异常检测

数据隔离:
  - 租户数据访问审计
  - 租户间资源隔离
  - 租户配置隔离
  - 租户日志隔离