Sub2API 源码技术分析与搭建教程:把 AI 订阅变成可管理的 API 网关

面向读者:刚接触后端、网关、AI API 中转的新手。 阅读目标:看完你应该知道 Sub2API 是什么、怎么跑起来、源码怎么分层、一次请求在系统里经历了什么,以及它为什么能做账号调度、鉴权、计费和限流。

1. 一句话理解这个项目

Sub2API 不是一个普通的"转发接口"。它更像一个面向 AI 服务的 SaaS 网关:管理员把上游 AI 账号或 API Key 接入系统,用户拿到平台生成的 API Key 后调用统一入口,系统在中间完成鉴权、选择账号、转发请求、记录用量、扣费、限流、监控和后台管理。

如果用生活化比喻:

  • 上游账号像"批发来的 AI 调用额度"。
  • 平台 API Key 像"发给用户的门票"。
  • 网关像"检票员 + 调度员 + 记账员"。
  • 管理后台像"老板看的控制台"。

官方 README 对它的定位是:AI API Gateway Platform for Subscription Quota Distribution,也就是"用于订阅配额分发管理的 AI API 网关平台"。

2. 技术栈速览

从仓库可以看出,这是一个前后端分离但可打包成单体镜像的项目。

层级 技术 作用
后端 Go、Gin、Ent、Wire HTTP 服务、业务逻辑、数据库 ORM、依赖注入
前端 Vue 3、Vite、Pinia、Vue Router、TailwindCSS 管理后台、用户中心、设置向导
数据库 PostgreSQL 保存用户、账号、API Key、订阅、计费、日志等核心数据
缓存/队列 Redis 限流、缓存、会话、异步用量记录等
部署 Docker Compose、systemd 安装脚本、多阶段 Dockerfile 快速部署、打包前端和后端

关键文件:

  • README.md / README_CN.md:项目介绍与部署说明。
  • backend/cmd/server/main.go:后端启动入口。
  • backend/internal/server/router.go:统一注册中间件和路由。
  • backend/internal/server/routes/gateway.go:AI 网关相关路由。
  • backend/internal/handler/openai_gateway_handler.go:OpenAI 网关 HTTP Handler。
  • backend/internal/service/openai_gateway_service.go:OpenAI 请求转发、兼容、重试、流式处理等核心逻辑。
  • backend/internal/service/openai_account_scheduler.go:OpenAI 上游账号调度器。
  • backend/internal/service/api_key_service.go:平台 API Key 管理与校验逻辑。
  • backend/internal/service/billing_service.go:计费缓存、模型价格、余额/订阅用量逻辑。
  • frontend/package.json:前端脚本和依赖。
  • deploy/docker-compose.ymldeploy/.env.example:Docker 部署配置。
  • Dockerfile:前端构建、后端构建、最终镜像三阶段打包。

3. 新手最快搭建方式:Docker Compose

如果你只是想先跑起来,推荐 Docker Compose。它会同时拉起 Sub2API、PostgreSQL 和 Redis。

3.1 环境要求

  • Linux 服务器或本地 Docker 环境
  • Docker 20.10+
  • Docker Compose v2+
  • 推荐开放端口:8080

3.2 一键准备部署目录

官方 README 提供的快速方式:

复制代码
mkdir -p sub2api-deploy && cd sub2api-deploy
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
docker compose up -d
docker compose logs -f sub2api

这个脚本会做几件对新手很友好的事:

  1. 下载 Docker Compose 配置和 .env.example
  2. 自动生成 JWT_SECRETTOTP_ENCRYPTION_KEYPOSTGRES_PASSWORD 等密钥。
  3. 创建数据目录,方便以后备份和迁移。
  4. 启动后访问 http://服务器IP:8080

3.3 手动部署方式

如果你想理解每一步,可以手动来:

复制代码
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy
cp .env.example .env
nano .env

.env 里最关键的是:

复制代码
POSTGRES_PASSWORD=your_secure_password_here
JWT_SECRET=your_jwt_secret_here
TOTP_ENCRYPTION_KEY=your_totp_key_here
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=your_admin_password
SERVER_PORT=8080

生成密钥可以用:

复制代码
openssl rand -hex 32

然后启动:

复制代码
docker compose up -d
docker compose logs -f sub2api

