Files
openteam/.env.example
T
Sakurasan f1181150aa perf: 亲和表分片、记账按驱动并发、访问日志与原始体控量
继续上一轮性能改造的剩余项:

- 会话亲和表分 16 片(原来一个全局 mutex,每个请求都要走一遍),
  键改为内联 FNV-1a 的 64 位摘要(原先是拼接字符串 + hex,每请求两次分配),
  请求路径上零分配;新增键稳定性/无分配/分片并发测试。
- 记账落库按驱动自适应并发:Recorder 按用户分片到多个 worker(同一用户恒定
  同片,流水顺序不变)。SQLite 只有一个写者,实测 4 worker 会互相抢锁把
  p99 拖到 19–34ms,所以 SQLite 固定 1 个、PostgreSQL 用 4 个。
  新增多分片落库与顺序测试(用显式 worker 数在 SQLite 上验证语义)。
- 访问日志:每请求一次 log.Printf 直写 stderr,高 QPS 下是系统调用热点;
  改为 64 KiB 缓冲 + 每秒 flush(写/flush 互斥,-race 已验)。
- 原始体控量:LogRaw 落库的请求体截到 256 KiB、响应体截到 1 MiB 并标注原始
  体积,避免单条日志几十 MB 拖慢 usage_logs 查询与备份。
- 渠道加权随机选路从 crypto/rand + big.Int 换成 math/rand/v2 的 IntN,
  去掉每请求的系统调用与两次分配。

跳过的两项与原因:
- 「PG 下把 model 目录查询合并成一次 JOIN」:目录已缓存 10s,冷路径每模型
  10 秒才走一次,合并收益接近 0。
- 「每流一个 heartbeat goroutine」:Go 的 ticker 成本很低,千级并发流仍在
  可接受范围,改动收益不抵风险。

实测(16 并发、mock 上游、SQLite):p50 679µs→558µs、p99 2.6ms→2.2ms、
RPS 18.7k→23.9k。验证:go vet、go test ./...、-race(proxy/channel/usage)全绿。
2026-10-30 14:25:10 +08:00

89 lines
4.3 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
# 渠道健康检查
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