限流改为全局应用
This commit is contained in:
@@ -596,9 +596,9 @@ func formatPlatformText(platform string) string {
|
|||||||
func initImageTarRoutes(router *gin.Engine) {
|
func initImageTarRoutes(router *gin.Engine) {
|
||||||
imageAPI := router.Group("/api/image")
|
imageAPI := router.Group("/api/image")
|
||||||
{
|
{
|
||||||
imageAPI.GET("/download/:image", RateLimitMiddleware(globalLimiter), handleDirectImageDownload)
|
imageAPI.GET("/download/:image", handleDirectImageDownload)
|
||||||
imageAPI.GET("/info/:image", RateLimitMiddleware(globalLimiter), handleImageInfo)
|
imageAPI.GET("/info/:image", handleImageInfo)
|
||||||
imageAPI.POST("/batch", RateLimitMiddleware(globalLimiter), handleSimpleBatchDownload)
|
imageAPI.POST("/batch", handleSimpleBatchDownload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/main.go
11
src/main.go
@@ -84,6 +84,9 @@ func main() {
|
|||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// 全局限流中间件 - 应用到所有路由
|
||||||
|
router.Use(RateLimitMiddleware(globalLimiter))
|
||||||
|
|
||||||
// 初始化监控端点
|
// 初始化监控端点
|
||||||
initHealthRoutes(router)
|
initHealthRoutes(router)
|
||||||
|
|
||||||
@@ -113,15 +116,15 @@ func main() {
|
|||||||
RegisterSearchRoute(router)
|
RegisterSearchRoute(router)
|
||||||
|
|
||||||
// 注册Docker认证路由(/token*)
|
// 注册Docker认证路由(/token*)
|
||||||
router.Any("/token", RateLimitMiddleware(globalLimiter), ProxyDockerAuthGin)
|
router.Any("/token", ProxyDockerAuthGin)
|
||||||
router.Any("/token/*path", RateLimitMiddleware(globalLimiter), ProxyDockerAuthGin)
|
router.Any("/token/*path", ProxyDockerAuthGin)
|
||||||
|
|
||||||
// 注册Docker Registry代理路由
|
// 注册Docker Registry代理路由
|
||||||
router.Any("/v2/*path", RateLimitMiddleware(globalLimiter), ProxyDockerRegistryGin)
|
router.Any("/v2/*path", ProxyDockerRegistryGin)
|
||||||
|
|
||||||
|
|
||||||
// 注册NoRoute处理器
|
// 注册NoRoute处理器
|
||||||
router.NoRoute(RateLimitMiddleware(globalLimiter), handler)
|
router.NoRoute(handler)
|
||||||
|
|
||||||
cfg := GetConfig()
|
cfg := GetConfig()
|
||||||
fmt.Printf("🚀 HubProxy 启动成功\n")
|
fmt.Printf("🚀 HubProxy 启动成功\n")
|
||||||
|
|||||||
@@ -230,6 +230,14 @@ func (i *IPRateLimiter) GetLimiter(ip string) (*rate.Limiter, bool) {
|
|||||||
// RateLimitMiddleware 速率限制中间件
|
// RateLimitMiddleware 速率限制中间件
|
||||||
func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
|
func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
// 静态文件豁免:跳过限流检查
|
||||||
|
path := c.Request.URL.Path
|
||||||
|
if path == "/" || path == "/favicon.ico" || path == "/images.html" || path == "/search.html" ||
|
||||||
|
strings.HasPrefix(path, "/public/") {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 获取客户端真实IP
|
// 获取客户端真实IP
|
||||||
var ip string
|
var ip string
|
||||||
|
|
||||||
@@ -295,25 +303,4 @@ func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyRateLimit 应用限流到特定路由
|
|
||||||
func ApplyRateLimit(router *gin.Engine, path string, method string, handler gin.HandlerFunc) {
|
|
||||||
// 使用全局限流器
|
|
||||||
limiter := globalLimiter
|
|
||||||
if limiter == nil {
|
|
||||||
limiter = initGlobalLimiter()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据HTTP方法应用限流
|
|
||||||
switch method {
|
|
||||||
case "GET":
|
|
||||||
router.GET(path, RateLimitMiddleware(limiter), handler)
|
|
||||||
case "POST":
|
|
||||||
router.POST(path, RateLimitMiddleware(limiter), handler)
|
|
||||||
case "PUT":
|
|
||||||
router.PUT(path, RateLimitMiddleware(limiter), handler)
|
|
||||||
case "DELETE":
|
|
||||||
router.DELETE(path, RateLimitMiddleware(limiter), handler)
|
|
||||||
default:
|
|
||||||
router.Any(path, RateLimitMiddleware(limiter), handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -71,14 +71,6 @@ func buildManifestCacheKey(imageRef, reference string) string {
|
|||||||
return buildCacheKey("manifest", key)
|
return buildCacheKey("manifest", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildManifestCacheKeyWithPlatform(imageRef, reference, platform string) string {
|
|
||||||
if platform == "" {
|
|
||||||
platform = "default"
|
|
||||||
}
|
|
||||||
key := fmt.Sprintf("%s:%s@%s", imageRef, reference, platform)
|
|
||||||
return buildCacheKey("manifest", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getManifestTTL(reference string) time.Duration {
|
func getManifestTTL(reference string) time.Duration {
|
||||||
cfg := GetConfig()
|
cfg := GetConfig()
|
||||||
defaultTTL := 30 * time.Minute
|
defaultTTL := 30 * time.Minute
|
||||||
@@ -150,3 +142,27 @@ func isCacheEnabled() bool {
|
|||||||
func isTokenCacheEnabled() bool {
|
func isTokenCacheEnabled() bool {
|
||||||
return isCacheEnabled()
|
return isCacheEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定期清理过期缓存,防止内存泄漏
|
||||||
|
func init() {
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(20 * time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
now := time.Now()
|
||||||
|
expiredKeys := make([]string, 0)
|
||||||
|
|
||||||
|
globalCache.cache.Range(func(key, value interface{}) bool {
|
||||||
|
if cached := value.(*CachedItem); now.After(cached.ExpiresAt) {
|
||||||
|
expiredKeys = append(expiredKeys, key.(string))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, key := range expiredKeys {
|
||||||
|
globalCache.cache.Delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user