任务的权限隔离与多租户(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 做抓取性能监控。

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

相关文章
|
SQL 运维 监控
云平台-多租户技术设计
云平台-多租户技术设计
云平台-多租户技术设计
|
负载均衡 应用服务中间件 Linux
Nginx系列教程(14) - LVS+KeepAlived+Nginx实现高性能负载均衡集群
Nginx系列教程(14) - LVS+KeepAlived+Nginx实现高性能负载均衡集群
3964 0
|
14天前
|
人工智能 程序员 决策智能
2026年智能体(Agent)怎么学?从入门到实战的全景避坑指南
2026年,AI进入“智能体元年”。本文系统解析智能体四大核心架构与Agentic Workflow设计模式,涵盖开发者、产品经理到业务人员的实战路径,助力把握AI代理红利期,实现从工具应用到架构创新的跃迁。
580 5
|
8月前
|
人工智能 调度 芯片
《大模型背后的隐形战场:异构计算调度全解析》
在大模型训练中,CPU、GPU和AI芯片各司其职:CPU擅长逻辑控制,GPU专攻并行计算,AI芯片则针对特定AI任务优化。然而,实现三者的高效协同面临诸多挑战,如任务分配、通信延迟及资源管理等问题。通过动态任务分配、通信优化与资源调整等策略,可提升训练效率。未来,随着硬件进步和算法智能化,异构计算协同调度将更加高效,并结合云计算、边缘计算等技术拓展应用范围,推动人工智能技术发展。
574 15
|
存储 缓存 监控
怎么更好地设计一个优秀的SaaS系统
设计一个优秀的SaaS系统,需要从架构、性能、安全性、租户隔离、扩展性等多方面进行深思熟虑。根据业务需求选择合适的多租户架构,保证数据隔离的同时提高系统性能。
1152 1
|
SQL 存储 druid
浅析SaaS多租户系统数据隔离实现方案
多租户问题,其是一种架构设计方式,就是在一台或者一组服务器上运行的SaaS系统,可以为多个租户(客户)提供服务,目的是为了让多个租户在互联网环境下使用同一套程序,且保证租户间的数据隔离。从这种架构设计的模式上,不难看出来,多租户架构的重点就是同一套程序下多个租户数据的隔离。由于租户数据是集中存储的,所以要实现数据的安全性,就是看能否实现对租户数据的隔离,防止租户数据不经意或被他人恶意地获取和篡改
2628 0
|
JavaScript 前端开发 算法
虚拟 DOM 是什么?
【10月更文挑战第18天】虚拟 DOM 是前端框架中的一项重要技术,它通过抽象和优化 DOM 操作,为前端应用带来了更好的性能、开发效率和可维护性。
616 1
|
存储 安全 Java
发现 XSS 漏洞?别急!SpringBoot这招轻松搞定!
在SpringBoot中,发现XSS(跨站脚本)漏洞时,可以通过一系列措施来轻松搞定这些安全问题。XSS攻击允许攻击者在受害者的浏览器中注入恶意脚本,这些脚本可以窃取用户的敏感信息、劫持用户会话或进行其他恶意操作。以下是一些在SpringBoot中修复XSS漏洞的有效方法
2535 7
|
小程序 Android开发 iOS开发
微信小程序-虚拟支付:适用场景 / iPhone调试用支付成功,Android调用失败,提示“小程序支付能力已被限制” / “errMsg“.“requestPayment:fail banned”
微信小程序-虚拟支付:适用场景 / iPhone调试用支付成功,Android调用失败,提示“小程序支付能力已被限制” / “errMsg“.“requestPayment:fail banned”
1331 0
|
存储 API C语言
C语言Log工具推荐-easylogger
C语言Log工具推荐-easylogger
554 1