Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package securityaudit
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const latencySampleCapacity = 2048
|
||||
|
||||
type AtomicMetrics struct {
|
||||
total atomic.Int64
|
||||
allowed atomic.Int64
|
||||
flagged atomic.Int64
|
||||
blocked atomic.Int64
|
||||
unavailable atomic.Int64
|
||||
invalid atomic.Int64
|
||||
timeouts atomic.Int64
|
||||
failovers atomic.Int64
|
||||
bulkheadFull atomic.Int64
|
||||
recordFailed atomic.Int64
|
||||
latencyTotal atomic.Int64
|
||||
latencyMax atomic.Int64
|
||||
enqueued atomic.Int64
|
||||
dropped atomic.Int64
|
||||
latencyMu sync.RWMutex
|
||||
latencies []int64
|
||||
latencyNext int
|
||||
}
|
||||
|
||||
func NewAtomicMetrics() *AtomicMetrics { return &AtomicMetrics{} }
|
||||
|
||||
func (m *AtomicMetrics) Snapshot() GuardMetricsSnapshot {
|
||||
if m == nil {
|
||||
return GuardMetricsSnapshot{}
|
||||
}
|
||||
snapshot := GuardMetricsSnapshot{
|
||||
Total: m.total.Load(), Allowed: m.allowed.Load(), Flagged: m.flagged.Load(),
|
||||
Blocked: m.blocked.Load(), Unavailable: m.unavailable.Load(), Invalid: m.invalid.Load(),
|
||||
Timeouts: m.timeouts.Load(), Failovers: m.failovers.Load(), BulkheadFull: m.bulkheadFull.Load(),
|
||||
RecordFailed: m.recordFailed.Load(), LatencyCount: m.total.Load(), LatencyMaxMS: m.latencyMax.Load(),
|
||||
}
|
||||
if snapshot.LatencyCount > 0 {
|
||||
snapshot.LatencyAvgMS = m.latencyTotal.Load() / snapshot.LatencyCount
|
||||
}
|
||||
m.latencyMu.RLock()
|
||||
samples := append([]int64(nil), m.latencies...)
|
||||
m.latencyMu.RUnlock()
|
||||
if len(samples) > 0 {
|
||||
sort.Slice(samples, func(i, j int) bool { return samples[i] < samples[j] })
|
||||
snapshot.LatencyP50MS = percentile(samples, 0.50)
|
||||
snapshot.LatencyP95MS = percentile(samples, 0.95)
|
||||
snapshot.LatencyP99MS = percentile(samples, 0.99)
|
||||
}
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func (m *AtomicMetrics) AuditSnapshot() AuditMetricsSnapshot {
|
||||
if m == nil {
|
||||
return AuditMetricsSnapshot{}
|
||||
}
|
||||
return AuditMetricsSnapshot{Enqueued: m.enqueued.Load(), Dropped: m.dropped.Load()}
|
||||
}
|
||||
|
||||
func (m *AtomicMetrics) Observe(kind DecisionKind, latency time.Duration) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
m.total.Add(1)
|
||||
latencyMS := latency.Milliseconds()
|
||||
if latencyMS < 0 {
|
||||
latencyMS = 0
|
||||
}
|
||||
m.latencyTotal.Add(latencyMS)
|
||||
for current := m.latencyMax.Load(); latencyMS > current && !m.latencyMax.CompareAndSwap(current, latencyMS); current = m.latencyMax.Load() {
|
||||
}
|
||||
m.latencyMu.Lock()
|
||||
if len(m.latencies) < latencySampleCapacity {
|
||||
m.latencies = append(m.latencies, latencyMS)
|
||||
} else {
|
||||
m.latencies[m.latencyNext] = latencyMS
|
||||
m.latencyNext = (m.latencyNext + 1) % latencySampleCapacity
|
||||
}
|
||||
m.latencyMu.Unlock()
|
||||
switch kind {
|
||||
case DecisionFlag:
|
||||
m.flagged.Add(1)
|
||||
case DecisionBlock:
|
||||
m.blocked.Add(1)
|
||||
case DecisionUnavailable:
|
||||
m.unavailable.Add(1)
|
||||
case DecisionInvalid:
|
||||
m.invalid.Add(1)
|
||||
default:
|
||||
m.allowed.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func percentile(sorted []int64, quantile float64) int64 {
|
||||
if len(sorted) == 0 {
|
||||
return 0
|
||||
}
|
||||
index := int(float64(len(sorted)-1) * quantile)
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
if index >= len(sorted) {
|
||||
index = len(sorted) - 1
|
||||
}
|
||||
return sorted[index]
|
||||
}
|
||||
|
||||
func (m *AtomicMetrics) IncEnqueued() {
|
||||
if m != nil {
|
||||
m.enqueued.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AtomicMetrics) IncDropped() {
|
||||
if m != nil {
|
||||
m.dropped.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AtomicMetrics) IncTimeout() {
|
||||
if m != nil {
|
||||
m.timeouts.Add(1)
|
||||
}
|
||||
}
|
||||
func (m *AtomicMetrics) IncFailover() {
|
||||
if m != nil {
|
||||
m.failovers.Add(1)
|
||||
}
|
||||
}
|
||||
func (m *AtomicMetrics) IncBulkheadFull() {
|
||||
if m != nil {
|
||||
m.bulkheadFull.Add(1)
|
||||
}
|
||||
}
|
||||
func (m *AtomicMetrics) IncRecordFailed() {
|
||||
if m != nil {
|
||||
m.recordFailed.Add(1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user