任务的权限隔离与多租户(SaaS)平台设计要点

简介: 本文介绍了一个多租户平台的构建,旨在解决权限隔离和数据独立性问题。平台采用FastAPI、Celery+Redis、PostgreSQL多schema、Requests+代理IP和JWT+RBAC技术,实现了任务隔离、代理独立和数据分区。项目强调了多租户系统在任务独立、代理隔离、数据分区和权限控制方面的复杂性,并提出了进一步扩展

一、项目背景

很多人做采集做到中后期,都会遇到一个绕不开的问题——“多用户共用平台怎么隔离权限?”

一开始也许只是想做个简单的管理后台,大家都能登录提交爬取任务。但慢慢你会发现:

  • 不同客户的数据混在一起,查的时候要手动过滤;
  • 某个团队的代理IP被封了,影响到了别人;
  • 某个任务出错竟然把别的租户任务也卡死了……

这就是典型的 权限隔离和多租户(SaaS)问题

于是,我就做了这样一个实战项目:构建一个支持多租户、任务隔离、代理独立的采集平台。让每个租户(公司或部门)都能在自己的空间里提交任务、查看结果、用自己的代理池,不干扰别人。

二、数据目标

我们以一个具体案例来展开。

假设有几家汽车经销商客户,他们都想抓取Autohome 上的汽车品牌、型号和价格数据,用于市场分析。

要求很明确:

  • 每个客户能自定义关键词(比如“新能源SUV”、“丰田”等);
  • 每个客户的数据独立保存;
  • 抓取任务之间互不影响;
  • 每个客户使用自己的代理身份,避免被封号或数据串线。

换句话说,这个平台要实现一个带有“隐形边界”的多租户采集系统,每个租户既能自由爬,又互不打扰。

三、技术选型

为了实现这样的系统,我选用了这套技术组合:

  • FastAPI:轻量、快、天然适合做多租户API层;
  • Celery + Redis:分布式任务调度系统,用来分发不同租户的抓取任务;
  • PostgreSQL:多 schema 架构,每个租户单独一个 schema 实现逻辑隔离;
  • Requests + 代理IP:为每个租户配置独立的代理身份;
  • JWT + RBAC:用户身份认证 + 角色权限控制;
  • Docker Compose:快速部署和环境隔离。

目标很清晰:前端提交任务 → 后端认证租户 → Celery 分发任务 → 采集模块带上独立代理执行 → 数据落入对应 schema。

四、模块实现

1. 用户与租户模型

每个用户都属于某个租户。登录后系统会根据 JWT 自动识别所属租户,从而决定他能访问的数据范围。

# models.py
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from database import Base

class Tenant(Base):
    __tablename__ = "tenants"
    id = Column(Integer, primary_key=True)
    name = Column(String, unique=True)
    users = relationship("User", back_populates="tenant")

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    username = Column(String)
    password = Column(String)
    tenant_id = Column(Integer, ForeignKey("tenants.id"))
    tenant = relationship("Tenant", back_populates="users")

这一层主要负责身份绑定——系统永远知道请求是谁的。


2. 任务调度与权限隔离

接着是任务调度。Celery 负责异步任务的分发,每个租户的任务通过独立参数区分,避免冲突。

# tasks.py
from celery import Celery
from crawler import fetch_car_data

celery = Celery("crawler", broker="redis://localhost:6379/0")

@celery.task
def run_crawl_task(tenant_id, keyword):
    """
    每个租户独立运行的任务
    """
    data = fetch_car_data(keyword, tenant_id)
    save_to_tenant_schema(tenant_id, data)

不同租户的任务在执行层面上是逻辑隔离的,比如租户A在抓汽车,租户B在抓价格,他们都走不同的 Celery 任务通道。

3. 抓取核心模块

这是整个系统的灵魂。每个租户都会使用独立的代理凭证,从网络层就实现隔离。这里用的是 爬虫代理 举例。

# crawler.py
import requests
from fake_useragent import UserAgent

def fetch_car_data(keyword, tenant_id):
    """
    抓取汽车品牌与价格信息
    每个租户使用独立的代理IP和UA
    """
    ua = UserAgent()
    headers = {
   
        "User-Agent": ua.random,
        "Cookie": "your_cookie_here",
    }

    # 16YUN代理示例配置
    proxy_host = "proxy.16yun.cn"
    proxy_port = "3100"
    proxy_user = f"tenant_{tenant_id}_user"
    proxy_pass = "tenant_specific_password"

    proxy_meta = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
    proxies = {
   
        "http": proxy_meta,
        "https": proxy_meta,
    }

    url = f"https://www.autohome.com.cn/{keyword}/"
    response = requests.get(url, headers=headers, proxies=proxies, timeout=10)

    if response.status_code == 200:
        # 模拟解析逻辑
        return {
   "keyword": keyword, "price": "12.5万", "brand": "Toyota"}
    else:
        return {
   "error": f"Request failed: {response.status_code}"}

这里最关键的是:不同租户的代理身份是分开的,不仅能防止封禁波及,也保证了任务独立。

4. 数据隔离(多 Schema)

数据库层我采用了 PostgreSQL 的多 schema 架构。每个租户独立一个 schema,这样数据上就彻底隔离了。

# database.py
from sqlalchemy import create_engine

def get_engine(tenant_id):
    """
    根据租户ID连接对应schema
    """
    db_url = f"postgresql://user:pass@localhost:5432/crawler"
    engine = create_engine(db_url, connect_args={
   "options": f"-c search_path=tenant_{tenant_id}"})
    return engine

