feat: 优化codex冷启动, 还有连接池数据库配置信息

This commit is contained in:
yangjianbo
2026-02-06 20:31:42 +08:00
parent ee01f80dc1
commit 2d4bbbf49d
9 changed files with 401 additions and 23 deletions

View File

@@ -789,8 +789,8 @@ func setDefaults() {
viper.SetDefault("redis.dial_timeout_seconds", 5)
viper.SetDefault("redis.read_timeout_seconds", 3)
viper.SetDefault("redis.write_timeout_seconds", 3)
viper.SetDefault("redis.pool_size", 128)
viper.SetDefault("redis.min_idle_conns", 10)
viper.SetDefault("redis.pool_size", 1024)
viper.SetDefault("redis.min_idle_conns", 128)
viper.SetDefault("redis.enable_tls", false)
// Ops (vNext)
@@ -888,7 +888,7 @@ func setDefaults() {
// HTTP 上游连接池配置(针对 5000+ 并发用户优化)
viper.SetDefault("gateway.max_idle_conns", 240) // 最大空闲连接总数HTTP/2 场景默认)
viper.SetDefault("gateway.max_idle_conns_per_host", 120) // 每主机最大空闲连接HTTP/2 场景默认)
viper.SetDefault("gateway.max_conns_per_host", 240) // 每主机最大连接数(含活跃HTTP/2 场景默认
viper.SetDefault("gateway.max_conns_per_host", 4096) // 每主机最大连接数(含活跃;流式/HTTP1.1 可调大
viper.SetDefault("gateway.idle_conn_timeout_seconds", 90) // 空闲连接超时(秒)
viper.SetDefault("gateway.max_upstream_clients", 5000)
viper.SetDefault("gateway.client_idle_ttl_seconds", 900)

View File

@@ -9,12 +9,29 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"
)
const (
opencodeCodexHeaderURL = "https://raw.githubusercontent.com/anomalyco/opencode/dev/packages/opencode/src/session/prompt/codex_header.txt"
codexCacheTTL = 15 * time.Minute
// 避免冷启动首请求被外网/ DNS / GitHub 卡死。
// http.DefaultClient 默认无超时,网络异常时可能阻塞很久。
opencodeFetchTimeout = 3 * time.Second
// 本地缓存为空时,最小回源间隔(防止并发下反复打 GitHub
opencodeEmptyCacheRefreshInterval = 1 * time.Minute
// 防抖:防止短时间内重复触发异步回源。
opencodeFetchDebounce = 3 * time.Second
)
var opencodeFetchHTTPClient = &http.Client{Timeout: opencodeFetchTimeout}
var (
opencodeFetchMu sync.Mutex
opencodeFetchInFlight bool
opencodeFetchLastStart time.Time
)
//go:embed prompts/codex_cli_instructions.md
@@ -230,30 +247,74 @@ func getOpenCodeCachedPrompt(url, cacheFileName, metaFileName string) string {
}
var meta opencodeCacheMetadata
if loadJSON(metaFile, &meta) && meta.LastChecked > 0 && cachedContent != "" {
if time.Since(time.UnixMilli(meta.LastChecked)) < codexCacheTTL {
return cachedContent
_ = loadJSON(metaFile, &meta)
if meta.LastChecked > 0 {
lastCheckedAt := time.UnixMilli(meta.LastChecked)
if cachedContent != "" {
if time.Since(lastCheckedAt) < codexCacheTTL {
return cachedContent
}
} else {
// 没有任何缓存内容时,回源失败也不应影响请求链路;这里做节流,避免并发下反复回源。
if time.Since(lastCheckedAt) < opencodeEmptyCacheRefreshInterval {
return ""
}
}
}
content, etag, status, err := fetchWithETag(url, meta.ETag)
if err == nil && status == http.StatusNotModified && cachedContent != "" {
return cachedContent
}
if err == nil && status >= 200 && status < 300 && content != "" {
_ = writeFile(cacheFile, content)
meta = opencodeCacheMetadata{
ETag: etag,
LastFetch: time.Now().UTC().Format(time.RFC3339),
LastChecked: time.Now().UnixMilli(),
}
_ = writeJSON(metaFile, meta)
return content
}
// 不在请求链路内同步拉取GitHub/DNS/网络异常会导致冷启动首请求卡 1 分钟+)。
// 直接返回当前缓存(可为空),并异步刷新缓存。
scheduleOpencodeCacheRefresh(url, cacheFile, metaFile, meta.ETag)
return cachedContent
}
func scheduleOpencodeCacheRefresh(url, cacheFile, metaFile, etag string) {
opencodeFetchMu.Lock()
if opencodeFetchInFlight {
opencodeFetchMu.Unlock()
return
}
if !opencodeFetchLastStart.IsZero() && time.Since(opencodeFetchLastStart) < opencodeFetchDebounce {
opencodeFetchMu.Unlock()
return
}
opencodeFetchInFlight = true
opencodeFetchLastStart = time.Now()
opencodeFetchMu.Unlock()
go func() {
defer func() {
opencodeFetchMu.Lock()
opencodeFetchInFlight = false
opencodeFetchMu.Unlock()
}()
now := time.Now()
content, newETag, status, err := fetchWithETag(url, etag)
var meta opencodeCacheMetadata
_ = loadJSON(metaFile, &meta)
meta.LastChecked = now.UnixMilli()
switch {
case err == nil && status == http.StatusNotModified:
// 304 表示无需更新缓存文件,只更新检查时间。
if newETag != "" {
meta.ETag = newETag
}
_ = writeJSON(metaFile, meta)
case err == nil && status >= 200 && status < 300 && strings.TrimSpace(content) != "":
_ = writeFile(cacheFile, content)
meta.ETag = newETag
meta.LastFetch = now.UTC().Format(time.RFC3339)
_ = writeJSON(metaFile, meta)
default:
// 拉取失败也记录检查时间,避免高并发下持续回源。
_ = writeJSON(metaFile, meta)
}
}()
}
func getOpenCodeCodexHeader() string {
// 优先从 opencode 仓库缓存获取指令。
opencodeInstructions := getOpenCodeCachedPrompt(opencodeCodexHeaderURL, "opencode-codex-header.txt", "opencode-codex-header-meta.json")
@@ -598,7 +659,7 @@ func fetchWithETag(url, etag string) (string, string, int, error) {
if etag != "" {
req.Header.Set("If-None-Match", etag)
}
resp, err := http.DefaultClient.Do(req)
resp, err := opencodeFetchHTTPClient.Do(req)
if err != nil {
return "", "", 0, err
}