profile(16 并发)显示请求路径的 CPU 有 ~70% 花在鉴权上:每个代理请求都要用 密钥哈希查 api_keys、再用 user_id 查 users,两条随机读。 - 新增鉴权缓存(OT_PROXY_AUTH_CACHE_TTL,默认 2s,0=关闭):缓存密钥与用户行, 但密钥状态、用户状态、密钥过期仍按当前时间逐请求判定;管理端写操作与用户的 密钥写操作经中间件立即清空缓存。余额随用户行一起缓存,只影响「≤0 就 402」 这道软闸,最长滞后一个 TTL(请求照样计费)。 - last_used_at 节流改由缓存内的 touch 表记账(原先按 DB 行时间戳判断,行被缓存后 时间戳不再变化,会导致每请求都写一次库)。touch 用 sync.Map:早先与缓存共用一把 排他锁,16 并发下互斥饥饿把吞吐从 31k 拖到 3.2k(p99 7ms),换 sync.Map 后消失。 - 去掉 usage_logs 上从未被查询过滤的 key_id / trace_id 索引(没有查询按它们过滤, 每个索引都是每行一次 B-tree 插入);schema v8 迁移里显式 DROP 旧库上的索引。 实测(16 并发、mock 上游、SQLite): - 鉴权缓存开:p50 131–177µs、p99 0.6–1.2ms、RPS 56k–81k - 鉴权缓存关:p50 535–631µs、p99 1.6–2.8ms、RPS 21k–27k (4 并发下同样受益:30k→41k) 测试:新增缓存命中零 SQL、状态/过期重判、失效、TTL 到期、缓存关闭、并发七项; go vet、go test ./...、-race(proxy/usage/store/api)全绿。
93 lines
4.7 KiB
Bash
93 lines
4.7 KiB
Bash
# openteam 服务配置示例 —— 复制为 .env 后修改
|
||
# 所有项均可用环境变量 OT_<KEY> 覆盖(点号转下划线,如 db.driver → OT_DB_DRIVER)
|
||
|
||
# 运行环境 development | production(仅这两个值;生产启动会做密钥断言,见下)
|
||
OT_ENV=development
|
||
OT_PORT=8080
|
||
|
||
# 数据库(开发默认 SQLite;生产切 postgres)
|
||
OT_DB_DRIVER=sqlite
|
||
OT_DB_DSN=data/openteam.db
|
||
# OT_DB_DRIVER=postgres
|
||
# OT_DB_DSN=host=localhost user=openteam password=openteam dbname=openteam port=5432 sslmode=disable
|
||
|
||
# JWT(生产必须更换为随机长字符串;OT_ENV=production 时本文件里的占位值会被启动校验拒绝)
|
||
OT_JWT_SECRET=change-me-to-a-long-random-string
|
||
OT_JWT_ACCESS_TTL=2h
|
||
OT_JWT_REFRESH_TTL=168h
|
||
# 生产必须为 true(refresh cookie 走 HTTPS;反代终止 TLS 时也要置 true)
|
||
OT_JWT_COOKIE_SECURE=false
|
||
|
||
# 注册策略:open(开放)| invite(邀请码)
|
||
OT_AUTH_REGISTRATION_MODE=open
|
||
|
||
# 渠道密钥加密主密钥(生产必须设置,32 字节以上)
|
||
OT_MASTER_KEY=change-me-master-key
|
||
|
||
# 默认上游渠道(首次启动自动创建 OpenAI 渠道)
|
||
OT_PROXY_UPSTREAM_KEY=
|
||
OT_PROXY_UPSTREAM_BASE_URL=https://api.openai.com
|
||
OT_PROXY_DEFAULT_MODEL=gpt-4o-mini
|
||
# 上游超时兜底值(渠道 timeout_ms 优先):
|
||
# 非流式 = 整段请求上限;流式 = 只约束首包,正文不限时长
|
||
OT_PROXY_TIMEOUT=300s
|
||
|
||
# /v1 请求体上限(MB):超限按客户端协议返回 413
|
||
OT_PROXY_MAX_BODY_MB=32
|
||
|
||
# 故障转移给"第二个及以后的渠道"留的时间预算(0=不限)。
|
||
# 首个渠道不受它约束——长回答本就要跑几分钟,但不能让坏渠道把客户端拖到 N×timeout。
|
||
OT_PROXY_FAILOVER_BUDGET=30s
|
||
# 流式空闲多久补一行 SSE 注释心跳(0=关闭)。挂在 nginx/Cloudflare 后面时,
|
||
# 上游长时间不出字(长思考)会被对端的空闲超时掐掉,心跳用来续住这条流。
|
||
OT_PROXY_STREAM_HEARTBEAT=15s
|
||
# 流式在放行给客户端之前最多等内容多久(0=不限):超时未出内容就取消本次尝试、
|
||
# 换下一个渠道。上游挂起(连接正常但一直不吐字)时的兜底。
|
||
OT_PROXY_FIRST_TOKEN_WAIT=0s
|
||
# 会话亲和 TTL(0=关闭,默认 5m):同一会话粘住上次应答的渠道,命中上游的提示词
|
||
# 缓存(整段上下文每轮重发),而不是被加权随机打散后全价重算。
|
||
OT_PROXY_AFFINITY_TTL=5m
|
||
# 渠道并发满载时每个候选渠道最多排队多久(0=立刻跳过)。上游 LLM 请求本来就慢,
|
||
# 短暂排队远好于把 502 直接甩给 agent;排队仍拿不到槽时返回 503 + Retry-After。
|
||
OT_PROXY_CHANNEL_ACQUIRE_WAIT=150ms
|
||
# 鉴权缓存 TTL(0=关闭,默认 2s)。命中时省掉每个请求的两条随机读(api_keys/users),
|
||
# 实测 p50 从 535µs 降到 131µs。代价:密钥/用户被停用后最多延迟这个时间才生效
|
||
# (管理端与用户的写操作会立即清空缓存,所以实际窗口通常远小于 TTL)。
|
||
OT_PROXY_AUTH_CACHE_TTL=2s
|
||
|
||
# 渠道健康检查
|
||
OT_PROXY_HEALTH_INTERVAL=60s
|
||
OT_PROXY_HEALTH_FAIL_THRESHOLD=2
|
||
|
||
# 调试:记录管理员的原始请求与响应到请求明细(默认关闭;流式记录全部事件)
|
||
# 记录管理员请求的原始请求体/响应体到 usage_logs(调试用)。
|
||
# 原始体会截断:请求体 256 KiB、响应体 1 MiB,避免单条日志几十 MB 拖慢查询与备份。
|
||
OT_PROXY_LOG_RAW=false
|
||
|
||
# 限流(内存计数,Redis 后置):用户级每秒请求数上限(0=不限制)
|
||
OT_RATELIMIT_USER_RPS=20
|
||
# 认证端点限速(每分钟,0=不限制):登录是密码爆破面,注册是批量建号面
|
||
OT_RATELIMIT_LOGIN_IP_PER_MIN=30
|
||
OT_RATELIMIT_LOGIN_USER_PER_MIN=10
|
||
OT_RATELIMIT_REGISTER_IP_PER_MIN=5
|
||
|
||
# 跨域白名单(逗号分隔 origin)。留空 = 不输出任何 CORS 头:
|
||
# 网关自托管 web/dist 时前后端同源、vite dev 走 proxy,都不需要跨域。
|
||
# 只有把 web/ 单独部署到别的域名时才需要配。
|
||
OT_CORS_ALLOW_ORIGINS=
|
||
# 反向代理(CIDR 或 IP,逗号分隔)。配置后 gin 才信任 X-Forwarded-For 取客户端 IP,
|
||
# 上面的认证限速依赖它;直连部署留空(此时按连接源地址计数,伪造头无效)。
|
||
OT_SERVER_TRUSTED_PROXIES=
|
||
|
||
# Passkey(WebAuthn):RPID 为域名,RPOrigin 为前端来源(需 HTTPS 或 localhost)
|
||
OT_WEBAUTHN_RP_ID=localhost
|
||
OT_WEBAUTHN_RP_ORIGIN=http://localhost:5173
|
||
OT_WEBAUTHN_RP_NAME=openteam
|
||
|
||
# 初始管理员(仅首次创建生效)。非 development 环境未显式设置 OT_ADMIN_PASSWORD
|
||
# 时拒绝播种管理员;admin123 是公开默认值,生产启动校验也会拒绝它。
|
||
OT_ADMIN_USERNAME=admin
|
||
OT_ADMIN_EMAIL=admin@localhost
|
||
OT_ADMIN_PASSWORD=admin123
|
||
|