feat(metrics): improve metrics handling with safe type conversions and enhanced request statistics

- Introduced safe type conversion functions to prevent panics when accessing metrics data.
- Updated MetricsHandler to utilize these functions for retrieving active requests, total requests, total errors, and average response time.
- Enhanced error rate calculation to avoid division by zero.
- Refactored buffer pool management in ProxyHandler for better memory handling.
- Improved target URL determination logic based on file extensions for more flexible proxy behavior.
This commit is contained in:
wood chen 2024-12-03 18:11:29 +08:00
parent 1db0e6ae98
commit 9602034f9d
2 changed files with 55 additions and 17 deletions

View File

@ -47,23 +47,40 @@ func (h *ProxyHandler) MetricsHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
var avgLatency int64 // 添加安全的类型转换函数
if latency, ok := stats["avg_latency"]; ok && latency != nil { safeInt64 := func(v interface{}) int64 {
avgLatency = latency.(int64) if v == nil {
return 0
}
if i, ok := v.(int64); ok {
return i
}
return 0
} }
safeInt := func(v interface{}) int {
if v == nil {
return 0
}
if i, ok := v.(int); ok {
return i
}
return 0
}
totalRequests := safeInt64(stats["total_requests"])
metrics := Metrics{ metrics := Metrics{
Uptime: uptime.String(), Uptime: uptime.String(),
ActiveRequests: stats["active_requests"].(int64), ActiveRequests: safeInt64(stats["active_requests"]),
TotalRequests: stats["total_requests"].(int64), TotalRequests: totalRequests,
TotalErrors: stats["total_errors"].(int64), TotalErrors: safeInt64(stats["total_errors"]),
ErrorRate: float64(stats["total_errors"].(int64)) / float64(stats["total_requests"].(int64)), ErrorRate: float64(safeInt64(stats["total_errors"])) / float64(max(totalRequests, 1)),
NumGoroutine: stats["num_goroutine"].(int), NumGoroutine: safeInt(stats["num_goroutine"]),
MemoryUsage: stats["memory_usage"].(string), MemoryUsage: stats["memory_usage"].(string),
AverageResponseTime: metrics.FormatDuration(time.Duration(avgLatency)), AverageResponseTime: metrics.FormatDuration(time.Duration(safeInt64(stats["avg_latency"]))),
TotalBytes: stats["total_bytes"].(int64), TotalBytes: safeInt64(stats["total_bytes"]),
BytesPerSecond: float64(stats["total_bytes"].(int64)) / metrics.Max(uptime.Seconds(), 1), BytesPerSecond: float64(safeInt64(stats["total_bytes"])) / metrics.Max(uptime.Seconds(), 1),
RequestsPerSecond: float64(stats["total_requests"].(int64)) / metrics.Max(uptime.Seconds(), 1), RequestsPerSecond: float64(totalRequests) / metrics.Max(uptime.Seconds(), 1),
StatusCodeStats: stats["status_code_stats"].(map[string]int64), StatusCodeStats: stats["status_code_stats"].(map[string]int64),
TopPaths: stats["top_paths"].([]models.PathMetrics), TopPaths: stats["top_paths"].([]models.PathMetrics),
RecentRequests: stats["recent_requests"].([]models.RequestLog), RecentRequests: stats["recent_requests"].([]models.RequestLog),
@ -76,6 +93,14 @@ func (h *ProxyHandler) MetricsHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
// 辅助函数
func max(a, b int64) int64 {
if a > b {
return a
}
return b
}
// 修改模板,添加登录页面 // 修改模板,添加登录页面
var loginTemplate = ` var loginTemplate = `
<!DOCTYPE html> <!DOCTYPE html>

View File

@ -6,6 +6,7 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"path"
"proxy-go/internal/config" "proxy-go/internal/config"
"proxy-go/internal/metrics" "proxy-go/internal/metrics"
"proxy-go/internal/utils" "proxy-go/internal/utils"
@ -22,7 +23,8 @@ const (
var bufferPool = sync.Pool{ var bufferPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return make([]byte, defaultBufferSize) buf := make([]byte, defaultBufferSize)
return &buf
}, },
} }
@ -108,8 +110,17 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
// 确定基础URL // 确定标基础URL
targetBase := utils.GetTargetURL(h.client, r, pathConfig, decodedPath) targetBase := pathConfig.DefaultTarget
// 检查文件扩展名
if pathConfig.ExtensionMap != nil {
ext := strings.ToLower(path.Ext(decodedPath))
if ext != "" {
ext = ext[1:] // 移除开头的点
targetBase = pathConfig.GetTargetForExt(ext)
}
}
// 重新编码路径,保留 '/' // 重新编码路径,保留 '/'
parts := strings.Split(decodedPath, "/") parts := strings.Split(decodedPath, "/")
@ -223,8 +234,10 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 大响应使用流式传输 // 大响应使用流式传输
var bytesCopied int64 var bytesCopied int64
if f, ok := w.(http.Flusher); ok { if f, ok := w.(http.Flusher); ok {
buf := bufferPool.Get().([]byte) bufPtr := bufferPool.Get().(*[]byte)
defer bufferPool.Put(buf) defer bufferPool.Put(bufPtr)
buf := *bufPtr
for { for {
n, rerr := resp.Body.Read(buf) n, rerr := resp.Body.Read(buf)
if n > 0 { if n > 0 {