如果使用本地目录版配置:

复制代码
docker compose -f docker-compose.local.yml up -d
docker compose -f docker-compose.local.yml logs -f sub2api

3.4 首次启动发生了什么?

backend/cmd/server/main.go 里可以看到启动逻辑:

  1. 解析 -setup-version 参数。
  2. 检查是否需要初始化配置。
  3. 如果设置了 AUTO_SETUP=true,就从环境变量自动初始化。
  4. 如果没有自动初始化,就启动 Web Setup Wizard。
  5. 初始化完成后进入正常服务模式。

Docker Compose 中的 deploy/docker-compose.yml 明确设置了:

复制代码
AUTO_SETUP=true
SERVER_HOST=0.0.0.0
SERVER_PORT=8080
DATABASE_HOST=postgres
REDIS_HOST=redis

所以容器部署时不需要你手动创建配置文件,系统会根据环境变量完成第一次初始化。

4. 本地开发搭建

如果你想读源码、改代码、参与开发,可以按开发模式来。

4.1 后端

后端位于 backend/,使用 Go modules。

复制代码
cd backend
go mod download
go build -o bin/server ./cmd/server

项目自己的后端 Makefile 提供了:

复制代码
make build
make test
make test-unit
make test-integration

根目录 Makefile 也封装了:

复制代码
make build-backend
make test-backend

注意:backend/go.mod 当前声明的是 go 1.26.3,README 徽章和开发文档中也出现过 Go 1.25.x 的描述。实际开发时建议以当前源码 go.mod 和 CI 配置为准。

4.2 前端

前端位于 frontend/,包管理器是 pnpm,不建议混用 npm。

复制代码
cd frontend
pnpm install
pnpm run dev

常用脚本来自 frontend/package.json

复制代码
pnpm run dev        # Vite 开发服务器
pnpm run build      # vue-tsc 类型检查 + Vite 构建
pnpm run typecheck  # 类型检查
pnpm run lint:check # ESLint 检查
pnpm run test:run   # Vitest 测试

开发文档 DEV_GUIDE.md 特别提醒:前端必须使用 pnpm,pnpm-lock.yaml 要同步提交,否则 CI 的 frozen lockfile 会失败。

5. 源码分层:这个项目怎么组织?

从目录上看,Sub2API 后端采用比较典型的 Go 分层架构。

复制代码
backend/
  cmd/server/          # 程序入口
  ent/                 # Ent ORM 生成代码和 schema
  internal/config/     # 配置加载、默认值、校验
  internal/server/     # HTTP server、router、routes、中间件组装
  internal/handler/    # HTTP Handler,负责解析请求和返回响应
  internal/service/    # 业务逻辑:账号、调度、计费、网关转发
  internal/repository/ # 数据访问、缓存、迁移、上游 HTTP 辅助
  migrations/          # 数据库迁移
  resources/           # 模型价格等资源

前端则是 Vue 项目常见结构:

复制代码
frontend/src/
  api/        # 调后端接口的封装
  stores/     # Pinia 状态管理
  router/     # Vue Router 路由与导航守卫
  views/      # 页面级组件
  components/ # 可复用组件

frontend/src/router/index.ts 里可以看到:路由按页面懒加载,包含 /setup/login/register、用户页面和管理后台页面,并通过 meta 字段控制是否需要登录。

6. 请求从用户到上游:一次 AI 调用的完整链路

最值得分析的是网关链路。以用户调用 OpenAI/Claude/Gemini 兼容接口为例,可以拆成 7 步。

第 1 步:请求进入 Gin 路由

backend/internal/server/router.go 中的 SetupRouter 会挂载通用中间件:

  • 请求日志
  • CORS
  • Security Headers / CSP
  • 嵌入式前端资源服务
  • API v1 路由
  • 网关路由

核心注册点:

复制代码
routes.RegisterGatewayRoutes(r, h, apiKeyAuth, apiKeyService, subscriptionService, opsService, settingService, cfg)

第 2 步:网关路由判断协议

backend/internal/server/routes/gateway.go 定义了 AI 网关入口:

  • /v1/messages:Claude Messages 兼容入口,也会根据分组平台转到 OpenAI 逻辑。
  • /v1/responses:Responses API。
  • /v1/chat/completions:OpenAI Chat Completions。
  • /v1/images/generations/v1/images/edits:OpenAI 图片接口。
  • /v1beta/models/...:Gemini 原生 API 兼容层。
  • /backend-api/codex/responses:Codex 相关直连路径。

