ORJSONResponse 确实比 FastAPI 默认的 JSONResponse 快很多,通常被称为目前 Python 生态中最快的 JSON 序列化方案。

以下是它为什么快的核心原因(底层原理拆解):
1. 核心差异:跳过了“字符串编码”这一步 (最关键)#
这是 orjson 能够秒杀标准库 json 的最大杀手锏。
- 标准
json(默认):- 把 Python 对象转换成 Python 字符串 (str)。
- Web 服务器(Uvicorn/Starlette)把字符串 编码 (Encode) 成 字节 (bytes) (通常是 UTF-8)。
- 通过网络发送字节。
- 缺点: 只要是 Web 传输,最终必须是 bytes。先转成 str 再转成 bytes 是多余的步骤,且 Python 的字符串在内存中开销很大。
orjson:- 把 Python 对象 直接 序列化成 UTF-8 字节 (bytes)。
- 通过网络发送字节。
- 优势: 少了一次全量的内存拷贝和编码转换。对于几 MB 的大 JSON 来说,这一步能节省大量的 CPU 时间。
2. 实现语言:Rust vs C/Python#
- 标准
json: 虽然也是 C 语言写的,但它是为了通用性和兼容性设计的,历史包袱重,很多逻辑还需要回调 Python 解释器。 orjson: 是用 Rust 编写的。- 它利用了 Rust 极致的内存管理安全性。
- 使用了 SIMD (单指令多数据流) 技术来加速解析和生成。
- 它是专门为“速度”而生的,牺牲了一些极少用到的灵活性,换取了极致的性能。
3. 原生支持复杂类型 (无需回调)#
这是处理数据库数据时快的主要原因。
- 标准
json: 遇到datetime(时间)、numpy数组、UUID等类型时,它不知道怎么处理。你必须提供一个default函数(比如str(obj)),这会导致每遇到一个时间字段,都要切回 Python 代码执行一次函数调用,开销极大。 orjson: 原生内置 了对datetime(RFC 3339 格式)、numpy、UUID、dataclasses的支持。它在 Rust 层面直接处理这些类型,不需要切回 Python,速度有数量级的提升。
性能差距有多少?#
- 微基准测试 (Micro-benchmarks): 序列化纯数据,
orjson通常比标准库快 5倍 到 10倍。 - 实际 Web 场景:
- 如果你的接口只返回
{"msg": "ok"},那没区别(因为瓶颈在网络 IO)。 - 如果你的接口返回一个 几千行的列表(比如查询数据库返回 1000 条用户记录,包含时间字段),使用
ORJSONResponse可能会让整个接口响应时间缩短 20% - 50%。
- 如果你的接口只返回
python
pip install orjson
#配置
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
# 设置为全局默认,所有路由都会自动享受加速
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items")
def read_items():
# 这里返回大列表时,速度会飞快
return [{"id": i, "name": f"Item {i}", "created_at": "2024-01-01"} for i in range(5000)]
1. 标准库 json 的做法:层层扒皮#
假设你要把一个 Python 字典 { "name": "Jacin" } 发给前端。标准库是这么干的:
- Python 对象:拿到字典。
- 翻译成 Python 字符串 (最慢的一步):
- 它会在内存里创建一个巨大的 Python
str对象。 - Python 的字符串在内存里是非常“重”的(为了支持 Unicode,它有很多元数据)。
- 结果:
u'{"name": "Jacin"}'(存在内存里)。
- 编码成字节 (Encode):
- 网络传输不能传 Python 对象,只能传 0101 的字节。
- 所以它必须把上面那个巨大的字符串,再进行一次
UTF-8编码。 - 结果:
b'{"name": "Jacin"}'。
- 发送。
缺点: 脱裤子放屁。中间那个“Python 字符串对象”不仅占内存,而且创建和销毁它非常耗时。
2. orjson 的做法:直达终点#
orjson 是用 Rust 写的,它极其“鸡贼”:
- Python 对象:拿到字典。
- 直接写内存 (Rust):
- 它跳过了 Python 的
str系统。 - 它直接在内存里开辟一块空间,按照 JSON 规范,把
{ "name": "Jacin" }的 UTF-8 字节码 填进去。 - 结果:直接拿到
b'{"name": "Jacin"}'。
- 发送。
优势: 少了一次“全量数据”的内存拷贝和格式转换。 如果你的 JSON 有 10MB 大:
-
标准库:先造 10MB 的字符串对象 -> 再转成 10MB 的字节流 -> 消耗 20MB 内存 + CPU 转换时间。
-
orjson:直接造 10MB 字节流 -> 消耗 10MB 内存 -> 结束。 B. 避免“回调 Python” (No GIL)
-
标准库:遇到
datetime时间格式,它不懂,它得停下来,去问 Python 解释器:“嘿,这个时间对象怎么转字符串?”(这涉及到 Python 函数调用,很慢)。 -
orjson:它在 Rust 代码里写死了对
datetime的支持。它看到时间对象,直接在 Rust 层面就给格式化了,根本不通知 Python。这不仅快,还减少了 GIL(全局解释器锁)的争用。
评论
还没有评论,来发第一个吧