这样做的好处是显而易见的——哪怕某个租户的表炸了,其他租户的数据完全不受影响。

5. API 层实现(FastAPI)

API 层是整个系统的入口。用户带着 JWT 令牌请求任务接口,后端自动判断租户身份并分发任务。

# main.py
from fastapi import FastAPI, Depends
from auth import get_current_user
from tasks import run_crawl_task

app = FastAPI()

@app.post("/crawl")
def crawl(keyword: str, user=Depends(get_current_user)):
    """
    发起抓取任务(基于当前用户租户)
    """
    tenant_id = user.tenant_id
    run_crawl_task.delay(tenant_id, keyword)
    return {
   "msg": f"爬取任务已提交(租户 {tenant_id})"}

这样,一个租户发的任务永远不会被另一个租户干扰,整个过程对用户是透明的。

五、总结

多租户采集平台最难的地方,其实不在“爬”,而在“分”。

要解决的是——

  • 不同租户之间的 任务独立
  • 不同网络层的 代理隔离
  • 不同存储层的 数据分区
  • 不同访问层的 权限控制

简单回顾一下本项目的分层方案:

  • 网络层:独立代理(爬虫代理)
  • 存储层:PostgreSQL 多 schema
  • 应用层:JWT + 权限验证
  • 任务层:Celery 异步队列

进一步扩展的话,还可以:

  • 加上限速和优先级控制;
  • 用 Kubernetes 给不同租户分配资源;
  • 加入 Prometheus 做抓取性能监控。

做完这个项目,你会真正理解一个“企业级采集平台”要面对的复杂度。而最有趣的是,当这些机制搭好之后,你的系统会变得异常稳健——再多租户都能轻松扩展。

相关文章
|
5月前
|
缓存 运维 监控
《SaaS网关多租户治理:从串流到稳控的实践》
本文记录某制造集团SaaS协同平台API网关多租户治理的重构实践。初代网关因依赖“路径前缀+静态IP映射”,在租户增至8家(含3家私有云部署)后,爆发数据串流、混合云适配差、个性化需求迭代慢、故障定位难四大问题。通过搭建“租户元数据+动态路由表”双层隔离机制解决串流,设计多维度决策的混合云路由策略引擎降低转发延迟,构建配置化规则引擎实现零代码定制,并攻克缓存穿透、路由断连、规则冲突三大细节难题。最终租户串流率归零,混合云路由延迟降45%,规则生效时间从2天缩至10秒。
320 9
《SaaS网关多租户治理:从串流到稳控的实践》
|
SQL 运维 监控
云平台-多租户技术设计
云平台-多租户技术设计
云平台-多租户技术设计
|
1月前
|
人工智能 API 机器人
OpenClaw 用户部署和使用指南汇总
本文档为OpenClaw(原MoltBot)官方使用指南,涵盖一键部署(阿里云轻量服务器年仅68元)、钉钉/飞书/企微等多平台AI员工搭建、典型场景实践及高频问题FAQ。同步更新产品化修复进展,助力用户高效落地7×24小时主动执行AI助手。
18556 90
|
负载均衡 应用服务中间件 Linux
Nginx系列教程(14) - LVS+KeepAlived+Nginx实现高性能负载均衡集群
Nginx系列教程(14) - LVS+KeepAlived+Nginx实现高性能负载均衡集群
4093 0
|
5月前
|
资源调度 监控 测试技术
《SaaS多租户实战指南:从灰度发布到故障容错的全链路架构设计》
本文聚焦企业级团队协作SaaS应用的多租户架构迭代实践,针对租户规模差异大、资源冲突、定制化与标准化矛盾等核心痛点展开。初期简易多租户模式因资源共享导致故障后,作者重构架构:采用“独立数据库+共享数据库+租户标识”的混合隔离方案,解决数据隔离与成本平衡问题;搭建基于租户画像的弹性资源调度体系,通过预测式调度与实时调整提升资源利用率;以“核心标准化+定制插件化”架构,缩短定制需求响应时间;构建分层灰度发布与故障容错机制,将版本故障发生率大幅降低。最终总结出SaaS多租户架构需“以租户为中心”,在隔离、共享、定制间找到精细化平衡点的核心经验。
417 6
|
3月前
|
SQL 分布式计算 运维
一套平台养百家客户?多租户数据平台不是“分库分表”这么简单
一套平台养百家客户?多租户数据平台不是“分库分表”这么简单
135 6
|
存储 缓存 监控
怎么更好地设计一个优秀的SaaS系统
设计一个优秀的SaaS系统,需要从架构、性能、安全性、租户隔离、扩展性等多方面进行深思熟虑。根据业务需求选择合适的多租户架构,保证数据隔离的同时提高系统性能。
1285 1
|
SQL 存储 druid
浅析SaaS多租户系统数据隔离实现方案
多租户问题,其是一种架构设计方式,就是在一台或者一组服务器上运行的SaaS系统,可以为多个租户(客户)提供服务,目的是为了让多个租户在互联网环境下使用同一套程序,且保证租户间的数据隔离。从这种架构设计的模式上,不难看出来,多租户架构的重点就是同一套程序下多个租户数据的隔离。由于租户数据是集中存储的,所以要实现数据的安全性,就是看能否实现对租户数据的隔离,防止租户数据不经意或被他人恶意地获取和篡改
2695 0