这说明 Sub2API 的目标不是只支持一种 API,而是把多个 AI 生态的入口统一到一个平台里。

第 3 步:API Key 鉴权

网关路由会使用 API Key 中间件。平台发给用户的 API Key 不是上游官方 Key,而是 Sub2API 自己生成和管理的 Key。

backend/internal/service/api_key_service.go 负责 API Key 的创建、查询、删除、额度检查、过期检查、失败限速等。源码里能看到这些错误类型:

  • API_KEY_NOT_FOUND
  • API_KEY_EXPIRED
  • API_KEY_QUOTA_EXHAUSTED
  • API_KEY_RATE_5H_EXCEEDED
  • API_KEY_RATE_1D_EXCEEDED
  • API_KEY_RATE_7D_EXCEEDED

这就是"平台 Key"和"上游 Key"解耦的关键:用户只接触平台 Key,系统内部再决定用哪个上游账号。

第 4 步:检查分组、订阅和余额

网关中还有 RequireGroupAssignment、订阅校验、计费缓存等逻辑。billing_service.go 中定义了余额、订阅、API Key 时间窗口用量缓存接口。

它关心三类数据:

  1. 用户余额。
  2. 用户某个分组下的订阅。
  3. 某个 API Key 在 5 小时、1 天、7 天窗口内的用量。

因此系统可以支持更细的商业规则:某个用户能用哪些模型、多少额度、是否超限、是否需要扣余额。

第 5 步:选择上游账号

这是 Sub2API 比普通反代更有技术含量的部分。

backend/internal/service/openai_account_scheduler.go 定义了 OpenAI 账号调度接口:

复制代码
Select(ctx, req) (*AccountSelectionResult, OpenAIAccountScheduleDecision, error)
ReportResult(accountID, success, firstTokenMs)
ReportSwitch()
SnapshotMetrics()

调度器会综合考虑:

  • 分组 ID
  • 会话 Hash
  • 粘性账号 ID
  • previous_response_id
  • 请求模型
  • 上游传输方式
  • 图片能力
  • 是否需要 compact 模式
  • 排除账号列表

源码里的调度层名称很直观:

  • previous_response_id:优先保持同一个上下文响应链。
  • session_hash:同一个会话尽量打到同一个账号。
  • load_balance:无法命中粘性时再负载均衡。

它还维护运行时统计,例如错误率 EWMA、首 Token 延迟 TTFT EWMA,用来辅助选择更健康的账号。

新手可以这样理解:普通轮询是"排队轮流上",Sub2API 的调度更像"先看你是不是老客户,再看哪个窗口空、哪个服务员状态好"。

第 6 步:构造并转发上游请求

backend/internal/service/openai_gateway_service.go 是 OpenAI 网关的核心大文件。它处理的事情很多:

  • OpenAI Responses / Chat Completions / Images 等接口兼容。
  • OAuth 账号与 API Key 账号的不同上游地址。
  • Codex CLI 相关请求头、会话和 WebSocket 行为。
  • 请求头白名单,避免把不安全或无意义的客户端头透传给上游。
  • 流式响应、重试、错误恢复。
  • 上游响应头和用量信息解析。

源码中可以看到一些关键常量:

复制代码
chatgptCodexURL = "https://chatgpt.com/backend-api/codex/responses"
openaiPlatformAPIURL = "https://api.openai.com/v1/responses"
openaiStickySessionTTL = time.Hour

这说明它不是简单把 URL 换一下,而是根据账号类型、客户端类型、模型能力和接口形态走不同路径。

第 7 步:记录用量、扣费、返回结果

请求成功后,系统需要把调用量转成成本,写入用量记录,再更新余额或订阅额度。billing_service.go 里有模型价格结构 ModelPricing,包含输入 token、输出 token、缓存 token、长上下文倍率、图片输出 token 等字段。

这解释了为什么它可以做到"Token 级别的用量追踪和成本计算"。对平台运营者来说,这比只统计请求次数精细得多。

7. 几个值得学习的技术点

7.1 Setup Wizard 与 Auto Setup

