Skip to content

Commit 7439d5e

Browse files
committed
Update.
1 parent f33c746 commit 7439d5e

File tree

5 files changed

+153
-61
lines changed

5 files changed

+153
-61
lines changed

cmd/dashboard/controller/common_page.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,18 +389,22 @@ func (cp *commonPage) ws(c *gin.Context) {
389389
}
390390
defer conn.Close()
391391
count := 0
392+
393+
// 使用更高频率的单一数据流 - 每秒更新一次
392394
for {
393395
// 获取服务器状态数据
394396
stat, err := cp.getServerStat(c, false)
395397
if err != nil {
398+
time.Sleep(time.Second)
396399
continue
397400
}
398401

399-
// 增强数据:添加流量信息
402+
// 增强数据:添加最新流量信息
400403
var data Data
401404
if err := utils.Json.Unmarshal(stat, &data); err == nil {
402405
// 添加流量数据
403-
data.TrafficData = buildTrafficData()
406+
data.TrafficData = buildTrafficData() // 获取最新周期性流量数据
407+
404408
// 重新序列化
405409
enhancedStat, enhancedErr := utils.Json.Marshal(data)
406410
if enhancedErr == nil {
@@ -411,14 +415,17 @@ func (cp *commonPage) ws(c *gin.Context) {
411415
if err := conn.WriteMessage(websocket.TextMessage, stat); err != nil {
412416
break
413417
}
418+
414419
count += 1
415420
if count%4 == 0 {
416421
err = conn.WriteMessage(websocket.PingMessage, []byte{})
417422
if err != nil {
418423
break
419424
}
420425
}
421-
time.Sleep(time.Second * 2)
426+
427+
// 减少更新间隔,提高实时性
428+
time.Sleep(time.Second)
422429
}
423430
}
424431

cmd/dashboard/controller/controller.go

Lines changed: 109 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/hashicorp/go-uuid"
1919
"github.com/nicksnyder/go-i18n/v2/i18n"
2020

21+
"github.com/jinzhu/copier"
2122
"github.com/xos/serverstatus/model"
2223
"github.com/xos/serverstatus/pkg/mygin"
2324
"github.com/xos/serverstatus/pkg/utils"
@@ -367,41 +368,120 @@ func natGateway(c *gin.Context) {
367368
}
368369

369370
// buildTrafficData 构建用于前端显示的流量数据
371+
// 返回的数据符合周期配置的cycle_start和cycle_unit
370372
func buildTrafficData() []map[string]interface{} {
371-
trafficData := make([]map[string]interface{}, 0)
372-
373-
singleton.ServerLock.RLock()
374-
defer singleton.ServerLock.RUnlock()
375-
376-
// 遍历所有服务器,生成流量数据
377-
for _, server := range singleton.ServerList {
378-
// 跳过没有状态数据的服务器
379-
if server.State == nil {
380-
continue
381-
}
373+
singleton.AlertsLock.RLock()
374+
defer singleton.AlertsLock.RUnlock()
375+
376+
// 创建一个AlertsCycleTransferStatsStore的副本
377+
var statsStore map[uint64]model.CycleTransferStats
378+
copier.Copy(&statsStore, singleton.AlertsCycleTransferStatsStore)
379+
380+
// 从statsStore构建流量数据
381+
var trafficData []map[string]interface{}
382+
383+
if statsStore != nil {
384+
for cycleID, stats := range statsStore {
385+
// 查找对应的Alert规则,用于获取周期设置
386+
var alert *model.AlertRule
387+
for _, a := range singleton.Alerts {
388+
if a.ID == cycleID {
389+
alert = a
390+
break
391+
}
392+
}
382393

383-
// 计算已使用流量和总流量
384-
traffic := map[string]interface{}{
385-
"server_id": server.ID,
386-
"server_name": server.Name,
387-
"cycle_name": "Monthly", // 默认周期名称
394+
// 如果找不到Alert规则,跳过此项
395+
if alert == nil {
396+
continue
397+
}
388398

389-
// 使用字节数据作为源
390-
"is_bytes_source": true,
391-
"used_bytes": server.State.NetInTransfer + server.State.NetOutTransfer,
392-
"max_bytes": uint64(1099511627776), // 默认1TB限额,可根据实际情况调整
399+
// 找到与流量相关的Rule
400+
var flowRule *model.Rule
401+
for i := range alert.Rules {
402+
if alert.Rules[i].IsTransferDurationRule() {
403+
flowRule = &alert.Rules[i]
404+
break
405+
}
406+
}
393407

394-
// 提供格式化的流量字符串,兼容旧版本
395-
"used_formatted": formatBytes(server.State.NetInTransfer + server.State.NetOutTransfer),
396-
"max_formatted": "1TB",
397-
}
408+
// 如果没有流量相关规则,跳过
409+
if flowRule == nil {
410+
continue
411+
}
398412

399-
// 计算使用百分比
400-
if traffic["max_bytes"].(uint64) > 0 {
401-
traffic["used_percent"] = float64(traffic["used_bytes"].(uint64)) / float64(traffic["max_bytes"].(uint64)) * 100
413+
// 确保周期开始和结束时间正确设置
414+
from := flowRule.GetTransferDurationStart()
415+
to := flowRule.GetTransferDurationEnd()
416+
417+
// 更新stats中的周期时间,确保与规则一致
418+
stats.From = from
419+
stats.To = to
420+
stats.Max = uint64(flowRule.Max)
421+
stats.Name = alert.Name
422+
423+
// 更新主存储(需要写锁)
424+
go func(id uint64, start, end time.Time, max uint64, name string) {
425+
singleton.AlertsLock.Lock()
426+
defer singleton.AlertsLock.Unlock()
427+
if store, ok := singleton.AlertsCycleTransferStatsStore[id]; ok {
428+
store.From = start
429+
store.To = end
430+
store.Max = max
431+
store.Name = name
432+
}
433+
}(cycleID, from, to, uint64(flowRule.Max), alert.Name)
434+
435+
// 生成流量数据条目
436+
for serverID, transfer := range stats.Transfer {
437+
serverName := ""
438+
if stats.ServerName != nil {
439+
if name, exists := stats.ServerName[serverID]; exists {
440+
serverName = name
441+
}
442+
}
443+
444+
// 如果没有名称,尝试从ServerList获取
445+
if serverName == "" {
446+
singleton.ServerLock.RLock()
447+
if server := singleton.ServerList[serverID]; server != nil {
448+
serverName = server.Name
449+
}
450+
singleton.ServerLock.RUnlock()
451+
}
452+
453+
// 计算使用百分比
454+
usedPercent := float64(0)
455+
if stats.Max > 0 {
456+
usedPercent = (float64(transfer) / float64(stats.Max)) * 100
457+
usedPercent = math.Max(0, math.Min(100, usedPercent)) // 限制在0-100范围
458+
}
459+
460+
// 获取周期单位和开始时间,用于前端展示
461+
cycleUnit := flowRule.CycleUnit
462+
463+
// 构建完整的流量数据项,包含周期信息
464+
trafficItem := map[string]interface{}{
465+
"server_id": serverID,
466+
"server_name": serverName,
467+
"max_bytes": stats.Max,
468+
"used_bytes": transfer,
469+
"max_formatted": formatBytes(stats.Max),
470+
"used_formatted": formatBytes(transfer),
471+
"used_percent": math.Round(usedPercent*100) / 100,
472+
"cycle_name": stats.Name,
473+
"cycle_id": strconv.FormatUint(cycleID, 10),
474+
"cycle_start": stats.From.Format(time.RFC3339),
475+
"cycle_end": stats.To.Format(time.RFC3339),
476+
"cycle_unit": cycleUnit,
477+
"cycle_interval": flowRule.CycleInterval,
478+
"is_bytes_source": true,
479+
"now": time.Now().Unix() * 1000,
480+
}
481+
482+
trafficData = append(trafficData, trafficItem)
483+
}
402484
}
403-
404-
trafficData = append(trafficData, traffic)
405485
}
406486

407487
return trafficData

resource/static/main.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -807,16 +807,19 @@ function initWebSocket() {
807807
}
808808
}
809809

810-
// 处理流量数据更新
810+
// 处理流量数据更新 - 流量数据可能单独发送或与服务器状态一起发送
811811
if (data.trafficData && Array.isArray(data.trafficData)) {
812812
window.serverTrafficRawData = data.trafficData;
813813
if (window.trafficManager) {
814814
window.trafficManager.processTrafficData(data.trafficData);
815815
} else {
816816
window.extractTrafficData();
817817
}
818-
// 打印流量数据更新日志
819-
console.log('已通过WebSocket接收流量数据:', data.trafficData.length, '个服务器');
818+
// 打印流量数据调试信息,只显示部分数据
819+
if (data.trafficData.length > 0) {
820+
const sample = data.trafficData[0];
821+
console.debug(`流量数据更新: ${data.trafficData.length}个服务器, 示例(ID:${sample.server_id}): ${sample.used_formatted}/${sample.max_formatted}`);
822+
}
820823
}
821824
} catch (e) {
822825
console.error('处理WebSocket消息时出错:', e);
@@ -873,6 +876,9 @@ window.extractTrafficData = function() {
873876
maxBytes = item.max_bytes;
874877
usedBytes = item.used_bytes;
875878

879+
// 记录调试信息
880+
console.debug(`服务器 ${serverId} 流量数据更新: ${usedBytes}/${maxBytes} 字节`);
881+
876882
// 从字节数据计算百分比
877883
if (maxBytes > 0) {
878884
percent = (usedBytes / maxBytes) * 100;
@@ -911,6 +917,10 @@ window.extractTrafficData = function() {
911917
usedBytes: usedBytes,
912918
serverName: serverName,
913919
cycleName: item.cycle_name || "Unknown",
920+
cycleStart: item.cycle_start,
921+
cycleEnd: item.cycle_end,
922+
cycleUnit: item.cycle_unit,
923+
cycleInterval: item.cycle_interval,
914924
lastUpdate: Date.now(),
915925
isBytesSource: item.is_bytes_source || false
916926
};
@@ -1100,6 +1110,10 @@ class TrafficManager {
11001110
usedBytes: usedBytes,
11011111
serverName: item.server_name || 'Unknown',
11021112
cycleName: item.cycle_name || 'Default',
1113+
cycleStart: item.cycle_start,
1114+
cycleEnd: item.cycle_end,
1115+
cycleUnit: item.cycle_unit,
1116+
cycleInterval: item.cycle_interval,
11031117
lastUpdate: Date.now(),
11041118
isBytesSource: item.is_bytes_source || false // 记录数据源类型
11051119
};

service/singleton/alertsentinel.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,9 @@ func checkStatus() {
206206
// UpdateTrafficStats 更新服务器流量统计到AlertsCycleTransferStatsStore
207207
// 这个函数直接更新流量数据,确保前端显示正确
208208
func UpdateTrafficStats(serverID uint64, inTransfer, outTransfer uint64) {
209-
AlertsLock.Lock()
210-
defer AlertsLock.Unlock()
209+
// 使用轻量级的锁定以提高效率
210+
AlertsLock.RLock()
211+
defer AlertsLock.RUnlock()
211212

212213
// 没有报警规则时,不需要更新
213214
if len(Alerts) == 0 || AlertsCycleTransferStatsStore == nil {
@@ -216,18 +217,15 @@ func UpdateTrafficStats(serverID uint64, inTransfer, outTransfer uint64) {
216217

217218
// 查找服务器名称
218219
var serverName string
219-
if ServerList != nil {
220-
if server := ServerList[serverID]; server != nil {
221-
serverName = server.Name
222-
}
220+
ServerLock.RLock()
221+
if server := ServerList[serverID]; server != nil {
222+
serverName = server.Name
223223
}
224+
ServerLock.RUnlock()
224225

225226
// 流量总计
226227
totalTransfer := inTransfer + outTransfer
227228

228-
// 记录已更新的规则ID,避免在日志中显示多次
229-
var updatedRules []uint64
230-
231229
// 遍历所有报警规则,只更新包含此服务器的规则
232230
for _, alert := range Alerts {
233231
if !alert.Enabled() {
@@ -243,7 +241,6 @@ func UpdateTrafficStats(serverID uint64, inTransfer, outTransfer uint64) {
243241
// 检查是否包含流量监控规则
244242
for j := 0; j < len(alert.Rules); j++ {
245243
if alert.Rules[j].IsTransferDurationRule() {
246-
247244
// 检查此规则是否监控该服务器
248245
if alert.Rules[j].Cover == model.RuleCoverAll {
249246
// 监控全部服务器但排除了此服务器
@@ -258,23 +255,17 @@ func UpdateTrafficStats(serverID uint64, inTransfer, outTransfer uint64) {
258255
}
259256

260257
// 服务器在规则监控范围内,更新流量数据
261-
currentTransfer, exists := stats.Transfer[serverID]
262-
263-
// 如果新值大于当前值,或者当前不存在,则更新
264-
if !exists || totalTransfer > currentTransfer {
265-
stats.Transfer[serverID] = totalTransfer
266-
267-
// 更新服务器名称
268-
if serverName != "" && stats.ServerName[serverID] != serverName {
269-
stats.ServerName[serverID] = serverName
270-
}
258+
// 不论大小如何,总是更新最新值
259+
stats.Transfer[serverID] = totalTransfer
271260

272-
// 添加到已更新规则列表
273-
if !containsUint64(updatedRules, alert.ID) {
274-
updatedRules = append(updatedRules, alert.ID)
275-
}
261+
// 更新服务器名称
262+
if serverName != "" {
263+
stats.ServerName[serverID] = serverName
276264
}
277265

266+
// 更新最后更新时间
267+
stats.NextUpdate[serverID] = time.Now()
268+
278269
// 找到一个满足条件的规则即可退出循环
279270
break
280271
}

service/singleton/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ func UpdateServer(s *model.Server) error {
284284
server.LastFlowSaveTime = time.Now()
285285
}
286286

287-
// 更新用于前端显示的流量数据,确保前端能正确显示流量使用情况
288-
// 这是修复流量数据不更新的关键函数调用
287+
// 确保更新用于前端显示的流量数据
288+
// 注意:总是更新每次流量变化,不要等待时间间隔
289289
UpdateTrafficStats(s.ID, s.CumulativeNetInTransfer, s.CumulativeNetOutTransfer)
290290
}
291291

0 commit comments

Comments
 (0)