共计 2445 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
开发 ChatGPT 订阅服务时,我们通常会遇到以下几个核心挑战:

- API 调用限制:OpenAI 对 API 有严格的速率限制,需要设计合理的请求队列和重试机制
- 计费策略实现:订阅制需要精确跟踪用户使用量,避免超额或不足扣费
- 异步消息处理:订阅状态变更、到期提醒等需要可靠的消息队列支持
- 突发流量应对:促销活动可能导致短时间内注册量激增
架构设计
技术选型对比
- Serverless 架构:适合初期快速验证,但难以处理复杂订阅逻辑和状态管理
- 微服务架构:更适合企业级订阅系统,组件可独立扩展
最终选择 FastAPI+Redis 组合,因为:
- FastAPI 提供高性能 API 开发和自动文档生成
- Redis 完美支持高频率的额度计数操作
- Python 生态有丰富的异步处理库
核心实现
1. 带 JWT 验证的订阅端点
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/subscribe")
async def create_subscription(token: str = Depends(oauth2_scheme)):
# 验证 JWT 并创建订阅
try:
payload = verify_jwt(token) # 自定义 JWT 验证
user_id = payload.get("sub")
# 订阅逻辑...
except Exception as e:
logger.error(f"Subscription failed: {str(e)}")
raise HTTPException(status_code=400, detail="Subscription error")
2. Redis 原子化额度扣减
使用 Lua 脚本保证操作的原子性:
-- debit.lua
local key = KEYS[1]
local debit_amount = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key) or "0")
if current >= debit_amount then
return redis.call('DECRBY', key, debit_amount)
else
return -1 -- 额度不足
end
3. Celery 异步处理
from celery import Celery
celery = Celery('tasks', broker='redis://localhost:6379/0')
@celery.task(bind=True, max_retries=3)
def send_notification(self, user_id, message):
try:
# 发送通知逻辑
except Exception as exc:
raise self.retry(exc=exc)
生产级代码示例
带重试的 API 调用
import tenacity
from openai import OpenAI
@tenacity.retry(stop=tenacity.stop_after_attempt(3),
wait=tenacity.wait_exponential(multiplier=1, min=4, max=10),
retry=tenacity.retry_if_exception_type(Exception)
)
def call_chatgpt(prompt):
client = OpenAI()
try:
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
except Exception as e:
logger.error(f"API call failed: {e}")
raise
避坑指南
- 时区问题:
- 问题:UTC 时间导致计费周期与用户本地时间不符
-
解决:在用户表中存储时区偏好,所有时间操作都转换为用户本地时间
-
API 限流:
- 问题:突发请求导致 OpenAI API 限流
-
解决:实现请求队列和指数退避重试机制
-
数据一致性:
- 问题:订阅状态在数据库和缓存中不一致
- 解决:使用双写策略并添加定期校验任务
单元测试示例
import pytest
from unittest.mock import Mock
from services.subscription import create_subscription
@pytest.mark.asyncio
async def test_create_subscription_success():
mock_db = Mock()
mock_db.get_user.return_value = {"id": 1, "status": "active"}
result = await create_subscription(user_id=1, db=mock_db)
assert result["status"] == "success"
mock_db.create_subscription.assert_called_once()
延伸思考
现有系统可以进一步扩展支持:
- 多级订阅套餐:通过策略模式实现不同套餐的业务逻辑
- 家庭共享计划:添加用户组概念和共享额度池
- 使用预测:基于历史数据预测用户下月使用量
总结
构建生产级 ChatGPT 订阅服务需要考虑众多技术细节,从 API 限流处理到精确计费实现。本文介绍的 FastAPI+Redis+Celery 架构已被多个项目验证可行,关键点在于:
- 使用 Lua 脚本保证 Redis 操作的原子性
- 完善的异常处理和重试机制
- 清晰的订阅状态机设计
建议读者先在小流量环境验证核心逻辑,再逐步扩展功能。
正文完