main.go 的启动逻辑把首次运行分成两种:

  • 没有配置且没开自动初始化:启动 Setup Wizard。
  • Docker 环境设置 AUTO_SETUP=true:直接从环境变量生成配置。

这对开源项目很友好:小白可以走网页向导,生产部署可以走环境变量自动化。

7.2 前端嵌入后端二进制

Dockerfile 是三阶段构建:

  1. Node 镜像构建前端。
  2. Go 镜像编译后端,并带上前端 dist。
  3. Alpine 运行最终二进制。

后端启动时 web.HasEmbeddedFrontend() 会判断是否有嵌入前端资源。如果有,就由后端同时服务 API 和前端页面。

这样部署时只需要一个应用容器,不必单独部署 Nginx + 前端静态站点。

7.3 Redis Lua 原子限流

backend/internal/middleware/rate_limiter.go 使用 Redis Lua 脚本做限流:

复制代码
local current = redis.call('INCR', KEYS[1])
local ttl = redis.call('PTTL', KEYS[1])
if current == 1 then
  redis.call('PEXPIRE', KEYS[1], ARGV[1])
elseif ttl == -1 then
  redis.call('PEXPIRE', KEYS[1], ARGV[1])
end
return {current, repaired}

好处是计数和设置过期时间在 Redis 内部原子完成,避免高并发下出现计数错乱。

7.4 粘性会话

AI 对话不是一次性请求。很多客户端会带 session_idconversation_idprevious_response_id。如果同一个上下文被分配到不同上游账号,可能出现上下文丢失或响应异常。

所以调度器先尝试 previous response 和 session hash,再进入负载均衡。这是 AI 网关区别于普通 HTTP 网关的重要点。

7.5 网关不是"裸透传"

OpenAI 网关里有 allowed headers 白名单。这样做可以减少两个风险:

  1. 客户端乱七八糟的头传到上游,触发风控或兼容问题。
  2. 敏感头、代理头、环境头被意外泄露。

这也是成熟网关常见设计:该透传的透传,不该透传的坚决过滤。

7.6 h2c 与 Nginx 注意事项

项目支持 HTTP/2 Cleartext,即 h2c。README 还特别提醒:如果前面接 Nginx,并且 Codex CLI 依赖带下划线的请求头,需要在 Nginx http 块里开启:

复制代码
underscores_in_headers on;

否则像 session_id 这样的头可能被 Nginx 丢掉,进而破坏粘性会话。

8. 管理后台能做什么?

从 Handler 聚合结构 backend/internal/handler/handler.go 可以看到后台能力很丰富:

  • 用户管理
  • 分组管理
  • 上游账号管理
  • 公告管理
  • 数据管理与备份
  • OAuth 管理
  • 代理管理
  • 兑换码/促销码
  • 系统设置
  • 运维监控
  • 订阅管理
  • 用量统计
  • API Key 管理
  • 渠道监控
  • 内容审核
  • 支付与分销

这说明 Sub2API 不只是一个给开发者自用的小工具,而是带有 SaaS 运营后台的完整平台。

9. 新手读源码路线

如果你刚开始读,不建议直接打开 openai_gateway_service.go 从头硬啃。推荐路线:

第一阶段:先看启动和路由

  1. backend/cmd/server/main.go
  2. backend/internal/server/http.go
  3. backend/internal/server/router.go
  4. backend/internal/server/routes/gateway.go

目标:知道服务如何启动,请求如何进来。

第二阶段:看用户与 API Key

  1. backend/internal/handler/api_key_handler.go
  2. backend/internal/service/api_key_service.go
  3. backend/internal/service/api_key_auth_cache.go

目标:理解平台 API Key 如何生成、校验和缓存。

第三阶段:看网关主链路

  1. backend/internal/handler/openai_gateway_handler.go
  2. backend/internal/service/openai_gateway_service.go
  3. backend/internal/service/openai_account_scheduler.go

目标:理解请求如何被选择账号、转发上游、处理响应。

第四阶段:看计费和用量

  1. backend/internal/service/billing_service.go
  2. backend/internal/service/account_usage_service.go
  3. backend/internal/handler/usage_handler.go
  4. backend/internal/repository/ops_repo*.go

目标:理解 Token、成本、余额、订阅额度如何串起来。

