优化ip=“” nodeid=0

This commit is contained in:
2025-11-23 03:43:53 +08:00
parent 2c1c2b0857
commit 19f224c7fa
7 changed files with 614 additions and 35 deletions

View File

@@ -8,6 +8,7 @@ import (
"io"
"net"
"net/http"
"strings"
"sync"
"time"
@@ -24,6 +25,25 @@ var taskMutex sync.RWMutex
var backendURL string
var logger *zap.Logger
// 批量推送缓冲(每个任务一个缓冲)
var pushBuffers = make(map[string]*pushBuffer)
var bufferMutex sync.RWMutex
// pushBuffer 批量推送缓冲
type pushBuffer struct {
taskID string
results []map[string]interface{}
mu sync.Mutex
lastPush time.Time
pushTimer *time.Timer
}
const (
// 批量推送配置
batchPushInterval = 1 * time.Second // 批量推送间隔1秒
batchPushMaxSize = 10 // 批量推送最大数量10个结果
)
func InitContinuousHandler(cfg *config.Config) {
backendURL = cfg.Backend.URL
logger, _ = zap.NewProduction()
@@ -202,9 +222,6 @@ func pushResultToBackend(taskID string, result map[string]interface{}) {
result["packet_loss"] = false
}
// 推送结果到后端
url := fmt.Sprintf("%s/api/public/node/continuous/result", backendURL)
// 优先使用心跳返回的节点信息
nodeID := heartbeat.GetNodeID()
nodeIP := heartbeat.GetNodeIP()
@@ -215,22 +232,132 @@ func pushResultToBackend(taskID string, result map[string]interface{}) {
logger.Debug("使用本地IP作为后备", zap.String("node_ip", nodeIP))
}
// 发送 node_id 和 node_ip后端可以通过这些信息精准匹配
// 确保已经获取到 node_id,避免发送无效数据包
if nodeID == 0 {
logger.Warn("节点ID未获取跳过推送结果",
zap.String("task_id", taskID),
zap.String("node_ip", nodeIP),
zap.String("hint", "等待心跳返回node_id后再推送"))
return
}
// 确保已经获取到 node_ip
if nodeIP == "" {
logger.Warn("节点IP未获取跳过推送结果",
zap.String("task_id", taskID),
zap.Uint("node_id", nodeID),
zap.String("hint", "等待心跳返回node_ip后再推送"))
return
}
// 添加到批量推送缓冲
addToPushBuffer(taskID, nodeID, nodeIP, result)
}
// addToPushBuffer 添加结果到批量推送缓冲
func addToPushBuffer(taskID string, nodeID uint, nodeIP string, result map[string]interface{}) {
bufferMutex.Lock()
buffer, exists := pushBuffers[taskID]
if !exists {
buffer = &pushBuffer{
taskID: taskID,
results: make([]map[string]interface{}, 0, batchPushMaxSize),
lastPush: time.Now(),
}
pushBuffers[taskID] = buffer
}
bufferMutex.Unlock()
buffer.mu.Lock()
defer buffer.mu.Unlock()
// 添加结果到缓冲
buffer.results = append(buffer.results, result)
// 如果缓冲已满,立即推送
shouldFlush := len(buffer.results) >= batchPushMaxSize
buffer.mu.Unlock()
if shouldFlush {
flushPushBuffer(taskID, nodeID, nodeIP)
return
}
buffer.mu.Lock()
// 如果距离上次推送超过间隔时间,启动定时器推送
if buffer.pushTimer == nil {
buffer.pushTimer = time.AfterFunc(batchPushInterval, func() {
flushPushBuffer(taskID, nodeID, nodeIP)
})
}
}
// flushPushBuffer 刷新并推送缓冲中的结果
func flushPushBuffer(taskID string, nodeID uint, nodeIP string) {
bufferMutex.RLock()
buffer, exists := pushBuffers[taskID]
bufferMutex.RUnlock()
if !exists {
return
}
buffer.mu.Lock()
if len(buffer.results) == 0 {
buffer.mu.Unlock()
return
}
// 复制结果列表
results := make([]map[string]interface{}, len(buffer.results))
copy(results, buffer.results)
buffer.results = buffer.results[:0] // 清空缓冲
// 停止定时器
if buffer.pushTimer != nil {
buffer.pushTimer.Stop()
buffer.pushTimer = nil
}
buffer.lastPush = time.Now()
buffer.mu.Unlock()
// 批量推送结果(目前后端只支持单个结果,所以逐个推送)
// 但可以减少HTTP请求的频率
for _, result := range results {
pushSingleResult(taskID, nodeID, nodeIP, result)
}
}
// pushSingleResult 推送单个结果到后端
func pushSingleResult(taskID string, nodeID uint, nodeIP string, result map[string]interface{}) {
// 推送结果到后端
url := fmt.Sprintf("%s/api/public/node/continuous/result", backendURL)
// 获取节点位置信息
country, province, city, isp := heartbeat.GetNodeLocation()
// 发送 node_id、node_ip 和位置信息,后端可以通过这些信息精准匹配
data := map[string]interface{}{
"task_id": taskID,
"node_id": nodeID,
"node_ip": nodeIP,
"result": result,
}
// 如果 node_id 存在,优先发送 node_id
if nodeID > 0 {
data["node_id"] = nodeID
logger.Debug("推送结果时使用存储的node_id", zap.Uint("node_id", nodeID))
// 添加位置信息(如果存在)
if country != "" {
data["country"] = country
}
// 如果 node_ip 存在,发送 node_ip
if nodeIP != "" {
data["node_ip"] = nodeIP
logger.Debug("推送结果时使用存储的node_ip", zap.String("node_ip", nodeIP))
if province != "" {
data["province"] = province
}
if city != "" {
data["city"] = city
}
if isp != "" {
data["isp"] = isp
}
jsonData, err := json.Marshal(data)
@@ -261,18 +388,110 @@ func pushResultToBackend(taskID string, result map[string]interface{}) {
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
bodyStr := string(body)
// 检查是否是任务不存在的错误
if containsTaskNotFoundError(bodyStr) {
logger.Warn("后端任务不存在,停止节点端任务",
zap.String("task_id", taskID),
zap.String("response", bodyStr))
// 停止对应的持续测试任务
stopTaskByTaskID(taskID)
return
}
logger.Warn("推送结果失败,继续运行",
zap.Int("status", resp.StatusCode),
zap.String("task_id", taskID),
zap.String("url", url),
zap.String("response", string(body)))
// 推送失败不停止任务,继续运行
zap.String("response", bodyStr))
// 其他错误不停止任务,继续运行
return
}
logger.Debug("推送结果成功", zap.String("task_id", taskID))
}
// containsTaskNotFoundError 检查响应中是否包含任务不存在的错误
func containsTaskNotFoundError(responseBody string) bool {
// 检查常见的任务不存在错误消息
errorKeywords := []string{
"找不到对应的后端任务",
"任务不存在",
"task not found",
"找不到对应的任务",
}
responseLower := strings.ToLower(responseBody)
for _, keyword := range errorKeywords {
if strings.Contains(responseLower, strings.ToLower(keyword)) {
return true
}
}
// 尝试解析 JSON 响应,检查错误消息
var resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
if err := json.Unmarshal([]byte(responseBody), &resp); err == nil {
msgLower := strings.ToLower(resp.Msg)
for _, keyword := range errorKeywords {
if strings.Contains(msgLower, strings.ToLower(keyword)) {
return true
}
}
}
return false
}
// stopTaskByTaskID 根据 taskID 停止对应的持续测试任务
func stopTaskByTaskID(taskID string) {
taskMutex.Lock()
defer taskMutex.Unlock()
task, exists := continuousTasks[taskID]
if !exists {
logger.Debug("任务不存在,无需停止", zap.String("task_id", taskID))
return
}
logger.Info("停止持续测试任务", zap.String("task_id", taskID))
// 停止任务
task.IsRunning = false
if task.pingTask != nil {
task.pingTask.Stop()
}
if task.tcpingTask != nil {
task.tcpingTask.Stop()
}
// 关闭停止通道
select {
case <-task.StopCh:
// 已经关闭
default:
close(task.StopCh)
}
// 删除任务
delete(continuousTasks, taskID)
// 清理推送缓冲
bufferMutex.Lock()
if buffer, exists := pushBuffers[taskID]; exists {
if buffer.pushTimer != nil {
buffer.pushTimer.Stop()
}
delete(pushBuffers, taskID)
}
bufferMutex.Unlock()
logger.Info("持续测试任务已停止", zap.String("task_id", taskID))
}
func getLocalIP() string {
// 简化实现返回第一个非回环IP
// 实际应该获取外网IP