feat: 网站统计添加入站实时数据

This commit is contained in:
耗子
2026-02-20 04:57:14 +08:00
parent 8bf30ee586
commit a62cff7a73
3 changed files with 26 additions and 15 deletions
+14 -8
View File
@@ -74,9 +74,10 @@ type uriCount struct {
}
type rtSlot struct {
sec int64
bandwidth uint64
requests uint64
sec int64
bandwidth uint64
bandwidthIn uint64
requests uint64
}
// NewAggregator 创建聚合器实例
@@ -271,7 +272,7 @@ func (a *Aggregator) Record(entry *LogEntry) {
a.mu.Unlock()
// 实时统计(无锁原子写入)
a.addRealtime(now.Unix(), entry.Bytes)
a.addRealtime(now.Unix(), entry.Bytes, entry.ReqLength)
}
// SiteStats 返回各站点自上次刷新以来的未刷新增量(用于实时展示叠加到 DB 数据上)
@@ -500,7 +501,7 @@ func (a *Aggregator) DrainDetailStats() (detailsByDate map[string]map[string]*Si
func (a *Aggregator) Realtime() RealtimeStats {
now := time.Now().Unix()
var totalBw, totalReq uint64
var totalBw, totalBwIn, totalReq uint64
var count int64
// 统计最近 5 秒(排除当前秒)
for i := int64(1); i <= 5; i++ {
@@ -509,6 +510,7 @@ func (a *Aggregator) Realtime() RealtimeStats {
// 校验槽位秒戳,只读取确实属于目标秒的数据
if atomic.LoadInt64(&a.rtSlots[idx].sec) == sec {
totalBw += atomic.LoadUint64(&a.rtSlots[idx].bandwidth)
totalBwIn += atomic.LoadUint64(&a.rtSlots[idx].bandwidthIn)
totalReq += atomic.LoadUint64(&a.rtSlots[idx].requests)
count++
}
@@ -519,8 +521,9 @@ func (a *Aggregator) Realtime() RealtimeStats {
}
return RealtimeStats{
Bandwidth: float64(totalBw) / float64(count),
RPS: float64(totalReq) / float64(count),
Bandwidth: float64(totalBw) / float64(count),
BandwidthIn: float64(totalBwIn) / float64(count),
RPS: float64(totalReq) / float64(count),
}
}
@@ -545,7 +548,7 @@ func (a *Aggregator) gcDrainedPastDaysLocked() {
}
// addRealtime 写入实时槽位,保证秒切换时不会因清零覆盖并发写入
func (a *Aggregator) addRealtime(sec int64, bytes uint64) {
func (a *Aggregator) addRealtime(sec int64, bytes, bytesIn uint64) {
idx := sec % 60
slot := &a.rtSlots[idx]
initMark := -sec
@@ -554,6 +557,7 @@ func (a *Aggregator) addRealtime(sec int64, bytes uint64) {
cur := atomic.LoadInt64(&slot.sec)
if cur == sec {
atomic.AddUint64(&slot.bandwidth, bytes)
atomic.AddUint64(&slot.bandwidthIn, bytesIn)
atomic.AddUint64(&slot.requests, 1)
return
}
@@ -567,9 +571,11 @@ func (a *Aggregator) addRealtime(sec int64, bytes uint64) {
// 抢到初始化权后,先清零,再发布秒戳,最后写入当前请求
if atomic.CompareAndSwapInt64(&slot.sec, cur, initMark) {
atomic.StoreUint64(&slot.bandwidth, 0)
atomic.StoreUint64(&slot.bandwidthIn, 0)
atomic.StoreUint64(&slot.requests, 0)
atomic.StoreInt64(&slot.sec, sec)
atomic.AddUint64(&slot.bandwidth, bytes)
atomic.AddUint64(&slot.bandwidthIn, bytesIn)
atomic.AddUint64(&slot.requests, 1)
return
}
+3 -2
View File
@@ -54,8 +54,9 @@ type SiteSnapshot struct {
// RealtimeStats 实时统计
type RealtimeStats struct {
Bandwidth float64 `json:"bandwidth"` // 字节/秒
RPS float64 `json:"rps"` // 请求/秒
Bandwidth float64 `json:"bandwidth"` // 出站字节/秒
BandwidthIn float64 `json:"bandwidth_in"` // 入站字节/秒
RPS float64 `json:"rps"` // 请求/秒
}
// ErrorEntry 错误日志条目
+9 -5
View File
@@ -106,7 +106,7 @@ const loadOverview = () => {
}
// 实时数据
const realtime = ref({ bandwidth: 0, rps: 0 })
const realtime = ref({ bandwidth: 0, bandwidth_in: 0, rps: 0 })
let pollTimer: ReturnType<typeof setInterval> | null = null
const loadRealtime = () => {
@@ -439,7 +439,7 @@ const trafficChartOption = computed<EChartsOption>(() => {
<n-flex vertical :size="20">
<!-- 统计卡片 -->
<n-spin :show="loading">
<div class="gap-12 grid grid-cols-3 lg:grid-cols-9 sm:grid-cols-5">
<div class="gap-12 grid grid-cols-3 lg:grid-cols-10 sm:grid-cols-5">
<n-card v-for="m in metrics" :key="m.key" :bordered="false" size="small">
<div class="flex flex-col gap-4">
<span class="text-12px text-[var(--text-color-3)]">{{ m.label }}</span>
@@ -468,12 +468,16 @@ const trafficChartOption = computed<EChartsOption>(() => {
</n-card>
<n-card :bordered="false" size="small">
<div class="flex flex-col gap-4">
<span class="text-12px text-[var(--text-color-3)]">{{
$gettext('Realtime Bandwidth')
}}</span>
<span class="text-12px text-[var(--text-color-3)]">{{ $gettext('Outbound') }}</span>
<span class="text-20px font-bold">{{ formatBytes(realtime.bandwidth) }}/s</span>
</div>
</n-card>
<n-card :bordered="false" size="small">
<div class="flex flex-col gap-4">
<span class="text-12px text-[var(--text-color-3)]">{{ $gettext('Inbound') }}</span>
<span class="text-20px font-bold">{{ formatBytes(realtime.bandwidth_in) }}/s</span>
</div>
</n-card>
<n-card :bordered="false" size="small">
<div class="flex flex-col gap-4">
<span class="text-12px text-[var(--text-color-3)]">RPS</span>