优化离线镜像的防抖以及日志

This commit is contained in:
user123456
2025-06-18 16:04:53 +08:00
parent fa9e9210ab
commit 51ace73b78

View File

@@ -33,16 +33,18 @@ type DebounceEntry struct {
// DownloadDebouncer 下载防抖器 // DownloadDebouncer 下载防抖器
type DownloadDebouncer struct { type DownloadDebouncer struct {
mu sync.RWMutex mu sync.RWMutex
entries map[string]*DebounceEntry entries map[string]*DebounceEntry
window time.Duration window time.Duration
lastCleanup time.Time
} }
// NewDownloadDebouncer 创建下载防抖器 // NewDownloadDebouncer 创建下载防抖器
func NewDownloadDebouncer(window time.Duration) *DownloadDebouncer { func NewDownloadDebouncer(window time.Duration) *DownloadDebouncer {
return &DownloadDebouncer{ return &DownloadDebouncer{
entries: make(map[string]*DebounceEntry), entries: make(map[string]*DebounceEntry),
window: window, window: window,
lastCleanup: time.Now(),
} }
} }
@@ -66,9 +68,10 @@ func (d *DownloadDebouncer) ShouldAllow(userID, contentKey string) bool {
UserID: userID, UserID: userID,
} }
// 清理过期条目(简单策略每100次请求清理一次) // 清理过期条目(每5分钟清理一次)
if len(d.entries)%100 == 0 { if time.Since(d.lastCleanup) > 5*time.Minute {
d.cleanup(now) d.cleanup(now)
d.lastCleanup = now
} }
return true return true
@@ -128,8 +131,8 @@ var (
func initDebouncer() { func initDebouncer() {
// 单个镜像5秒防抖窗口 // 单个镜像5秒防抖窗口
singleImageDebouncer = NewDownloadDebouncer(5 * time.Second) singleImageDebouncer = NewDownloadDebouncer(5 * time.Second)
// 批量镜像:30秒防抖窗口(影响更大,需要更长保护) // 批量镜像:60秒防抖窗口
batchImageDebouncer = NewDownloadDebouncer(30 * time.Second) batchImageDebouncer = NewDownloadDebouncer(60 * time.Second)
} }
// ImageStreamer 镜像流式下载器 // ImageStreamer 镜像流式下载器
@@ -454,7 +457,31 @@ func (is *ImageStreamer) streamDockerFormatWithReturn(ctx context.Context, tarWr
return err return err
} }
// streamSingleImageForBatch 为批量下载流式处理单个镜像 // processImageForBatch 处理镜像的公共逻辑,用于批量下载
func (is *ImageStreamer) processImageForBatch(ctx context.Context, img v1.Image, tarWriter *tar.Writer, imageRef string, options *StreamOptions) (map[string]interface{}, map[string]map[string]string, error) {
layers, err := img.Layers()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像层失败: %w", err)
}
configFile, err := img.ConfigFile()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像配置失败: %w", err)
}
log.Printf("镜像包含 %d 层", len(layers))
var manifest map[string]interface{}
var repositories map[string]map[string]string
err = is.streamDockerFormatWithReturn(ctx, tarWriter, img, layers, configFile, imageRef, &manifest, &repositories, options)
if err != nil {
return nil, nil, err
}
return manifest, repositories, nil
}
func (is *ImageStreamer) streamSingleImageForBatch(ctx context.Context, tarWriter *tar.Writer, imageRef string, options *StreamOptions) (map[string]interface{}, map[string]map[string]string, error) { func (is *ImageStreamer) streamSingleImageForBatch(ctx context.Context, tarWriter *tar.Writer, imageRef string, options *StreamOptions) (map[string]interface{}, map[string]map[string]string, error) {
ref, err := name.ParseReference(imageRef) ref, err := name.ParseReference(imageRef)
if err != nil { if err != nil {
@@ -468,84 +495,30 @@ func (is *ImageStreamer) streamSingleImageForBatch(ctx context.Context, tarWrite
return nil, nil, fmt.Errorf("获取镜像描述失败: %w", err) return nil, nil, fmt.Errorf("获取镜像描述失败: %w", err)
} }
var manifest map[string]interface{} var img v1.Image
var repositories map[string]map[string]string
switch desc.MediaType { switch desc.MediaType {
case types.OCIImageIndex, types.DockerManifestList: case types.OCIImageIndex, types.DockerManifestList:
// 处理多架构镜像 // 处理多架构镜像
img, err := is.selectPlatformImage(desc, options) img, err = is.selectPlatformImage(desc, options)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("选择平台镜像失败: %w", err) return nil, nil, fmt.Errorf("选择平台镜像失败: %w", err)
} }
layers, err := img.Layers()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像层失败: %w", err)
}
configFile, err := img.ConfigFile()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像配置失败: %w", err)
}
log.Printf("镜像包含 %d 层", len(layers))
err = is.streamDockerFormatWithReturn(ctx, tarWriter, img, layers, configFile, imageRef, &manifest, &repositories, options)
if err != nil {
return nil, nil, err
}
case types.OCIManifestSchema1, types.DockerManifestSchema2: case types.OCIManifestSchema1, types.DockerManifestSchema2:
img, err := desc.Image() img, err = desc.Image()
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("获取镜像失败: %w", err) return nil, nil, fmt.Errorf("获取镜像失败: %w", err)
} }
layers, err := img.Layers()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像层失败: %w", err)
}
configFile, err := img.ConfigFile()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像配置失败: %w", err)
}
log.Printf("镜像包含 %d 层", len(layers))
err = is.streamDockerFormatWithReturn(ctx, tarWriter, img, layers, configFile, imageRef, &manifest, &repositories, options)
if err != nil {
return nil, nil, err
}
default: default:
img, err := desc.Image() img, err = desc.Image()
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("获取镜像失败: %w", err) return nil, nil, fmt.Errorf("获取镜像失败: %w", err)
} }
layers, err := img.Layers()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像层失败: %w", err)
}
configFile, err := img.ConfigFile()
if err != nil {
return nil, nil, fmt.Errorf("获取镜像配置失败: %w", err)
}
log.Printf("镜像包含 %d 层", len(layers))
err = is.streamDockerFormatWithReturn(ctx, tarWriter, img, layers, configFile, imageRef, &manifest, &repositories, options)
if err != nil {
return nil, nil, err
}
} }
return manifest, repositories, nil return is.processImageForBatch(ctx, img, tarWriter, imageRef, options)
} }
// selectPlatformImage 从多架构镜像中选择合适的平台镜像 // selectPlatformImage 从多架构镜像中选择合适的平台镜像
func (is *ImageStreamer) selectPlatformImage(desc *remote.Descriptor, options *StreamOptions) (v1.Image, error) { func (is *ImageStreamer) selectPlatformImage(desc *remote.Descriptor, options *StreamOptions) (v1.Image, error) {
index, err := desc.ImageIndex() index, err := desc.ImageIndex()
@@ -609,7 +582,6 @@ var globalImageStreamer *ImageStreamer
// initImageStreamer 初始化镜像下载器 // initImageStreamer 初始化镜像下载器
func initImageStreamer() { func initImageStreamer() {
globalImageStreamer = NewImageStreamer(nil) globalImageStreamer = NewImageStreamer(nil)
// 镜像下载器初始化完成
} }
// formatPlatformText 格式化平台文本 // formatPlatformText 格式化平台文本
@@ -721,7 +693,7 @@ func handleSimpleBatchDownload(c *gin.Context) {
if !batchImageDebouncer.ShouldAllow(userID, contentKey) { if !batchImageDebouncer.ShouldAllow(userID, contentKey) {
c.JSON(http.StatusTooManyRequests, gin.H{ c.JSON(http.StatusTooManyRequests, gin.H{
"error": "批量下载请求过于频繁,请稍后再试", "error": "批量下载请求过于频繁,请稍后再试",
"retry_after": 30, "retry_after": 60,
}) })
return return
} }