第五阶段:看前端后台

  1. frontend/src/router/index.ts
  2. frontend/src/api/
  3. frontend/src/stores/
  4. frontend/src/views/admin/

目标:理解后端能力如何被管理后台组织成页面。

10. 常见问题

Q1:我没有上游账号,可以跑起来吗?

可以。项目本身可以部署和进入后台,但真正转发 AI 请求需要配置上游账号、API Key 或 OAuth 账号。

Q2:用户调用的是谁的 Key?

用户调用的是 Sub2API 生成的平台 Key。上游官方 Key 或 OAuth 凭据保存在平台侧,由调度器选择使用哪个上游账号。

Q3:为什么需要 PostgreSQL 和 Redis?

PostgreSQL 保存长期数据,比如用户、账号、配置、订阅、用量记录。Redis 处理高频、短期、并发相关数据,比如限流计数、缓存、会话粘性和队列状态。

Q4:为什么不直接 Nginx 反代?

Nginx 只能做比较基础的转发和负载均衡。Sub2API 还要做 API Key 鉴权、账号调度、Token 计费、订阅额度、用户后台、支付、监控、粘性会话和模型兼容,这些已经是应用层网关能力。

Q5:simple 和 standard 模式有什么区别?

配置示例中说明:standard 是完整 SaaS 模式,包含计费和余额检查;simple 会隐藏 SaaS 功能,并跳过计费/余额检查,更适合内部使用。

11. 总结:这个项目最值得学什么?

Sub2API 的价值不在于"把请求转发到 OpenAI/Claude/Gemini"这么简单,而在于它把一个 AI API 平台需要的核心能力组合起来:

  • 用 Gin 承接多协议 API。
  • 用平台 API Key 解耦用户和上游账号。
  • 用调度器管理多账号、多模型、多会话。
  • 用 Redis 做高并发缓存、限流和粘性会话。
  • 用 PostgreSQL 保存长期业务数据。
  • 用计费系统把 Token 用量变成余额和订阅额度。
  • 用 Vue 管理后台把复杂运营能力可视化。
  • 用 Docker 和 Setup Wizard 降低部署门槛。

对小白来说,这个项目是学习"真实世界后端系统"的好样本:它既有 Web 后台,也有网关高并发;既有业务系统,也有工程化部署;既要考虑用户体验,也要考虑上游 API 的兼容和风控。

如果你想练手,可以从三个小目标开始:

  1. 先用 Docker Compose 跑起来。
  2. 在后台创建一个用户和 API Key。
  3. 顺着 /v1/chat/completions/v1/responses 的路由,把一次请求从 route、handler、service、scheduler 到 billing 画成流程图。

当你能画出这张图,你就已经从"会部署"进阶到了"读懂一个真实 AI 网关系统"。

相关推荐
老虎海子3 小时前
从零入门 OpenAI Codex|登录、权限、终端、记忆配置全实操
人工智能·vscode·自然语言处理·chatgpt·个人开发·业界资讯
与芯同行3 小时前
TP9243S与TP9311双芯片:AI语音产品从采集到回放的完整解决方案
人工智能
若兰幽竹3 小时前
【大模型应用】抖音爆款视频深度分析系统:流水线式AI逆向拆解流量密码,精准预测播放量!
人工智能·python·音视频·抖音爆款分析
AI技术控3 小时前
NeuroH-TGL 论文解读:面向脑疾病诊断的神经异质性引导时序图学习方法
人工智能·语言模型·自然语言处理·langchain·nlp
fuquxiaoguang3 小时前
微软Maia 200的“算力经济学”:推理时代的专用芯片如何改写游戏规则
人工智能·microsoft
心中有国也有家3 小时前
pytorch-adapter:让 PyTorch 模型“无缝”跑在昇腾 NPU 上
人工智能·pytorch·笔记·python·学习
Sharewinfo_BJ3 小时前
从手工报表到实时BI:一个零售数据平台的踩坑与重构实战
大数据·人工智能·科技·数据分析·微软·powerbi
Elastic 中国社区官方博客3 小时前
在 Elasticsearch 中,存储向量查询速度最高提升 3 倍
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索
Cosolar3 小时前
从零搭建本地 RAG 系统:LangChain + LM Studio 完整实战指南
人工智能·后端·面试