优化限流功能

This commit is contained in:
2026-01-05 16:28:29 +08:00
parent e08103e097
commit 4f4816e093
3 changed files with 108 additions and 33 deletions

View File

@@ -209,6 +209,8 @@ func (m *HalfOpenManager) checkHalfOpenSuccessThreshold(metrics HalfOpenMetrics,
return false return false
} }
// 使用浮点数除法计算成功率,避免整数除法精度丢失问题
// 例如: passedRequests=1, failedRequests=2, 则 successRate = 0.333... 而不是 0
successRate := float64(passedRequests) / float64(totalRequests) successRate := float64(passedRequests) / float64(totalRequests)
return successRate >= successThreshold return successRate >= successThreshold
} }
@@ -1106,31 +1108,73 @@ func syncCircuitBreakerStateToDistributed(ctx context.Context, resourceName, sta
g.Log().Warningf(ctx, "分布式熔断状态同步失败,跳过: %s, 最后错误: %v", lockKey, lastErr) g.Log().Warningf(ctx, "分布式熔断状态同步失败,跳过: %s, 最后错误: %v", lockKey, lastErr)
} }
// CircuitBreakerHealthCheckHandler 健康检查接口 // checkCircuitBreakerAuthToken 验证熔断器管理接口的认证Token
func CircuitBreakerHealthCheckHandler(r *ghttp.Request) { // 统一的认证逻辑,用于健康检查、重置等管理接口
// 添加认证检查使用JWT Token或API Key // 返回值:
// - true: 认证通过
// - false: 认证失败已发送401响应
func checkCircuitBreakerAuthToken(r *ghttp.Request) bool {
// 从Header中获取认证信息 // 从Header中获取认证信息
authToken := r.Header.Get("Authorization") authToken := r.Header.Get("Authorization")
// 如果Header中没有尝试从查询参数获取仅用于开发/测试环境)
// 生产环境应禁用此方式仅支持Header认证
if authToken == "" { if authToken == "" {
// 尝试从查询参数获取(仅用于开发环境)
authToken = r.Get("authToken").String() authToken = r.Get("authToken").String()
} }
// 简单的Token验证(生产环境应使用更严格的认证) // 检查Token是否为空
// 建议使用JWT或其他安全的认证机制
if authToken == "" { if authToken == "" {
g.Log().Warningf(r.GetCtx(), "熔断器健康检查被拒绝:缺少认证信息IP=%s", r.GetClientIp()) g.Log().Warningf(r.GetCtx(), "熔断器管理接口访问被拒绝:缺少认证Token, IP=%s, Path=%s",
r.GetClientIp(), r.URL.Path)
r.Response.WriteStatusExit(401, "Unauthorized: Missing authentication token") r.Response.WriteStatusExit(401, "Unauthorized: Missing authentication token")
return return false
} }
// TODO: 在这里添加真正的Token验证逻辑 // 支持Bearer Token格式
// 示例使用JWT验证 if strings.HasPrefix(authToken, "Bearer ") {
// claims, err := jwt.ParseWithClaims(authToken, &MyClaims{}) authToken = strings.TrimPrefix(authToken, "Bearer ")
}
// TODO: 实现完整的Token验证逻辑
// 建议使用JWT或其他安全机制
//
// 1. 使用gogf/gf/v2/os/gjwt进行JWT验证
// token, err := gjwt.ParseAndVerify(authToken, []byte(secret))
// if err != nil { // if err != nil {
// r.Response.WriteStatusExit(401, "Unauthorized: Invalid token") // return false
// return
// } // }
//
// 2. 或使用其他JWT库如github.com/golang-jwt/jwt/v5
// claims := &MyClaims{}
// token, err := jwt.ParseWithClaims(authToken, claims, ...)
//
// 3. 验证Token的
// - 签名有效性
// - 过期时间exp
// - 签发者iss
// - 权限范围scope/roles
//
// 4. 从Token中提取用户/服务信息存储到context供后续使用
// 当前为简化实现仅检查Token非空
// 生产环境必须替换为真实的Token验证逻辑
// 示例使用validateToken函数需在其他地方实现
// if !validateToken(authToken) {
// g.Log().Warningf(r.GetCtx(), "熔断器管理接口访问被拒绝无效的认证Token, IP=%s", r.GetClientIp())
// r.Response.WriteStatusExit(401, "Unauthorized: Invalid token")
// return false
// }
return true
}
// CircuitBreakerHealthCheckHandler 健康检查接口
func CircuitBreakerHealthCheckHandler(r *ghttp.Request) {
// 认证检查使用Token验证
if !checkCircuitBreakerAuthToken(r) {
return
}
page := r.Get("page").Int() page := r.Get("page").Int()
size := r.Get("size").Int() size := r.Get("size").Int()
@@ -1237,20 +1281,11 @@ func batchProcessResources(r *ghttp.Request, processFunc func(resourceName strin
// CircuitBreakerResetHandler 重置熔断器 // CircuitBreakerResetHandler 重置熔断器
func CircuitBreakerResetHandler(r *ghttp.Request) { func CircuitBreakerResetHandler(r *ghttp.Request) {
// 添加认证检查(与健康检查接口相同) // 认证检查
authToken := r.Header.Get("Authorization") if !checkCircuitBreakerAuthToken(r) {
if authToken == "" {
authToken = r.Get("authToken").String()
}
if authToken == "" {
g.Log().Warningf(r.GetCtx(), "熔断器重置被拒绝缺少认证信息IP=%s", r.GetClientIp())
r.Response.WriteStatusExit(401, "Unauthorized: Missing authentication token")
return return
} }
// TODO: 添加真正的Token验证逻辑
resourceName := r.Get("resource").String() resourceName := r.Get("resource").String()
if resourceName == "" || resourceName == "*" { if resourceName == "" || resourceName == "*" {

View File

@@ -30,6 +30,9 @@ func Auth(r *ghttp.Request) {
} }
// 验证 token // 验证 token
// TODO: 实现完整的JWT验证逻辑
// 当前为占位实现实际使用时应替换为真实的token验证
// 例如使用gogf/gf/v2/os/gjwt或其他JWT库进行验证
if !validateToken(gstr.SubStrFrom(token, "7")) { if !validateToken(gstr.SubStrFrom(token, "7")) {
r.Response.WriteStatusExit(401, "Unauthorized") r.Response.WriteStatusExit(401, "Unauthorized")
return return
@@ -37,3 +40,44 @@ func Auth(r *ghttp.Request) {
r.Middleware.Next() r.Middleware.Next()
} }
// validateToken 验证Token有效性
// 当前为简化实现实际生产环境应使用JWT或其他安全机制进行验证
// 示例:
// - 使用gogf/gf/v2/os/gjwt库解析和验证JWT token
// - 验证token签名、过期时间、签发者等
// - 从token中提取用户信息并存储到context
//
// 返回值:
// - true: token有效
// - false: token无效或过期
func validateToken(token string) bool {
// TODO: 实现真实的token验证逻辑
// 当前为占位实现返回true以允许基本功能运行
// 生产环境必须替换为真实的验证逻辑
// 简单的非空检查
if token == "" {
return false
}
// 建议的JWT验证示例需要引入jwt库:
/*
claims := &jwt.MapClaims{}
t, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte("your-secret-key"), nil
})
if err != nil || !t.Valid {
return false
}
// 检查过期时间
if exp, ok := (*claims)["exp"].(float64); ok {
if time.Now().Unix() > int64(exp) {
return false
}
}
*/
// 临时返回true实际使用时应实现完整验证
return true
}

View File

@@ -16,8 +16,4 @@ const (
RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1 RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1
RateLimitKeyService = "service:%s" // 服务限流: service:customerService RateLimitKeyService = "service:%s" // 服务限流: service:customerService
RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests
RateLimitKeyOrder = "order:create:%s" // 订单创建限流: order:create:123
RateLimitKeyTransfer = "wallet:transfer:%s" // 钱包转账限流: wallet:transfer:123
RateLimitKeyMessage = "cs:message:%s" // 客服消息限流: cs:message:123
RateLimitKeyUpload = "oss:upload:%s" // 文件上传限流: oss:upload:123
) )