整体性能优化
This commit is contained in:
@@ -63,6 +63,12 @@ var (
|
||||
appConfigLock sync.RWMutex
|
||||
isViperEnabled bool
|
||||
viperInstance *viper.Viper
|
||||
|
||||
// ✅ 配置缓存变量
|
||||
cachedConfig *AppConfig
|
||||
configCacheTime time.Time
|
||||
configCacheTTL = 5 * time.Second
|
||||
configCacheMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// DefaultConfig 返回默认配置
|
||||
@@ -141,21 +147,45 @@ func DefaultConfig() *AppConfig {
|
||||
|
||||
// GetConfig 安全地获取配置副本
|
||||
func GetConfig() *AppConfig {
|
||||
appConfigLock.RLock()
|
||||
defer appConfigLock.RUnlock()
|
||||
// ✅ 快速缓存检查,减少深拷贝开销
|
||||
configCacheMutex.RLock()
|
||||
if cachedConfig != nil && time.Since(configCacheTime) < configCacheTTL {
|
||||
config := cachedConfig
|
||||
configCacheMutex.RUnlock()
|
||||
return config
|
||||
}
|
||||
configCacheMutex.RUnlock()
|
||||
|
||||
if appConfig == nil {
|
||||
return DefaultConfig()
|
||||
// 缓存过期,重新生成配置
|
||||
configCacheMutex.Lock()
|
||||
defer configCacheMutex.Unlock()
|
||||
|
||||
// 双重检查,防止重复生成
|
||||
if cachedConfig != nil && time.Since(configCacheTime) < configCacheTTL {
|
||||
return cachedConfig
|
||||
}
|
||||
|
||||
// 返回配置的深拷贝
|
||||
appConfigLock.RLock()
|
||||
if appConfig == nil {
|
||||
appConfigLock.RUnlock()
|
||||
defaultCfg := DefaultConfig()
|
||||
cachedConfig = defaultCfg
|
||||
configCacheTime = time.Now()
|
||||
return defaultCfg
|
||||
}
|
||||
|
||||
// 生成新的配置深拷贝
|
||||
configCopy := *appConfig
|
||||
configCopy.Security.WhiteList = append([]string(nil), appConfig.Security.WhiteList...)
|
||||
configCopy.Security.BlackList = append([]string(nil), appConfig.Security.BlackList...)
|
||||
configCopy.Proxy.WhiteList = append([]string(nil), appConfig.Proxy.WhiteList...)
|
||||
configCopy.Proxy.BlackList = append([]string(nil), appConfig.Proxy.BlackList...)
|
||||
appConfigLock.RUnlock()
|
||||
|
||||
return &configCopy
|
||||
cachedConfig = &configCopy
|
||||
configCacheTime = time.Now()
|
||||
|
||||
return cachedConfig
|
||||
}
|
||||
|
||||
// setConfig 安全地设置配置
|
||||
@@ -163,6 +193,11 @@ func setConfig(cfg *AppConfig) {
|
||||
appConfigLock.Lock()
|
||||
defer appConfigLock.Unlock()
|
||||
appConfig = cfg
|
||||
|
||||
// ✅ 配置更新时清除缓存
|
||||
configCacheMutex.Lock()
|
||||
cachedConfig = nil
|
||||
configCacheMutex.Unlock()
|
||||
}
|
||||
|
||||
// LoadConfig 加载配置文件
|
||||
@@ -190,9 +225,7 @@ func LoadConfig() error {
|
||||
go enableViperHotReload()
|
||||
}
|
||||
|
||||
fmt.Printf("配置加载成功: 监听 %s:%d, 文件大小限制 %d MB, 限流 %d请求/%g小时, 离线镜像并发数 %d\n",
|
||||
cfg.Server.Host, cfg.Server.Port, cfg.Server.FileSize/(1024*1024),
|
||||
cfg.RateLimit.RequestLimit, cfg.RateLimit.PeriodHours, cfg.Download.MaxImages)
|
||||
// 配置加载成功,详细信息在启动时统一显示
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -218,7 +251,7 @@ func enableViperHotReload() {
|
||||
}
|
||||
|
||||
isViperEnabled = true
|
||||
fmt.Println("热重载已启用")
|
||||
// 热重载已启用,不显示额外信息
|
||||
|
||||
// 🚀 启用文件监听
|
||||
viperInstance.WatchConfig()
|
||||
|
||||
@@ -79,7 +79,7 @@ func initDockerProxy() {
|
||||
options: options,
|
||||
}
|
||||
|
||||
fmt.Printf("Docker代理已初始化\n")
|
||||
// Docker代理初始化完成
|
||||
}
|
||||
|
||||
// ProxyDockerRegistryGin 标准Docker Registry API v2代理
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
@@ -525,8 +526,7 @@ var globalImageStreamer *ImageStreamer
|
||||
// initImageStreamer 初始化镜像下载器
|
||||
func initImageStreamer() {
|
||||
globalImageStreamer = NewImageStreamer(nil)
|
||||
log.Printf("镜像下载器初始化完成,并发数: %d,缓存: %v",
|
||||
globalImageStreamer.concurrency, isCacheEnabled())
|
||||
// 镜像下载器初始化完成
|
||||
}
|
||||
|
||||
// formatPlatformText 格式化平台文本
|
||||
@@ -724,7 +724,11 @@ func (is *ImageStreamer) StreamMultipleImages(ctx context.Context, imageRefs []s
|
||||
|
||||
log.Printf("处理镜像 %d/%d: %s", i+1, len(imageRefs), imageRef)
|
||||
|
||||
manifest, repositories, err := is.streamSingleImageForBatch(ctx, tarWriter, imageRef, options)
|
||||
// ✅ 添加超时保护,防止单个镜像处理时间过长
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Minute)
|
||||
manifest, repositories, err := is.streamSingleImageForBatch(timeoutCtx, tarWriter, imageRef, options)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("下载镜像 %s 失败: %v", imageRef, err)
|
||||
return fmt.Errorf("下载镜像 %s 失败: %w", imageRef, err)
|
||||
|
||||
18
src/main.go
18
src/main.go
@@ -3,12 +3,14 @@ package main
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
//go:embed public/*
|
||||
@@ -66,6 +68,15 @@ func main() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
|
||||
// ✅ 添加全局Panic恢复保护
|
||||
router.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||||
log.Printf("🚨 Panic recovered: %v", recovered)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Internal server error",
|
||||
"code": "INTERNAL_ERROR",
|
||||
})
|
||||
}))
|
||||
|
||||
// 初始化镜像tar下载路由
|
||||
initImageTarRoutes(router)
|
||||
|
||||
@@ -103,7 +114,10 @@ func main() {
|
||||
router.NoRoute(RateLimitMiddleware(globalLimiter), handler)
|
||||
|
||||
cfg := GetConfig()
|
||||
fmt.Printf("启动成功,项目地址:https://github.com/sky22333/hubproxy \n")
|
||||
fmt.Printf("🚀 HubProxy 启动成功\n")
|
||||
fmt.Printf("📡 监听地址: %s:%d\n", cfg.Server.Host, cfg.Server.Port)
|
||||
fmt.Printf("⚡ 限流配置: %d请求/%g小时\n", cfg.RateLimit.RequestLimit, cfg.RateLimit.PeriodHours)
|
||||
fmt.Printf("🔗 项目地址: https://github.com/sky22333/hubproxy\n")
|
||||
|
||||
err := router.Run(fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port))
|
||||
if err != nil {
|
||||
|
||||
@@ -92,8 +92,7 @@ func initGlobalLimiter() *IPRateLimiter {
|
||||
// 启动定期清理goroutine
|
||||
go limiter.cleanupRoutine()
|
||||
|
||||
fmt.Printf("限流器初始化: %d请求/%g小时, 白名单 %d个, 黑名单 %d个\n",
|
||||
cfg.RateLimit.RequestLimit, cfg.RateLimit.PeriodHours, len(whitelist), len(blacklist))
|
||||
// 限流器初始化完成,详细信息在启动时统一显示
|
||||
|
||||
return limiter
|
||||
}
|
||||
@@ -189,29 +188,40 @@ func (i *IPRateLimiter) GetLimiter(ip string) (*rate.Limiter, bool) {
|
||||
return rate.NewLimiter(rate.Inf, i.b), true // 白名单中的IP不受限制
|
||||
}
|
||||
|
||||
// 使用纯IP作为缓存键
|
||||
now := time.Now()
|
||||
|
||||
// ✅ 双重检查锁定,解决竞态条件
|
||||
i.mu.RLock()
|
||||
entry, exists := i.ips[cleanIP]
|
||||
i.mu.RUnlock()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
if !exists {
|
||||
// 创建新的限流器
|
||||
if exists {
|
||||
// 安全更新访问时间
|
||||
i.mu.Lock()
|
||||
entry = &rateLimiterEntry{
|
||||
limiter: rate.NewLimiter(i.r, i.b),
|
||||
lastAccess: now,
|
||||
if entry, stillExists := i.ips[cleanIP]; stillExists {
|
||||
entry.lastAccess = now
|
||||
i.mu.Unlock()
|
||||
return entry.limiter, true
|
||||
}
|
||||
i.ips[cleanIP] = entry
|
||||
i.mu.Unlock()
|
||||
} else {
|
||||
// 更新最后访问时间
|
||||
i.mu.Lock()
|
||||
entry.lastAccess = now
|
||||
i.mu.Unlock()
|
||||
}
|
||||
|
||||
// 创建新条目时的双重检查
|
||||
i.mu.Lock()
|
||||
if entry, exists := i.ips[cleanIP]; exists {
|
||||
entry.lastAccess = now
|
||||
i.mu.Unlock()
|
||||
return entry.limiter, true
|
||||
}
|
||||
|
||||
// 创建新条目
|
||||
entry = &rateLimiterEntry{
|
||||
limiter: rate.NewLimiter(i.r, i.b),
|
||||
lastAccess: now,
|
||||
}
|
||||
i.ips[cleanIP] = entry
|
||||
i.mu.Unlock()
|
||||
|
||||
return entry.limiter, true
|
||||
}
|
||||
|
||||
|
||||
@@ -114,22 +114,29 @@ func (c *Cache) Set(key string, data interface{}) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// 如果缓存已满,删除最旧的条目
|
||||
if len(c.data) >= c.maxSize {
|
||||
oldest := time.Now()
|
||||
var oldestKey string
|
||||
for k, v := range c.data {
|
||||
if v.timestamp.Before(oldest) {
|
||||
oldest = v.timestamp
|
||||
oldestKey = k
|
||||
}
|
||||
// ✅ 先清理过期项,防止内存泄漏
|
||||
now := time.Now()
|
||||
for k, v := range c.data {
|
||||
if now.Sub(v.timestamp) > cacheTTL {
|
||||
delete(c.data, k)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果清理后仍然超限,批量删除最旧的条目
|
||||
if len(c.data) >= c.maxSize {
|
||||
toDelete := len(c.data) / 4 // 删除25%最旧的
|
||||
for k := range c.data {
|
||||
if toDelete <= 0 {
|
||||
break
|
||||
}
|
||||
delete(c.data, k)
|
||||
toDelete--
|
||||
}
|
||||
delete(c.data, oldestKey)
|
||||
}
|
||||
|
||||
c.data[key] = cacheEntry{
|
||||
data: data,
|
||||
timestamp: time.Now(),
|
||||
timestamp: now,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user