github.com/TeaOSLab/EdgeNode@v1.3.8/internal/stats/traffic_stat_manager.go (about) 1 package stats 2 3 import ( 4 "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" 5 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 6 "github.com/TeaOSLab/EdgeNode/internal/events" 7 "github.com/TeaOSLab/EdgeNode/internal/goman" 8 "github.com/TeaOSLab/EdgeNode/internal/monitor" 9 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 10 "github.com/TeaOSLab/EdgeNode/internal/rpc" 11 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 12 "github.com/iwind/TeaGo/Tea" 13 "github.com/iwind/TeaGo/maps" 14 "github.com/iwind/TeaGo/types" 15 "sort" 16 "strconv" 17 "strings" 18 "sync" 19 "time" 20 ) 21 22 var SharedTrafficStatManager = NewTrafficStatManager() 23 24 type TrafficItem struct { 25 UserId int64 26 Bytes int64 27 CachedBytes int64 28 CountRequests int64 29 CountCachedRequests int64 30 CountAttackRequests int64 31 AttackBytes int64 32 PlanId int64 33 CheckingTrafficLimit bool 34 } 35 36 func (this *TrafficItem) Add(anotherItem *TrafficItem) { 37 this.Bytes += anotherItem.Bytes 38 this.CachedBytes += anotherItem.CachedBytes 39 this.CountRequests += anotherItem.CountRequests 40 this.CountCachedRequests += anotherItem.CountCachedRequests 41 this.CountAttackRequests += anotherItem.CountAttackRequests 42 this.AttackBytes += anotherItem.AttackBytes 43 } 44 45 // TrafficStatManager 区域流量统计 46 type TrafficStatManager struct { 47 itemMap map[string]*TrafficItem // [timestamp serverId] => *TrafficItem 48 domainsMap map[int64]map[string]*TrafficItem // serverIde => { timestamp @ domain => *TrafficItem } 49 50 pbItems []*pb.ServerDailyStat 51 pbDomainItems []*pb.UploadServerDailyStatsRequest_DomainStat 52 53 locker sync.Mutex 54 55 totalRequests int64 56 } 57 58 // NewTrafficStatManager 获取新对象 59 func NewTrafficStatManager() *TrafficStatManager { 60 var manager = &TrafficStatManager{ 61 itemMap: map[string]*TrafficItem{}, 62 domainsMap: map[int64]map[string]*TrafficItem{}, 63 } 64 65 return manager 66 } 67 68 // Start 启动自动任务 69 func (this *TrafficStatManager) Start() { 70 // 上传请求总数 71 var monitorTicker = time.NewTicker(1 * time.Minute) 72 events.OnKey(events.EventQuit, this, func() { 73 monitorTicker.Stop() 74 }) 75 goman.New(func() { 76 for range monitorTicker.C { 77 if this.totalRequests > 0 { 78 monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemRequests, maps.Map{"total": this.totalRequests}) 79 this.totalRequests = 0 80 } 81 } 82 }) 83 84 // 上传统计数据 85 var duration = 5 * time.Minute 86 if Tea.IsTesting() { 87 // 测试环境缩短上传时间,方便我们调试 88 duration = 30 * time.Second 89 } 90 var ticker = time.NewTicker(duration) 91 events.OnKey(events.EventQuit, this, func() { 92 remotelogs.Println("TRAFFIC_STAT_MANAGER", "quit") 93 ticker.Stop() 94 }) 95 remotelogs.Println("TRAFFIC_STAT_MANAGER", "start ...") 96 for range ticker.C { 97 err := this.Upload() 98 if err != nil { 99 if !rpc.IsConnError(err) { 100 remotelogs.Error("TRAFFIC_STAT_MANAGER", "upload stats failed: "+err.Error()) 101 } else { 102 remotelogs.Warn("TRAFFIC_STAT_MANAGER", "upload stats failed: "+err.Error()) 103 } 104 } 105 } 106 } 107 108 // Add 添加流量 109 func (this *TrafficStatManager) Add(userId int64, serverId int64, domain string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttacks int64, attackBytes int64, countWebsocketConnections int64, checkingTrafficLimit bool, planId int64) { 110 if serverId == 0 { 111 return 112 } 113 114 // 添加到带宽 115 SharedBandwidthStatManager.AddTraffic(serverId, cachedBytes, countRequests, countCachedRequests, countAttacks, attackBytes, countWebsocketConnections) 116 117 if bytes == 0 && countRequests == 0 { 118 return 119 } 120 121 this.totalRequests++ 122 123 var timestamp = fasttime.Now().UnixFloor(300) 124 var key = strconv.FormatInt(timestamp, 10) + strconv.FormatInt(serverId, 10) 125 this.locker.Lock() 126 127 // 总的流量 128 item, ok := this.itemMap[key] 129 if !ok { 130 item = &TrafficItem{ 131 UserId: userId, 132 } 133 this.itemMap[key] = item 134 } 135 item.Bytes += bytes 136 item.CachedBytes += cachedBytes 137 item.CountRequests += countRequests 138 item.CountCachedRequests += countCachedRequests 139 item.CountAttackRequests += countAttacks 140 item.AttackBytes += attackBytes 141 item.CheckingTrafficLimit = checkingTrafficLimit 142 item.PlanId = planId 143 144 // 单个域名流量 145 if len(domain) < 128 { 146 var domainKey = types.String(timestamp) + "@" + domain 147 serverDomainMap, ok := this.domainsMap[serverId] 148 if !ok { 149 serverDomainMap = map[string]*TrafficItem{} 150 this.domainsMap[serverId] = serverDomainMap 151 } 152 153 domainItem, ok := serverDomainMap[domainKey] 154 if !ok { 155 domainItem = &TrafficItem{} 156 serverDomainMap[domainKey] = domainItem 157 } 158 domainItem.Bytes += bytes 159 domainItem.CachedBytes += cachedBytes 160 domainItem.CountRequests += countRequests 161 domainItem.CountCachedRequests += countCachedRequests 162 domainItem.CountAttackRequests += countAttacks 163 domainItem.AttackBytes += attackBytes 164 } 165 166 this.locker.Unlock() 167 } 168 169 // Upload 上传流量 170 func (this *TrafficStatManager) Upload() error { 171 var regionId int64 172 nodeConfig, _ := nodeconfigs.SharedNodeConfig() 173 if nodeConfig != nil { 174 regionId = nodeConfig.RegionId 175 } 176 177 client, err := rpc.SharedRPC() 178 if err != nil { 179 return err 180 } 181 182 this.locker.Lock() 183 184 var itemMap = this.itemMap 185 var domainMap = this.domainsMap 186 187 // reset 188 this.itemMap = map[string]*TrafficItem{} 189 this.domainsMap = map[int64]map[string]*TrafficItem{} 190 191 this.locker.Unlock() 192 193 // 服务统计 194 var pbServerStats = []*pb.ServerDailyStat{} 195 for key, item := range itemMap { 196 timestamp, err := strconv.ParseInt(key[:10], 10, 64) 197 if err != nil { 198 return err 199 } 200 serverId, err := strconv.ParseInt(key[10:], 10, 64) 201 if err != nil { 202 return err 203 } 204 205 pbServerStats = append(pbServerStats, &pb.ServerDailyStat{ 206 UserId: item.UserId, 207 ServerId: serverId, 208 NodeRegionId: regionId, 209 Bytes: item.Bytes, 210 CachedBytes: item.CachedBytes, 211 CountRequests: item.CountRequests, 212 CountCachedRequests: item.CountCachedRequests, 213 CountAttackRequests: item.CountAttackRequests, 214 AttackBytes: item.AttackBytes, 215 CheckTrafficLimiting: item.CheckingTrafficLimit, 216 PlanId: item.PlanId, 217 CreatedAt: timestamp, 218 }) 219 } 220 221 // 域名统计 222 const maxDomainsPerServer = 20 223 var pbDomainStats = []*pb.UploadServerDailyStatsRequest_DomainStat{} 224 for serverId, serverDomainMap := range domainMap { 225 // 如果超过单个服务最大值,则只取前N个 226 var shouldTrim = len(serverDomainMap) > maxDomainsPerServer 227 var tempItems []*pb.UploadServerDailyStatsRequest_DomainStat 228 229 for key, item := range serverDomainMap { 230 var pieces = strings.SplitN(key, "@", 2) 231 if len(pieces) != 2 { 232 continue 233 } 234 235 // 修正数据 236 if item.CachedBytes > item.Bytes || item.CountCachedRequests == item.CountRequests { 237 item.CachedBytes = item.Bytes 238 } 239 240 var pbItem = &pb.UploadServerDailyStatsRequest_DomainStat{ 241 ServerId: serverId, 242 Domain: pieces[1], 243 Bytes: item.Bytes, 244 CachedBytes: item.CachedBytes, 245 CountRequests: item.CountRequests, 246 CountCachedRequests: item.CountCachedRequests, 247 CountAttackRequests: item.CountAttackRequests, 248 AttackBytes: item.AttackBytes, 249 CreatedAt: types.Int64(pieces[0]), 250 } 251 if !shouldTrim { 252 pbDomainStats = append(pbDomainStats, pbItem) 253 } else { 254 tempItems = append(tempItems, pbItem) 255 } 256 } 257 258 if shouldTrim { 259 sort.Slice(tempItems, func(i, j int) bool { 260 return tempItems[i].CountRequests > tempItems[j].CountRequests 261 }) 262 263 pbDomainStats = append(pbDomainStats, tempItems[:maxDomainsPerServer]...) 264 } 265 } 266 267 // 历史未提交记录 268 if len(this.pbItems) > 0 || len(this.pbDomainItems) > 0 { 269 var expiredAt = time.Now().Unix() - 1200 // 只保留20分钟 270 271 for _, item := range this.pbItems { 272 if item.CreatedAt > expiredAt { 273 pbServerStats = append(pbServerStats, item) 274 } 275 } 276 this.pbItems = nil 277 278 for _, item := range this.pbDomainItems { 279 if item.CreatedAt > expiredAt { 280 pbDomainStats = append(pbDomainStats, item) 281 } 282 } 283 this.pbDomainItems = nil 284 } 285 286 if len(pbServerStats) == 0 && len(pbDomainStats) == 0 { 287 return nil 288 } 289 290 _, err = client.ServerDailyStatRPC.UploadServerDailyStats(client.Context(), &pb.UploadServerDailyStatsRequest{ 291 Stats: pbServerStats, 292 DomainStats: pbDomainStats, 293 }) 294 if err != nil { 295 // 加回历史记录 296 this.pbItems = pbServerStats 297 this.pbDomainItems = pbDomainStats 298 299 return err 300 } 301 302 return nil 303 }