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
}
var avgLatency int64
if latency, ok := stats["avg_latency"]; ok && latency != nil {
avgLatency = latency.(int64)
// 添加安全的类型转换函数
safeInt64 := func(v interface{}) 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{
Uptime: uptime.String(),
ActiveRequests: stats["active_requests"].(int64),
TotalRequests: stats["total_requests"].(int64),
TotalErrors: stats["total_errors"].(int64),
ErrorRate: float64(stats["total_errors"].(int64)) / float64(stats["total_requests"].(int64)),
NumGoroutine: stats["num_goroutine"].(int),
ActiveRequests: safeInt64(stats["active_requests"]),
TotalRequests: totalRequests,
TotalErrors: safeInt64(stats["total_errors"]),
ErrorRate: float64(safeInt64(stats["total_errors"])) / float64(max(totalRequests, 1)),
NumGoroutine: safeInt(stats["num_goroutine"]),
MemoryUsage: stats["memory_usage"].(string),
AverageResponseTime: metrics.FormatDuration(time.Duration(avgLatency)),
TotalBytes: stats["total_bytes"].(int64),
BytesPerSecond: float64(stats["total_bytes"].(int64)) / metrics.Max(uptime.Seconds(), 1),
RequestsPerSecond: float64(stats["total_requests"].(int64)) / metrics.Max(uptime.Seconds(), 1),
AverageResponseTime: metrics.FormatDuration(time.Duration(safeInt64(stats["avg_latency"]))),
TotalBytes: safeInt64(stats["total_bytes"]),
BytesPerSecond: float64(safeInt64(stats["total_bytes"])) / metrics.Max(uptime.Seconds(), 1),
RequestsPerSecond: float64(totalRequests) / metrics.Max(uptime.Seconds(), 1),
StatusCodeStats: stats["status_code_stats"].(map[string]int64),
TopPaths: stats["top_paths"].([]models.PathMetrics),
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 = `
<!DOCTYPE html>

View File

@ -6,6 +6,7 @@ import (
"log"
"net/http"
"net/url"
"path"
"proxy-go/internal/config"
"proxy-go/internal/metrics"
"proxy-go/internal/utils"
@ -22,7 +23,8 @@ const (
var bufferPool = sync.Pool{
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
}
// 确定基础URL
targetBase := utils.GetTargetURL(h.client, r, pathConfig, decodedPath)
// 确定标基础URL
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, "/")
@ -223,8 +234,10 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 大响应使用流式传输
var bytesCopied int64
if f, ok := w.(http.Flusher); ok {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
bufPtr := bufferPool.Get().(*[]byte)
defer bufferPool.Put(bufPtr)
buf := *bufPtr
for {
n, rerr := resp.Body.Read(buf)
if n > 0 {