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