Django MVT vs FastAPI DDD架构

Django MVT vs FastAPI DDD 架构区别

文章目录

    • [Django MVT vs FastAPI DDD 架构区别](#Django MVT vs FastAPI DDD 架构区别)
  • [01:Django MVT vs FastAPI ------ "全能坦克"与"极速跑车"的架构对决](#01:Django MVT vs FastAPI —— “全能坦克”与“极速跑车”的架构对决)
    • [🏛️ 核心哲学对比](#🏛️ 核心哲学对比)
      • [1. Django: "Batteries Included" (自带电池) -> **MVT 架构**](#1. Django: "Batteries Included" (自带电池) -> MVT 架构)
      • [2. FastAPI: "Modern & Fast" (现代与速度) -> **路由 + 依赖注入**](#2. FastAPI: "Modern & Fast" (现代与速度) -> 路由 + 依赖注入)
    • [⚔️ 架构组件深度对标](#⚔️ 架构组件深度对标)
    • [🔍 关键差异点详解](#🔍 关键差异点详解)
      • [1. 路由注册方式:分散 vs 集中](#1. 路由注册方式:分散 vs 集中)
      • [2. 依赖注入:隐式全局 vs 显式组合](#2. 依赖注入:隐式全局 vs 显式组合)
      • [3. 数据验证:运行时 vs 编译时(类型提示)](#3. 数据验证:运行时 vs 编译时(类型提示))
      • [4. 异步支持 (Async/Await)](#4. 异步支持 (Async/Await))
    • [🧐 架构选择指南:什么时候用哪个?](#🧐 架构选择指南:什么时候用哪个?)
      • [✅ 选择 Django (MVT) 当:](#✅ 选择 Django (MVT) 当:)
      • [✅ 选择 FastAPI 当:](#✅ 选择 FastAPI 当:)
    • [💣 常见误区](#💣 常见误区)
    • [📝 总结](#📝 总结)
  • [02:架构深潜 ------ FastAPI 下的 DDD 与 Django 的 MTV 实战原理](#02:架构深潜 —— FastAPI 下的 DDD 与 Django 的 MTV 实战原理)
    • [🏛️ 第一部分:Django 的 MTV 架构 ------ 从"大泥球"到"实用主义分层"](#🏛️ 第一部分:Django 的 MTV 架构 —— 从“大泥球”到“实用主义分层”)
      • [1. 原生 MTV 的核心逻辑](#1. 原生 MTV 的核心逻辑)
      • [2. 面对复杂业务时的"痛点"](#2. 面对复杂业务时的“痛点”)
      • [3. Django 下的"伪 DDD"演进策略](#3. Django 下的“伪 DDD”演进策略)
    • [🚀 第二部分:FastAPI 下的 DDD 架构 ------ 天生一对的"自由组合"](#🚀 第二部分:FastAPI 下的 DDD 架构 —— 天生一对的“自由组合”)
      • [1. FastAPI 与 DDD 的天然契合点](#1. FastAPI 与 DDD 的天然契合点)
      • [2. 标准的 FastAPI + DDD 分层架构原理](#2. 标准的 FastAPI + DDD 分层架构原理)
        • [A. 领域层 (Domain Layer) ------ 核心中的核心](#A. 领域层 (Domain Layer) —— 核心中的核心)
        • [B. 应用层 (Application Layer) ------ 用例编排](#B. 应用层 (Application Layer) —— 用例编排)
        • [C. 基础设施层 (Infrastructure Layer) ------ 具体实现](#C. 基础设施层 (Infrastructure Layer) —— 具体实现)
        • [D. 表现层 (Presentation / Interface Layer) ------ FastAPI 的主场](#D. 表现层 (Presentation / Interface Layer) —— FastAPI 的主场)
      • [3. 依赖流向:箭头向内](#3. 依赖流向:箭头向内)
    • [⚔️ 第三部分:深度对比 ------ 两种哲学的碰撞](#⚔️ 第三部分:深度对比 —— 两种哲学的碰撞)
    • [💡 核心原理总结](#💡 核心原理总结)
      • [1. Django 的"实用主义"](#1. Django 的“实用主义”)
      • [2. FastAPI 的"自由主义"](#2. FastAPI 的“自由主义”)
      • [3. 关键分歧点:贫血模型 vs 充血模型](#3. 关键分歧点:贫血模型 vs 充血模型)
    • [📝 结语](#📝 结语)
  • [03:实战深潜 ------ 文件上传与异步解析的架构对决](#03:实战深潜 —— 文件上传与异步解析的架构对决)
    • [🎯 场景定义:核心挑战](#🎯 场景定义:核心挑战)
    • [🐘 方案一:Django 架构 (MTV + Celery)](#🐘 方案一:Django 架构 (MTV + Celery))
      • "重型坦克"模式:稳健、隔离、但略显笨重
      • [1. 架构流向](#1. 架构流向)
      • [2. 核心组件职责](#2. 核心组件职责)
        • [A. Model (数据层)](#A. Model (数据层))
        • [B. View (控制层)](#B. View (控制层))
        • [C. Task (异步层 - Celery)](#C. Task (异步层 - Celery))
      • [3. Django 方案的痛点](#3. Django 方案的痛点)
    • [⚡ 方案二:FastAPI 架构 (DDD + Native Async)](#⚡ 方案二:FastAPI 架构 (DDD + Native Async))
      • "极速跑车"模式:轻量、内聚、极致并发
      • [1. 架构流向 (轻量级原生方案)](#1. 架构流向 (轻量级原生方案))
      • [2. 核心组件职责 (DDD 分层)](#2. 核心组件职责 (DDD 分层))
        • [A. Domain Layer (领域层)](#A. Domain Layer (领域层))
        • [B. Infrastructure Layer (基础设施层)](#B. Infrastructure Layer (基础设施层))
        • [C. Interface Layer (FastAPI Router)](#C. Interface Layer (FastAPI Router))
      • [3. FastAPI 方案的痛点](#3. FastAPI 方案的痛点)
    • [⚔️ 深度对比:架构决策的关键点](#⚔️ 深度对比:架构决策的关键点)
    • [💡 最佳实践建议](#💡 最佳实践建议)
      • [1. 什么时候坚持用 Django + Celery?](#1. 什么时候坚持用 Django + Celery?)
      • [2. 什么时候选择 FastAPI + Native Async?](#2. 什么时候选择 FastAPI + Native Async?)
      • [3. 混合模式 (The Best of Both Worlds)](#3. 混合模式 (The Best of Both Worlds))
    • [📝 总结](#📝 总结)
  • [04:混合架构实战 ------ FastAPI + Celery 的"黄金搭档"模式](#04:混合架构实战 —— FastAPI + Celery 的“黄金搭档”模式)
    • [🏗️ 架构全景图:各司其职](#🏗️ 架构全景图:各司其职)
    • [💻 核心代码实现原理](#💻 核心代码实现原理)
      • [1. 项目结构建议](#1. 项目结构建议)
      • [2. 第一步:配置 Celery (`core/celery_app.py`)](#2. 第一步:配置 Celery (core/celery_app.py))
      • [3. 第二步:定义 Celery 任务 (`tasks.py`)](#3. 第二步:定义 Celery 任务 (tasks.py))
      • [4. 第三步:FastAPI 路由层 (`api/routes.py`)](#4. 第三步:FastAPI 路由层 (api/routes.py))
    • [⚙️ 关键难点与解决方案](#⚙️ 关键难点与解决方案)
      • [1. 异步 (FastAPI) 与 同步 (Celery) 的摩擦](#1. 异步 (FastAPI) 与 同步 (Celery) 的摩擦)
      • [2. 文件传递方式](#2. 文件传递方式)
      • [3. 状态同步机制](#3. 状态同步机制)
      • [4. 优雅关闭与信号处理](#4. 优雅关闭与信号处理)
    • [🚀 进阶优化:如何让体验更丝滑?](#🚀 进阶优化:如何让体验更丝滑?)
      • [1. 实时推送 (WebSocket + Celery)](#1. 实时推送 (WebSocket + Celery))
      • [2. 动态扩缩容](#2. 动态扩缩容)
      • [3. 依赖注入的妙用](#3. 依赖注入的妙用)
    • [📝 总结:为什么这是"Best of Both Worlds"?](#📝 总结:为什么这是“Best of Both Worlds”?)

01:Django MVT vs FastAPI ------ "全能坦克"与"极速跑车"的架构对决

写在前面

今天是 2026 年 3 月 15 日。在 Python Web 的生态里,DjangoFastAPI 无疑是两座高峰。

很多刚学完 Django MVT 模式的同学,转头看到 FastAPI 的代码结构,往往会感到一阵眩晕:"怎么没有 Template 了?View 去哪了?Controller 呢?"

这篇笔记,我们不谈谁好谁坏,只从架构设计哲学 的角度,深度拆解 Django 的 MVTFastAPI 的路由/依赖注入模式 到底有什么本质区别。搞懂了这个,你才能在不同场景下选对框架。


🏛️ 核心哲学对比

1. Django: "Batteries Included" (自带电池) -> MVT 架构

Django 诞生于新闻编辑室,目的是快速发布内容

  • 核心逻辑 :框架帮你把该想的都想好了。它强制你遵循 MVT (Model-View-Template) 模式。
  • 特点:强约定、单体化、同步为主(虽已支持异步)、全功能集成。
  • 类比 :像一家五星级酒店。你住进去,房间、餐厅、健身房、洗衣服务全都有。你不需要自己带床单,但你也很难把床拆了换成吊床。

2. FastAPI: "Modern & Fast" (现代与速度) -> 路由 + 依赖注入

FastAPI 诞生于微服务和 API 优先的时代,目的是高性能与开发体验

  • 核心逻辑 :框架只提供核心能力(路由、验证、文档),其他全靠你组装。它没有固定的"MVT"说法,通常被称为 Path Operation Functions 配合 Dependency Injection
  • 特点:弱约定、模块化、原生异步、基于类型提示(Type Hints)。
  • 类比 :像一套乐高积木 或者赛车改装车间。只给你底盘和引擎(路由和验证),车身、轮胎、内饰你自己选。你可以组装成 F1 赛车,也可以组装成家用轿车,自由度极高,但需要你自己懂搭配。

⚔️ 架构组件深度对标

让我们把两个框架的组件拉出来"排排坐",看看它们是如何对应(或不对应)的。

概念 Django (MVT) FastAPI (现代 API 风格) 本质区别
数据层 Model (models.py)内置 ORM,强耦合 Django。 Schema / Model (pydantic.BaseModel)通常用 Pydantic 做数据验证,ORM 可选 (SQLAlchemy, Tortoise, Django ORM)。 Django Model 既是数据结构又是数据库映射;FastAPI 中数据验证 (Pydantic) 与 数据库操作 (ORM) 是分离的。
逻辑层 View (views.py)函数或类,接收 Request,返回 Response。 Path Operation Function 直接装饰在函数上 (@app.get),逻辑即路由。 Django 的 View 需要先在 urls.py 注册;FastAPI 的路由直接定义在函数上,所见即所得
展示层 Template (templates/)DTL 语法,负责渲染 HTML。 无内置模板通常返回 JSON。若需 HTML,需手动集成 Jinja2 (其实 Django 模板也源于此)。 Django 默认为了服务端渲染 (SSR) ;FastAPI 默认为了前后端分离 (API)
请求处理 Middleware & Context Processors 全局钩子,隐式传递 request Dependency Injection显式声明依赖,参数自动注入。 这是最大的架构差异! Django 隐式,FastAPI 显式。
文档 需第三方库 (如 drf-spectacular) 或手写。 自动生成 OpenAPI (Swagger/ReDoc)基于类型提示自动生成。 FastAPI 将文档作为一等公民,Django 视为附加功能。

🔍 关键差异点详解

1. 路由注册方式:分散 vs 集中

  • Django (集中式)

    你需要维护一个 urls.py 文件,把 URL 路径映射到 View 函数。

    python 复制代码
    # urls.py
    path('articles/<int:id>/', views.article_detail, name='detail')
    
    # views.py
    def article_detail(request, id): ...

    缺点:改个 URL 要动两个文件,容易不同步。

  • FastAPI (分散式/装饰器)

    路由直接挂在函数头上。

    python 复制代码
    @app.get("/articles/{article_id}")
    async def read_article(article_id: int):
        return {"id": article_id}

    优点:代码在一起,重构方便,类型检查直接生效。

2. 依赖注入:隐式全局 vs 显式组合

这是 FastAPI 最迷人的地方,也是它与 Django 最大的架构不同。

  • Django 的做法

    你想在 View 里获取当前用户?Django 通过中间件把 user 塞进 request 对象里。

    python 复制代码
    def my_view(request):
        user = request.user  # 隐式依赖,你必须知道 request 里有这个

    如果你想复用一段"检查权限"的逻辑,通常用装饰器 (@permission_required)。

  • FastAPI 的做法

    你显式定义一个依赖函数,然后在路由参数里声明它。

    python 复制代码
    # 定义依赖
    async def get_current_user(token: str = Header(...)) -> User:
        # 解析 token 逻辑
        return user
    
    # 使用依赖
    @app.get("/items/")
    async def read_items(current_user: User = Depends(get_current_user)):
        # current_user 自动注入,无需手动调用
        return {"owner": current_user.username}

    优势

    1. 测试极其方便:测试时可以轻松 override(覆盖)这个依赖,返回一个假用户。
    2. 组合性强:可以层层嵌套依赖(A 依赖 B,B 依赖 C),形成清晰的依赖树。
    3. 文档自动化:FastAPI 能分析这些依赖,自动生成 API 文档中的认证要求。

3. 数据验证:运行时 vs 编译时(类型提示)

  • Django :

    主要依赖 Form 类或 Serializer (DRF) 在运行时进行验证。虽然 DRF 很强,但代码量较大,且类型提示支持不如原生好。

  • FastAPI :

    基于 Python Type HintsPydantic

    python 复制代码
    # 直接在参数里定义类型和验证规则
    @app.post("/items/")
    async def create_item(item: ItemSchema): # ItemSchema 继承自 Pydantic
        # 如果请求体不符合 schema,FastAPI 自动返回 422 错误,根本进不到函数内部
        return item

    这种模式让编辑器(IDE)能提供完美的自动补全,并且在代码运行前就能发现很多类型错误。

4. 异步支持 (Async/Await)

  • Django :

    虽然 3.0+ 支持异步 View 和 ORM,但由于历史包袱,整个生态(尤其是中间件和第三方库)是混合的。如果在异步 View 里调用了同步的 ORM 方法,会阻塞事件循环。

  • FastAPI :
    生而异步 。基于 Starlette 和 Pydantic,全链路原生支持 async/await。在高并发 IO 密集型场景(如大量调用外部 API、WebSocket)下,性能表现通常优于 Django。

🧐 架构选择指南:什么时候用哪个?

✅ 选择 Django (MVT) 当:

  1. 你需要快速构建全栈应用:不仅要有 API,还要有后台管理系统(Admin)、HTML 页面、用户认证、邮件发送。Django 一行命令全搞定。
  2. 团队偏好"约定大于配置":希望新人上来就有统一的目录结构和编码规范,不想花时间选型各种库。
  3. 内容驱动型网站:博客、新闻门户、电商、SaaS 平台,业务逻辑复杂且关系紧密。
  4. 不需要极致的并发:普通的 Web 请求,Django 的性能完全够用。

✅ 选择 FastAPI 当:

  1. 纯 API 后端:前端是 Vue/React/Mobile,后端只负责吐 JSON。
  2. 微服务架构:需要将大系统拆分成小服务,每个服务轻量、独立部署。
  3. 高并发/实时应用:聊天室、即时推送、大量调用第三方 AI 接口等 IO 密集型任务。
  4. 机器学习/AI 服务:Python 是 AI 的首选,FastAPI 能很好地封装模型推理接口,且性能损耗小。
  5. 对类型安全和自动文档有强需求

💣 常见误区

  1. "FastAPI 没有 MVC/MVT,所以它没有架构"

    • 真相 :FastAPI 只是没有强制你使用某种架构。你可以在 FastAPI 中手动实现分层架构(Controller-Service-Repository),而且很多人就是这么做的。它给了你自由,但也要求你有更强的架构设计能力。
  2. "Django 太慢了,不能用在生产环境"

    • 真相:对于 95% 的 Web 应用,瓶颈在数据库和网络,而不是框架本身的几毫秒差异。Django 的异步化进展很快,且配合缓存、CDN 后,性能完全不是问题。
  3. "学了 FastAPI 就不用学 Django ORM 了"

    • 真相:FastAPI 本身不带 ORM。你依然需要学习 SQLAlchemy 或 Tortoise ORM,甚至可以在 FastAPI 里直接使用 Django ORM(虽然不常见)。Django ORM 的设计思想依然值得学习。

📝 总结

  • Django MVTOpinionated (有主见) 的。它告诉你:"照我说的做,我能保你衣食无忧。"适合大而全的系统。
  • FastAPIUnopinionated (随和) 的。它告诉你:"我给你最快的引擎和最好的工具,车怎么造看你。"适合小而美高并发的 API 服务。

在 2026 年的今天,两者并不是非此即彼的关系。很多大型架构甚至是 Django 做主业务和管理后台 + FastAPI 做高性能微服务 的混合模式。理解它们的架构差异,能让你在技术选型时更加游刃有余。

02:架构深潜 ------ FastAPI 下的 DDD 与 Django 的 MTV 实战原理

写在前面

接上一讲,我们对比了 Django 的 MVT 和 FastAPI 的灵活路由。但很多同学在真正落地大型项目时会发现:框架只是骨架,架构才是灵魂

简单的 CRUD(增删改查)用哪个框架都差不多,可一旦业务逻辑复杂到像"电商交易链路"或"金融风控系统",代码结构就会决定项目的生死。

今天这一讲,我们不写一行具体代码,而是从设计原理组织哲学层面,深度剖析:

  1. Django 是如何在 MTV(Model-Template-View) 模式下演进以应对复杂业务的?
  2. FastAPI 是如何天然契合 DDD(领域驱动设计) 思想的?
  3. 两者在分层策略依赖方向业务逻辑归属上的本质区别。

🏛️ 第一部分:Django 的 MTV 架构 ------ 从"大泥球"到"实用主义分层"

Django 官方推崇的是 MTV (Model-Template-View),这其实是 MVC 的变种。但在 DDD(领域驱动设计)的语境下,原生 Django 结构往往面临挑战。

1. 原生 MTV 的核心逻辑

  • Model (模型) : 数据的唯一真理。包含数据库字段定义、基础验证逻辑,甚至部分业务方法(如 user.has_permission())。
  • Template (模板): 纯展示层,负责 HTML 渲染。
  • View (视图): 控制器角色。接收请求,调用 Model,选择 Template。

2. 面对复杂业务时的"痛点"

在 DDD 理念中,我们希望**业务逻辑(Domain Logic)**独立于框架(Framework Agnostic)。

但在原生 Django 中:

  • Model 太重:为了复用逻辑,开发者倾向于把所有业务规则都塞进 Model 方法里,导致 Model 变得臃肿不堪,且强耦合 Django ORM。
  • View 太乱:复杂的业务流程(如"下单 -> 扣库存 -> 计算优惠 -> 创建订单 -> 发送通知")如果全写在 View 函数里,会导致 View 变成几千行的"上帝函数"。
  • 测试困难:因为业务逻辑散落在 Model 和 View 中,且深度依赖 Django 的环境(Request, ORM),单元测试很难在不启动 Django 环境的情况下运行。

3. Django 下的"伪 DDD"演进策略

虽然 Django 不强制 DDD,但社区在大型项目中形成了一套约定俗成的分层模式,用来在 MTV 框架内实现 DDD 的思想:

  • 引入 Service 层 (服务层)

    将复杂的业务逻辑从 View 和 Model 中剥离出来,放入 services.pyservices/ 目录。View 只负责流程调度,Service 负责核心业务规则。

  • 引入 Domain 层 (领域层)

    在极复杂的项目中,会单独创建一个 domain/ app。这里存放纯粹的 Python 类(实体 Entity、值对象 Value Object),完全不继承 django.db.models.Model

    • 原理:核心业务逻辑操作这些纯 Python 对象。
    • 适配 :通过 Repository 模式(通常在 repositories.py)将这些纯对象与 Django ORM 模型进行转换。
  • 依赖倒置的妥协

    原生 Django 是"自底向上"的(Model 定义好,View 去用)。而在 DDD 实践中,需要人为地通过接口(Interface/Abstract Base Class)来解耦,让上层业务不直接依赖下层的具体 ORM 实现。

核心总结 :Django 的 MTV 是一种框架优先 的架构。要在其中实现 DDD,需要开发者主动克制框架的便利性,人为地增加抽象层(Service, Repository, Domain Entities),这是一种"带着镣铐跳舞"的艺术。

🚀 第二部分:FastAPI 下的 DDD 架构 ------ 天生一对的"自由组合"

FastAPI 本身没有预设的架构模式(没有强制的 Model 或 View 概念),这反而让它成为了实施 DDD (领域驱动设计) 的绝佳土壤。

1. FastAPI 与 DDD 的天然契合点

DDD 的核心诉求是:业务逻辑核心化,基础设施边缘化

FastAPI 的两大特性完美支持这一点:

  • 依赖注入 (Dependency Injection):可以清晰地界定"基础设施"(如数据库会话、当前用户、外部 API 客户端)和"业务逻辑"。
  • Pydantic 模型:作为数据传输对象 (DTO),与领域模型 (Domain Model) 天然分离。

2. 标准的 FastAPI + DDD 分层架构原理

在 FastAPI 实践中,通常会严格遵循 整洁架构 (Clean Architecture)六边形架构 (Hexagonal Architecture),分为以下四层:

A. 领域层 (Domain Layer) ------ 核心中的核心
  • 位置src/domain/
  • 内容
    • Entities (实体) : 纯粹的业务对象,包含核心业务规则。不依赖任何第三方库(无 SQLAlchemy, 无 FastAPI, 无 Pydantic)。
    • Value Objects (值对象) : 不可变的业务概念(如 EmailAddress, Money)。
    • Repository Interfaces (仓储接口) : 只定义抽象方法(如 get_by_id, save),不包含具体实现
    • Domain Services: 跨实体的复杂业务逻辑。
  • 原则:这一层是纯 Python 代码,甚至可以脱离 Web 框架独立运行和测试。
B. 应用层 (Application Layer) ------ 用例编排
  • 位置src/application/
  • 内容
    • Use Cases (用例) : 定义具体的业务操作(如 CreateOrderUseCase)。
    • DTOs: 定义输入输出的数据结构。
  • 职责:它调用领域层的实体和接口,编排业务流程,但不关心数据具体存在哪里(MySQL 还是 Mongo)。
C. 基础设施层 (Infrastructure Layer) ------ 具体实现
  • 位置src/infrastructure/
  • 内容
    • ORM Models: SQLAlchemy 或 Tortoise 的具体模型。
    • Repository Implementations: 实现领域层定义的接口,真正去操作数据库。
    • External Services: 调用微信支付的客户端、发送邮件的实现。
  • 原则:这一层依赖领域层(依赖倒置),将具体技术细节注入到上层。
D. 表现层 (Presentation / Interface Layer) ------ FastAPI 的主场
  • 位置src/api/main.py
  • 内容
    • Routers : 定义 HTTP 路径 (@app.get)。
    • Schemas: Pydantic 模型,用于请求验证和响应序列化。
    • Dependencies: 将基础设施层的实例(如 DB Session, Repo Impl)注入到路由函数中。
  • 职责:只负责处理 HTTP 协议、参数验证、异常捕获,然后调用应用层的 Use Case。

3. 依赖流向:箭头向内

在 FastAPI + DDD 架构中,依赖关系是严格单向的:
表现层 -> 应用层 -> 领域层 <- 基础设施层
(注意:基础设施层依赖领域层,而不是反过来)

这意味着:核心业务逻辑(领域层)根本不知道 FastAPI 的存在,也不知道数据库是 MySQL 还是 PostgreSQL。

⚔️ 第三部分:深度对比 ------ 两种哲学的碰撞

维度 Django (MTV + 演进) FastAPI (DDD 原生风格)
架构驱动力 框架驱动。结构由 Django 的 App 机制决定。 领域驱动。结构由业务边界 (Bounded Context) 决定。
业务逻辑位置 容易分散在 Model 方法和 View 函数中。需刻意提取到 Service 层。 严格集中在 Domain EntitiesUse Cases 中。
数据模型 ORM Model 即业务模型。通常混用,难以分离。 严格分离。Pydantic (DTO) != SQLAlchemy (DB) != Domain Entity (业务)。
依赖方向 通常是自上而下 (View -> Model)。难以反转。 依赖倒置。高层模块不依赖低层模块,都依赖抽象。
测试性 通常需要 pytest-django,加载整个框架环境。 核心领域逻辑可纯单元测试,无需任何 Web 或 DB 环境。
灵活性 较低。修改底层存储(如换 NoSQL)成本高,因为 Model 耦合紧。 极高。只需替换 Infrastructure 层的实现,核心业务代码无需改动。
上手难度 低。跟着教程做就能跑起来。 高。需要理解抽象、接口、依赖注入等设计模式。
适用规模 中小型项目、内容型网站、快速原型。 大型复杂系统、微服务、高迭代频率的核心业务。

💡 核心原理总结

1. Django 的"实用主义"

Django 的设计初衷是效率。它假设你的业务逻辑和数据库结构是紧密绑定的。

  • 优点:开发速度极快,代码量少, conventions(约定)减少了决策成本。
  • 代价:随着业务复杂度指数级上升,MTV 的三层结构容易崩塌,需要开发者具备很强的重构能力,人为地引入 DDD 概念来"救火"。

2. FastAPI 的"自由主义"

FastAPI 的设计初衷是性能与规范。它不提供业务结构,只提供工具。

  • 优点:天生适合 DDD。你可以从一开始就构建一个清晰、可测试、易维护的领域模型。基础设施的变更不会影响核心业务。
  • 代价:你需要自己搭建所有脚手架(目录结构、依赖注入配置、单元边界)。如果没有良好的架构设计能力,很容易把 FastAPI 写成"散弹枪代码"(所有逻辑都在路由函数里)。

3. 关键分歧点:贫血模型 vs 充血模型

  • Django 常见模式贫血模型 (Anemic Domain Model)。Model 主要是数据容器(Getter/Setter),逻辑都在外部的 Service 或 View 里。
  • DDD 理想模式 (FastAPI 易实现)充血模型 (Rich Domain Model) 。Entity 对象内部包含行为(如 order.cancel()),对象自己维护自己的状态不变性。

📝 结语

  • 如果你正在做一个内容管理系统、博客、或者初创公司的 MVP,Django 的 MTV 模式(即使稍微混合一点 Service 层)是最高效的选择。不要为了 DDD 而 DDD,过度设计是万恶之源。
  • 如果你正在构建核心交易系统、复杂的 SaaS 平台、或者需要长期演进且业务规则多变的微服务 ,那么采用 FastAPI + 严格 DDD 分层 是更明智的投资。虽然前期搭建成本高,但随着时间推移,其维护成本和扩展优势会呈指数级显现。

架构没有银弹,只有权衡 (Trade-off)。

03:实战深潜 ------ 文件上传与异步解析的架构对决

前情提要

在上一篇中,我们厘清了 Django (MTV) 与 FastAPI (DDD/现代架构) 的设计哲学。

理论总是灰色的,而生命之树常青。今天,我们选取一个极具代表性的"试金石"场景:"用户上传一个大文件(如 CSV/Excel),系统需要在后台异步解析、校验数据,并实时反馈进度"

这个场景完美覆盖了 Web 开发的几个核心痛点:

  1. 大文件 I/O:如何处理二进制流而不阻塞内存?
  2. 耗时计算:解析万行数据,绝不能让 HTTP 请求挂起。
  3. 异步解耦:如何把"接收"和"处理"分开?
  4. 状态追踪:用户怎么知道处理完了没?

让我们看看,在 Django (MTV + Celery)FastAPI (DDD + Native Async/BackgroundTasks) 两种架构下,这套流程是如何被拆解和实现的。


🎯 场景定义:核心挑战

假设我们要实现一个功能:

  • 输入:用户 POST 一个 50MB 的 Excel 文件。
  • 处理
    1. 保存文件。
    2. 异步读取内容,逐行校验业务规则(耗时操作)。
    3. 将合法数据写入数据库,记录错误日志。
  • 输出
    • 接口立即返回 task_id
    • 用户可通过轮询或 WebSocket 获取进度(0% -> 100%)和结果。

🐘 方案一:Django 架构 (MTV + Celery)

"重型坦克"模式:稳健、隔离、但略显笨重

在 Django 的世界里,处理耗时任务的标准答案几乎是唯一的:Celery。因为 Django 的核心(ORM, Middleware)主要是同步阻塞的,如果在 View 里直接跑循环解析,整个工作线程就会被卡死,无法响应其他请求。

1. 架构流向

复制代码
User -> [Django View (MTV)] -> [Redis/RabbitMQ] -> [Celery Worker (独立进程)] -> [DB]
       ^                           |
       |______ (Polling API) ______|

2. 核心组件职责

A. Model (数据层)

我们需要一个"任务状态表"来解耦请求和处理。

  • TaskLog Model : 存储 task_id, status (PENDING, STARTED, SUCCESS), progress (int), error_message
  • 作用:这是 View 和 Celery 任务之间的"共享内存",用于传递状态。
B. View (控制层)

View 在这里非常"薄",它只做两件事:

  1. 接收 request.FILES,将文件保存到临时存储(本地或 S3)。
  2. 调用 Celery 任务:parse_task.delay(file_path, user_id)
  3. 创建 TaskLog 记录,立即返回 task_id 给前端。
  • 关键点 :View 绝不触碰解析逻辑。它只是一个"发令枪"。
C. Task (异步层 - Celery)

这是真正的"业务逻辑"所在地,通常位于 tasks.py

  • 独立性 :它是一个完全独立的 Python 进程,不依赖 HTTP 请求上下文(没有 request 对象)。
  • 流程
    1. 加载文件。
    2. 循环解析(每处理 100 行,更新一次 TaskLog 表的 progress 字段)。
    3. 捕获异常,更新状态为 FAILURE。
    4. 完成后更新状态为 SUCCESS。
  • 架构特点
    • 强隔离:即使解析代码崩溃(Segfault),只会挂掉一个 Worker 进程,主 Web 服务毫发无损。
    • 可靠性:Celery 提供重试机制(Retry)、ACK 确认,消息不会丢失。

3. Django 方案的痛点

  • 运维复杂:你需要额外部署 Redis/RabbitMQ 作为 Broker,还要单独运行 Celery Worker 进程,甚至需要 Flower 来监控。
  • 调试割裂:断点调试时,你必须在 Web 进程和 Worker 进程之间切换,上下文不连贯。
  • 状态同步压力 :为了显示进度条,任务需要频繁 save() 数据库,高并发下可能造成 DB 写压力(通常需要引入节流逻辑)。

⚡ 方案二:FastAPI 架构 (DDD + Native Async)

"极速跑车"模式:轻量、内聚、极致并发

FastAPI 基于 asyncio,天生具备处理高并发 I/O 的能力。对于文件解析这种 I/O 密集型任务,我们可以利用 BackgroundTasks (轻量级) 或 Arq/Celery (重量级)。在 DDD 架构下,我们倾向于先利用语言特性保持代码内聚。

1. 架构流向 (轻量级原生方案)

复制代码
User -> [FastAPI Router] -> [Event Loop] -> [BackgroundTasks] -> [Domain Service] -> [DB]
       ^                           |
       |______ (WebSocket) ________|

(注:如果是超大规模集群,FastAPI 也可以对接 Celery,但这里展示其原生优势)

2. 核心组件职责 (DDD 分层)

A. Domain Layer (领域层)
  • Service : FileParsingService
    • 核心逻辑 :这是一个纯异步函数 (async def parse(...))。
    • 非阻塞 I/O :使用 aiofiles 异步读取文件,避免阻塞事件循环。
    • 状态回调:Service 不直接操作 DB,而是通过回调或事件总线通知应用层更新状态。
B. Infrastructure Layer (基础设施层)
  • Repository : JobRepository (使用 SQLAlchemy Async 或 Tortoise ORM)。
    • 提供 update_progress(job_id, percent) 方法,这是非阻塞的 await repo.update(...)
C. Interface Layer (FastAPI Router)
  • Endpoint : POST /upload

    • 接收 UploadFile
    • 关键语法 :利用 FastAPI 的依赖注入注入 background_tasks
    python 复制代码
    @app.post("/upload")
    async def upload_file(
        file: UploadFile, 
        background_tasks: BackgroundTasks, # 自动注入
        current_user: User = Depends(get_current_user)
    ):
        job = await repo.create_job(user=current_user)
        # 将耗时任务加入后台队列,立即返回响应
        # 注意:这里传入的是协程函数或普通函数
        background_tasks.add_task(domain_service.parse_and_save, job.id, file)
        return {"job_id": job.id}
  • 实时反馈

    • 配合 WebSocket (@app.websocket("/ws/{job_id}"))。
    • 在解析过程中,Service 可以通过 WebSocket 管理器直接推送进度给前端,无需前端轮询数据库,体验极佳。

3. FastAPI 方案的痛点

  • 内存与进程隔离弱BackgroundTasks 运行在主进程的 Event Loop 中。如果解析代码发生严重错误(如 C 扩展库崩溃),可能会拖垮整个 API 服务。
  • CPU 密集型瓶颈:虽然 I/O 是异步的,但 Python 的 GIL 限制了解析(CPU 计算)的并行度。如果是纯 CPU 计算(如复杂加密),仍需卸载到多进程或外部 Worker。
  • 无内置重试 :原生的 BackgroundTasks 没有 Celery 那种"失败自动重试 3 次"的机制,需要自己写 try-except 逻辑。

⚔️ 深度对比:架构决策的关键点

维度 Django (MTV + Celery) FastAPI (DDD + Async)
并发模型 多进程/多线程。依赖 OS 调度,上下文切换开销大,但能利用多核 CPU。 单线程协程 (Event Loop)。用户态切换,极高并发效率,但受 GIL 限制 CPU 并行。
代码风格 同步阻塞风格。逻辑被物理分割在 View 和 Task 两个文件中。 异步流式风格 。从路由到 Service 全是 async/await,类型提示完整,链路清晰。
依赖复杂度 。需维护 Broker (Redis), Worker, Monitor。 。原生支持,仅需 aiofiles 等库,零额外组件。
状态管理 数据库轮询为主。频繁 Update 表,压力大。 WebSocket 推送 + 内存状态。实时性更好,DB 压力小。
错误隔离 。Worker 挂了不影响 Web 服务。 。任务崩溃可能波及主进程(需小心捕获异常)。
适用规模 企业级/超大规模。任务需要持久化、重试、定时、分布式调度。 中高并发/实时性要求高。任务生命周期短,追求极致响应速度。
DDD 契合度 。Task 文件往往变成新的"上帝脚本",难以融入领域层。 。异步服务天然可注入领域层,依赖关系清晰。

💡 最佳实践建议

1. 什么时候坚持用 Django + Celery?

  • 任务执行时间 > 5 分钟
  • 任务需要 精确的重试策略(如:失败后指数退避重试 3 次)。
  • 需要 定时任务(如:每天凌晨解析昨天的所有文件)。
  • 场景:银行日终结算、大规模报表生成、视频转码。
  • 理由 :你需要的是可靠性资源隔离,而不是极致的响应速度。

2. 什么时候选择 FastAPI + Native Async?

  • 任务执行时间 < 2 分钟
  • 实时反馈 有强需求(如:上传即刻看到解析进度条跳动)。
  • 并发量极大(每秒数百个上传请求),但单个任务计算量不大(主要是 I/O)。
  • 希望架构轻量化,减少运维组件(不想维护 Redis 集群)。
  • 场景:用户头像上传并裁剪、小型 CSV 导入、实时日志分析、AI 推理接口。
  • 理由 :你需要的是用户体验开发效率

3. 混合模式 (The Best of Both Worlds)

在 2026 年的大型架构中,我们经常看到:

  • FastAPI 作为网关和实时交互层,处理文件接收和 WebSocket 推送。
  • 对于超重任务,FastAPI 将消息投递到 RabbitMQ ,由后端的 Celery Worker 消费。
  • 这样既利用了 FastAPI 的高并发 I/O 优势,又保留了 Celery 的可靠任务调度能力。

📝 总结

  • Django 的方案工业化的:稳重、可靠、组件化,但略显笨重,适合"稳扎稳打"的大后方处理。它把"异步"当作一个独立的基础设施问题来解决。
  • FastAPI 的方案敏捷化的:轻快、流畅、语言级支持,适合"唯快不破"的前线交互。它把"异步"当作语言特性融入到业务逻辑的每一行代码中。

在处理"文件上传与解析"这个具体任务时:

  • 如果你追求开发速度系统稳定性,且任务不急,Django + Celery 是老牌劲旅。
  • 如果你追求用户体验 (实时进度)和资源利用率,FastAPI + Async 是现代首选。

04:混合架构实战 ------ FastAPI + Celery 的"黄金搭档"模式

前情提要

在上一篇中,我们对比了 Django 的"重型坦克"模式和 FastAPI 的"极速跑车"模式。

但现实世界往往不是非黑即白的。当我们用 FastAPI 构建了高并发的 API 网关,却面临超长耗时任务 (如解析 1GB 的基因测序文件、训练小型 AI 模型)时,原生的 BackgroundTasks 显得力不从心(缺乏重试、监控、持久化)。

这时候,我们需要引入 Celery 这位"分布式任务处理专家"。

核心问题:FastAPI 是异步的(Async),Celery 传统上是同步的(Sync)。两者结合会不会"水土不服"?代码结构该如何组织才能既保持 FastAPI 的清爽,又拥有 Celery 的稳健?

今天,我们就以 "文件上传与异步解析" 为例,手把手拆解 FastAPI + Celery 的混合架构实现方案。


🏗️ 架构全景图:各司其职

在这个混合模式中,三个组件分工明确:

  1. FastAPI (The Receiver) :

    • 职责 :接收 HTTP 请求,验证文件,将文件保存到持久化存储(S3/本地),立即向消息队列(Redis/RabbitMQ)发送一个"任务消息",然后秒回响应给前端。
    • 特点:极快,不阻塞,利用异步优势处理高并发上传。
  2. Message Broker (The Postman) :

    • 角色:Redis 或 RabbitMQ。
    • 职责:暂存任务消息,确保任务不丢失,负责负载均衡。
  3. Celery Worker (The Processor) :

    • 职责:监听队列,获取任务,执行耗时的文件解析逻辑,更新数据库状态,发送通知。
    • 特点:独立进程,可横向扩展,支持重试、定时、监控。
  4. 上传文件 2. 保存文件到 S3/磁盘 3. 发送任务 ID 4. 消费任务 5. 读取文件 6. 解析并更新状态 7. 轮询/WS 获取进度 8. 查询 DB 用户
    FastAPI App
    存储系统
    Redis/RabbitMQ
    Celery Worker
    数据库

💻 核心代码实现原理

我们将采用 DDD 分层思想 来组织代码,避免把逻辑全塞进一个文件。

1. 项目结构建议

text 复制代码
src/
├── api/                # FastAPI 路由层
│   ├── routes.py
│   └── schemas.py      # Pydantic 模型
├── core/               # 配置 (Celery config, DB config)
│   └── celery_app.py   # Celery 实例初始化
├── domain/             # 领域逻辑 (纯 Python)
│   └── services.py     # 文件解析的核心算法
├── infrastructure/     # 基础设施
│   ├── models.py       # SQLAlchemy 模型 (TaskLog)
│   └── storage.py      # 文件读写操作
└── tasks.py            # Celery 任务定义 (连接层)

2. 第一步:配置 Celery (core/celery_app.py)

这是连接 FastAPI 和 Worker 的桥梁。注意,我们需要在 FastAPI 启动时也能访问到这个 celery_app 实例。

python 复制代码
from celery import Celery
from src.core.config import settings

# 初始化 Celery
celery_app = Celery(
    "worker",
    broker=settings.CELERY_BROKER_URL,  # 如 redis://localhost:6379/0
    backend=settings.CELERY_RESULT_BACKEND # 如 redis://localhost:6379/1
)

# 加载配置
celery_app.conf.update(
    task_serializer="json",
    accept_content=["json"],
    result_serializer="json",
    timezone="UTC",
    enable_utc=True,
    # 关键配置:允许在 FastAPI 主进程中调用 delay 时不需要 event loop 阻塞
    task_always_eager=False, 
)

3. 第二步:定义 Celery 任务 (tasks.py)

这里是同步代码 的世界。Celery Worker 默认以同步方式运行任务(虽然 Celery 4.0+ 支持 async 任务,但在混合架构中,为了稳定性和库兼容性,通常建议在 Worker 端使用同步代码,或者使用 asyncio.run 包裹异步逻辑)。

python 复制代码
from src.core.celery_app import celery_app
from src.domain.services import FileParserService
from src.infrastructure.models import TaskLog, get_db_session
from src.infrastructure.storage import read_file_path

@celery_app.task(bind=True, max_retries=3)
def process_file_task(self, file_path: str, task_log_id: int):
    """
    Celery 任务:执行耗时的文件解析
    bind=True: 允许访问 task 实例本身 (用于 retry 和 update_state)
    """
    db = next(get_db_session())
    try:
        # 1. 更新状态为 STARTED
        task_log = db.query(TaskLog).get(task_log_id)
        task_log.status = "STARTED"
        db.commit()

        # 2. 执行核心业务逻辑 (同步阻塞操作,但在独立进程中不影响 FastAPI)
        # 假设 parser 是一个同步的重型库 (如 pandas, openpyxl)
        parser = FileParserService()
        
        # 模拟进度回调
        def on_progress(percent, message):
            task_log.progress = percent
            task_log.message = message
            db.commit()
            # 更新 Celery 自带状态 (可选,用于 inspect)
            self.update_state(state="PROGRESS", meta={"current": percent})

        result = parser.parse(file_path, callback=on_progress)

        # 3. 成功处理
        task_log.status = "SUCCESS"
        task_log.result_data = result
        db.commit()
        return {"status": "success", "rows_processed": len(result)}

    except Exception as exc:
        # 4. 异常处理与重试
        task_log.status = "FAILURE"
        task_log.error_message = str(exc)
        db.commit()
        # 指数退避重试
        raise self.retry(exc=exc, countdown=60) 

4. 第三步:FastAPI 路由层 (api/routes.py)

这里是异步代码的世界。FastAPI 只需要负责"点火",然后立即返回。

python 复制代码
from fastapi import APIRouter, UploadFile, File, BackgroundTasks, HTTPException
from src.core.celery_app import celery_app
from src.tasks import process_file_task
from src.infrastructure.models import TaskLog, create_task_log
from src.infrastructure.storage import save_upload_file
from src.api.schemas import TaskResponse

router = APIRouter()

@router.post("/upload", response_model=TaskResponse)
async def upload_file(file: UploadFile = File(...)):
    """
    1. 接收文件
    2. 保存文件到磁盘/S3 (异步 IO 优化)
    3. 创建数据库记录
    4. 触发 Celery 任务
    5. 立即返回 task_id
    """
    # A. 保存文件 (使用 aiofiles 进行异步写入,不阻塞事件循环)
    file_path = await save_upload_file(file)
    
    # B. 创建任务日志记录 (Pending 状态)
    # 注意:这里需要异步 ORM (如 SQLAlchemy Async 或 Tortoise)
    task_log = await create_task_log(filename=file.filename, path=file_path)
    
    # C. 触发 Celery 任务
    # .delay() 是 .apply_async() 的快捷方式,它将消息推送到 Broker,立即返回
    # 这是一个非阻塞操作 (取决于 Broker 客户端,Redis 客户端通常是异步友好的)
    process_file_task.delay(file_path=file_path, task_log_id=task_log.id)
    
    # D. 立即响应
    return {
        "task_id": task_log.id,
        "status": "pending",
        "message": "File uploaded. Processing started in background."
    }

@router.get("/task/{task_id}")
async def get_task_status(task_id: int):
    """
    前端轮询此接口获取进度
    """
    # 查询数据库中的 TaskLog
    task_log = await get_task_log_by_id(task_id)
    if not task_log:
        raise HTTPException(status_code=404, detail="Task not found")
    
    return {
        "task_id": task_log.id,
        "status": task_log.status,
        "progress": task_log.progress,
        "result": task_log.result_data if task_log.status == "SUCCESS" else None,
        "error": task_log.error_message if task_log.status == "FAILURE" else None
    }

⚙️ 关键难点与解决方案

1. 异步 (FastAPI) 与 同步 (Celery) 的摩擦

  • 问题 :FastAPI 是 async def,而 Celery 任务通常是 def
  • 解决
    • FastAPI 端 :调用 task.delay() 只是发送消息,这个动作非常快,即使是同步的 Redis 客户端也不会造成明显阻塞。如果使用 aioredis 等异步客户端更佳。
    • Celery 端 :保持任务是同步的。因为 Worker 是独立进程,阻塞只会影响当前 Worker,不会卡死 FastAPI 主服务。如果在任务内部必须调用异步库(如 httpx 异步版),可以使用 asyncio.run() 包裹。

2. 文件传递方式

  • 误区 :试图把整个文件内容作为参数传给 Celery 任务。
    • process_file_task.delay(file_content=...) ❌ (消息队列爆炸,性能极差)
  • 最佳实践只传文件路径 (Path/URL)
    • FastAPI 先把文件存到共享存储(本地磁盘/NFS/S3)。
    • Celery 任务根据路径去读取文件。
    • 这样消息队列里只有几十字节的元数据,极其轻量。

3. 状态同步机制

  • 方案 A (推荐)数据库作为单一事实来源 (Single Source of Truth)
    • Celery 任务直接更新 DB 中的 TaskLog 表。
    • FastAPI 接口只查 DB。
    • 优点:解耦,即使 Celery 挂了,状态也在 DB 里;支持多 Worker 并发更新。
  • 方案 B (Celery Backend) :使用 Celery 的 backend (Redis) 存储结果。
    • 缺点 :不适合存储复杂的进度条信息(需要频繁序列化/反序列化),且查询不如 SQL 灵活。通常只用于获取最终 resultstate

4. 优雅关闭与信号处理

  • 当服务器重启时,正在进行的任务怎么办?
  • Celery 配置 :设置 task_acks_late=Trueworker_prefetch_multiplier=1
    • 这意味着:只有任务彻底完成后,Broker 才会确认删除消息。如果 Worker 中途挂掉,消息会重新回到队列,被其他 Worker 拾取重试。

🚀 进阶优化:如何让体验更丝滑?

1. 实时推送 (WebSocket + Celery)

轮询(Polling)太浪费资源了。我们可以结合 WebSocket:

  • 架构
    1. Celery 任务完成后(或每更新 10% 进度),向 Redis 发布一个消息 (redis.publish('task_updates', ...)).
    2. FastAPI 中有一个后台任务监听 Redis 频道。
    3. 一旦收到消息,FastAPI 通过 WebSocket 主动推送给前端。
  • 效果:前端进度条实时跳动,无需每秒请求一次 HTTP 接口。

2. 动态扩缩容

  • 既然是混合架构,Celery Worker 可以独立部署。
  • 在 Kubernetes 中,可以根据 Redis 队列长度 (redis.llen('celery')) 自动增加或减少 Worker Pod 的数量。
  • FastAPI 服务专注于接收入口流量,Worker 集群专注于计算,互不干扰。

3. 依赖注入的妙用

在 FastAPI 中,你可以编写一个 Depends(get_celery_task_status),直接在路由参数中获取任务状态,甚至拦截请求(如果任务失败,直接返回错误,不再执行后续逻辑)。

📝 总结:为什么这是"Best of Both Worlds"?

特性 纯 FastAPI (BackgroundTasks) 纯 Django (Celery) FastAPI + Celery (混合)
响应速度 极快 (FastAPI 处理 IO 无敌)
任务可靠性 低 (进程挂了就没了) 极高 (重试、持久化) 极高 (继承 Celery 所有优势)
开发体验 简单,但缺乏监控 繁琐,配置多 平衡 (FastAPI 写接口爽,Celery 写逻辑稳)
资源隔离 无 (阻塞主线程) (独立进程) (计算压力完全剥离)
适用场景 小文件,秒级任务 传统企业应用 高并发上传 + 重型计算 (AI, 大数据)

核心心法

让 FastAPI 做它最擅长的:高并发 I/O、协议解析、实时通信

让 Celery 做它最擅长的:分布式调度、可靠执行、耗时计算

两者通过 消息队列共享存储 握手,通过 数据库 同步状态。

相关推荐
bug攻城狮2 小时前
SpringBoot 脚手架搭建指南:从零构建企业级开发框架
java·spring boot·后端·架构·系统架构·设计规范
大傻^2 小时前
【Spring AI -01】Spring AI 2.0 架构重构深度解析:从单体核心到模块化领域的演进
人工智能·spring·架构·spring ai·模块化设计·java 21·架构迁移
猹叉叉(学习版)2 小时前
【ASP.NET CORE】 12. DDD基本概念
笔记·后端·架构·c#·asp.net·.netcore
請你喝杯Java2 小时前
OpenClaw 架构解析
架构
Dfreedom.2 小时前
神经网络架构全景图:分类、演进与对比分析
神经网络·架构·分类
Franciz小测测3 小时前
基于FastAPI的自动化随机初始密码方案
运维·自动化·fastapi
三掌柜6663 小时前
TypeScript+React 全栈生态实战:从架构选型到工程落地,告别开发踩坑
react.js·架构·typescript
Smoothcloud润云3 小时前
告别 Selenium:Playwright 现代 Web 自动化测试从入门到实战
前端·人工智能·selenium·测试工具·架构·自动化
乾元3 小时前
API 安全: 保护 AI 应用的交互接口
网络·人工智能·安全·web安全·机器学习·架构·安全架构