github.com/TeaOSLab/EdgeNode@v1.3.8/internal/stats/http_request_stat_manager.go (about) 1 package stats 2 3 import ( 4 iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary" 5 "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" 6 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 7 "github.com/TeaOSLab/EdgeNode/internal/events" 8 "github.com/TeaOSLab/EdgeNode/internal/goman" 9 "github.com/TeaOSLab/EdgeNode/internal/monitor" 10 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 11 "github.com/TeaOSLab/EdgeNode/internal/rpc" 12 "github.com/TeaOSLab/EdgeNode/internal/trackers" 13 "github.com/TeaOSLab/EdgeNode/internal/utils" 14 "github.com/TeaOSLab/EdgeNode/internal/utils/agents" 15 "github.com/TeaOSLab/EdgeNode/internal/waf" 16 "github.com/iwind/TeaGo/Tea" 17 "github.com/iwind/TeaGo/maps" 18 "github.com/iwind/TeaGo/types" 19 timeutil "github.com/iwind/TeaGo/utils/time" 20 "sort" 21 "strconv" 22 "strings" 23 "sync" 24 "time" 25 ) 26 27 type StatItem struct { 28 Bytes int64 29 CountRequests int64 30 CountAttackRequests int64 31 AttackBytes int64 32 } 33 34 var SharedHTTPRequestStatManager = NewHTTPRequestStatManager() 35 36 // HTTPRequestStatManager HTTP请求相关的统计 37 // 这里的统计是一个辅助统计,注意不要因为统计而影响服务工作性能 38 type HTTPRequestStatManager struct { 39 ipChan chan string 40 userAgentChan chan string 41 firewallRuleGroupChan chan string 42 43 cityMap map[string]*StatItem // serverId@country@province@city => *StatItem ,不需要加锁,因为我们是使用channel依次执行的 44 providerMap map[string]int64 // serverId@provider => count 45 systemMap map[string]int64 // serverId@system@version => count 46 browserMap map[string]int64 // serverId@browser@version => count 47 48 dailyFirewallRuleGroupMap map[string]int64 // serverId@firewallRuleGroupId@action => count 49 50 serverCityCountMap map[string]int16 // serverIdString => count cities 51 serverSystemCountMap map[string]int16 // serverIdString => count systems 52 serverBrowserCountMap map[string]int16 // serverIdString => count browsers 53 54 totalAttackRequests int64 55 56 locker sync.Mutex 57 58 monitorTicker *time.Ticker 59 uploadTicker *time.Ticker 60 } 61 62 // NewHTTPRequestStatManager 获取新对象 63 func NewHTTPRequestStatManager() *HTTPRequestStatManager { 64 return &HTTPRequestStatManager{ 65 ipChan: make(chan string, 10_000), // TODO 将来可以配置容量 66 userAgentChan: make(chan string, 10_000), // TODO 将来可以配置容量 67 firewallRuleGroupChan: make(chan string, 10_000), // TODO 将来可以配置容量 68 cityMap: map[string]*StatItem{}, 69 providerMap: map[string]int64{}, 70 systemMap: map[string]int64{}, 71 browserMap: map[string]int64{}, 72 dailyFirewallRuleGroupMap: map[string]int64{}, 73 74 serverCityCountMap: map[string]int16{}, 75 serverSystemCountMap: map[string]int16{}, 76 serverBrowserCountMap: map[string]int16{}, 77 } 78 } 79 80 // Start 启动 81 func (this *HTTPRequestStatManager) Start() { 82 // 上传请求总数 83 this.monitorTicker = time.NewTicker(1 * time.Minute) 84 events.OnKey(events.EventQuit, this, func() { 85 this.monitorTicker.Stop() 86 }) 87 goman.New(func() { 88 for range this.monitorTicker.C { 89 if this.totalAttackRequests > 0 { 90 monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemAttackRequests, maps.Map{"total": this.totalAttackRequests}) 91 this.totalAttackRequests = 0 92 } 93 } 94 }) 95 96 this.uploadTicker = time.NewTicker(30 * time.Minute) 97 if Tea.IsTesting() { 98 this.uploadTicker = time.NewTicker(10 * time.Second) // 在测试环境下缩短Ticker时间,以方便我们调试 99 } 100 remotelogs.Println("HTTP_REQUEST_STAT_MANAGER", "start ...") 101 events.OnKey(events.EventQuit, this, func() { 102 remotelogs.Println("HTTP_REQUEST_STAT_MANAGER", "quit") 103 this.uploadTicker.Stop() 104 }) 105 106 // 上传Ticker 107 goman.New(func() { 108 for range this.uploadTicker.C { 109 var tr = trackers.Begin("UPLOAD_REQUEST_STATS") 110 err := this.Upload() 111 tr.End() 112 if err != nil { 113 if !rpc.IsConnError(err) { 114 remotelogs.Error("HTTP_REQUEST_STAT_MANAGER", "upload failed: "+err.Error()) 115 } else { 116 remotelogs.Warn("HTTP_REQUEST_STAT_MANAGER", "upload failed: "+err.Error()) 117 } 118 } 119 120 } 121 }) 122 123 // 分析Ticker 124 for { 125 err := this.Loop() 126 if err != nil { 127 if rpc.IsConnError(err) { 128 remotelogs.Warn("HTTP_REQUEST_STAT_MANAGER", err.Error()) 129 } else { 130 remotelogs.Error("HTTP_REQUEST_STAT_MANAGER", err.Error()) 131 } 132 } 133 } 134 } 135 136 // AddRemoteAddr 添加客户端地址 137 func (this *HTTPRequestStatManager) AddRemoteAddr(serverId int64, remoteAddr string, bytes int64, isAttack bool) { 138 if len(remoteAddr) == 0 { 139 return 140 } 141 if remoteAddr[0] == '[' { // 排除IPv6 142 return 143 } 144 var index = strings.Index(remoteAddr, ":") 145 var ip string 146 if index < 0 { 147 ip = remoteAddr 148 } else { 149 ip = remoteAddr[:index] 150 } 151 if len(ip) > 0 { 152 var s string 153 if isAttack { 154 s = strconv.FormatInt(serverId, 10) + "@" + ip + "@" + types.String(bytes) + "@1" 155 } else { 156 s = strconv.FormatInt(serverId, 10) + "@" + ip + "@" + types.String(bytes) + "@0" 157 } 158 select { 159 case this.ipChan <- s: 160 default: 161 // 超出容量我们就丢弃 162 } 163 } 164 } 165 166 // AddUserAgent 添加UserAgent 167 func (this *HTTPRequestStatManager) AddUserAgent(serverId int64, userAgent string, ip string) { 168 if len(userAgent) == 0 || strings.ContainsRune(userAgent, '@') /** 非常重要,防止后面组合字符串时出现异常 **/ { 169 return 170 } 171 172 // 是否包含一些知名Agent 173 if len(userAgent) > 0 && len(ip) > 0 && agents.IsAgentFromUserAgent(userAgent) { 174 agents.SharedQueue.Push(ip) 175 } 176 177 select { 178 case this.userAgentChan <- strconv.FormatInt(serverId, 10) + "@" + userAgent: 179 default: 180 // 超出容量我们就丢弃 181 } 182 } 183 184 // AddFirewallRuleGroupId 添加防火墙拦截动作 185 func (this *HTTPRequestStatManager) AddFirewallRuleGroupId(serverId int64, firewallRuleGroupId int64, actions []*waf.ActionConfig) { 186 if firewallRuleGroupId <= 0 { 187 return 188 } 189 190 this.totalAttackRequests++ 191 192 for _, action := range actions { 193 select { 194 case this.firewallRuleGroupChan <- strconv.FormatInt(serverId, 10) + "@" + strconv.FormatInt(firewallRuleGroupId, 10) + "@" + action.Code: 195 default: 196 // 超出容量我们就丢弃 197 } 198 } 199 } 200 201 // Loop 单个循环 202 func (this *HTTPRequestStatManager) Loop() error { 203 select { 204 case ipString := <-this.ipChan: 205 // serverId@ip@bytes@isAttack 206 var pieces = strings.Split(ipString, "@") 207 if len(pieces) < 4 { 208 return nil 209 } 210 var serverIdString = pieces[0] 211 var ip = pieces[1] 212 213 var result = iplib.LookupIP(ip) 214 if result != nil && result.IsOk() { 215 this.locker.Lock() 216 if result.CountryId() > 0 { 217 var key = serverIdString + "@" + types.String(result.CountryId()) + "@" + types.String(result.ProvinceId()) + "@" + types.String(result.CityId()) 218 stat, ok := this.cityMap[key] 219 if !ok { 220 // 检查数量 221 if this.serverCityCountMap[serverIdString] > 128 { // 限制单个服务的城市数量,防止数量过多 222 this.locker.Unlock() 223 return nil 224 } 225 this.serverCityCountMap[serverIdString]++ // 需要放在限制之后,因为使用的是int16 226 227 stat = &StatItem{} 228 this.cityMap[key] = stat 229 } 230 stat.Bytes += types.Int64(pieces[2]) 231 stat.CountRequests++ 232 if types.Int8(pieces[3]) == 1 { 233 stat.AttackBytes += types.Int64(pieces[2]) 234 stat.CountAttackRequests++ 235 } 236 } 237 238 if result.ProviderId() > 0 { 239 this.providerMap[serverIdString+"@"+types.String(result.ProviderId())]++ 240 } else if utils.IsLocalIP(ip) { // 局域网IP 241 this.providerMap[serverIdString+"@258"]++ 242 } 243 this.locker.Unlock() 244 } 245 case userAgentString := <-this.userAgentChan: 246 var atIndex = strings.Index(userAgentString, "@") 247 if atIndex < 0 { 248 return nil 249 } 250 var serverIdString = userAgentString[:atIndex] 251 var userAgent = userAgentString[atIndex+1:] 252 253 var result = SharedUserAgentParser.Parse(userAgent) 254 var osInfo = result.OS 255 if len(osInfo.Name) > 0 { 256 dotIndex := strings.Index(osInfo.Version, ".") 257 if dotIndex > -1 { 258 osInfo.Version = osInfo.Version[:dotIndex] 259 } 260 this.locker.Lock() 261 262 var systemKey = serverIdString + "@" + osInfo.Name + "@" + osInfo.Version 263 _, ok := this.systemMap[systemKey] 264 if !ok { 265 if this.serverSystemCountMap[serverIdString] < 128 { // 限制最大数据,防止攻击 266 this.serverSystemCountMap[serverIdString]++ 267 ok = true 268 } 269 } 270 if ok { 271 this.systemMap[systemKey]++ 272 } 273 this.locker.Unlock() 274 } 275 276 var browser, browserVersion = result.BrowserName, result.BrowserVersion 277 if len(browser) > 0 { 278 dotIndex := strings.Index(browserVersion, ".") 279 if dotIndex > -1 { 280 browserVersion = browserVersion[:dotIndex] 281 } 282 this.locker.Lock() 283 284 var browserKey = serverIdString + "@" + browser + "@" + browserVersion 285 _, ok := this.browserMap[browserKey] 286 if !ok { 287 if this.serverBrowserCountMap[serverIdString] < 256 { // 限制最大数据,防止攻击 288 this.serverBrowserCountMap[serverIdString]++ 289 ok = true 290 } 291 } 292 if ok { 293 this.browserMap[browserKey]++ 294 } 295 this.locker.Unlock() 296 } 297 case firewallRuleGroupString := <-this.firewallRuleGroupChan: 298 this.locker.Lock() 299 this.dailyFirewallRuleGroupMap[firewallRuleGroupString]++ 300 this.locker.Unlock() 301 } 302 303 return nil 304 } 305 306 // Upload 上传数据 307 func (this *HTTPRequestStatManager) Upload() error { 308 // 上传统计数据 309 rpcClient, err := rpc.SharedRPC() 310 if err != nil { 311 return err 312 } 313 314 // 拷贝数据 315 this.locker.Lock() 316 var cityMap = this.cityMap 317 var providerMap = this.providerMap 318 var systemMap = this.systemMap 319 var browserMap = this.browserMap 320 var dailyFirewallRuleGroupMap = this.dailyFirewallRuleGroupMap 321 322 this.cityMap = map[string]*StatItem{} 323 this.providerMap = map[string]int64{} 324 this.systemMap = map[string]int64{} 325 this.browserMap = map[string]int64{} 326 this.dailyFirewallRuleGroupMap = map[string]int64{} 327 328 this.serverCityCountMap = map[string]int16{} 329 this.serverSystemCountMap = map[string]int16{} 330 this.serverBrowserCountMap = map[string]int16{} 331 332 this.locker.Unlock() 333 334 // 上传限制 335 var maxCities int16 = 32 336 var maxProviders int16 = 32 337 var maxSystems int16 = 64 338 var maxBrowsers int16 = 64 339 nodeConfig, _ := nodeconfigs.SharedNodeConfig() 340 if nodeConfig != nil { 341 var serverConfig = nodeConfig.GlobalServerConfig // 复制是为了防止在中途修改 342 if serverConfig != nil { 343 var uploadConfig = serverConfig.Stat.Upload 344 if uploadConfig.MaxCities > 0 { 345 maxCities = uploadConfig.MaxCities 346 } 347 if uploadConfig.MaxProviders > 0 { 348 maxProviders = uploadConfig.MaxProviders 349 } 350 if uploadConfig.MaxSystems > 0 { 351 maxSystems = uploadConfig.MaxSystems 352 } 353 if uploadConfig.MaxBrowsers > 0 { 354 maxBrowsers = uploadConfig.MaxBrowsers 355 } 356 } 357 } 358 359 var pbCities = []*pb.UploadServerHTTPRequestStatRequest_RegionCity{} 360 var pbProviders = []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{} 361 var pbSystems = []*pb.UploadServerHTTPRequestStatRequest_System{} 362 var pbBrowsers = []*pb.UploadServerHTTPRequestStatRequest_Browser{} 363 364 // 城市 365 for k, stat := range cityMap { 366 var pieces = strings.SplitN(k, "@", 4) 367 var serverId = types.Int64(pieces[0]) 368 pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{ 369 ServerId: serverId, 370 CountryId: types.Int64(pieces[1]), 371 ProvinceId: types.Int64(pieces[2]), 372 CityId: types.Int64(pieces[3]), 373 CountRequests: stat.CountRequests, 374 CountAttackRequests: stat.CountAttackRequests, 375 Bytes: stat.Bytes, 376 AttackBytes: stat.AttackBytes, 377 }) 378 } 379 if len(cityMap) > int(maxCities) { 380 var newPBCities = []*pb.UploadServerHTTPRequestStatRequest_RegionCity{} 381 sort.Slice(pbCities, func(i, j int) bool { 382 return pbCities[i].CountRequests > pbCities[j].CountRequests 383 }) 384 var serverCountMap = map[int64]int16{} // serverId => count 385 for _, city := range pbCities { 386 serverCountMap[city.ServerId]++ 387 if serverCountMap[city.ServerId] > maxCities { 388 continue 389 } 390 newPBCities = append(newPBCities, city) 391 } 392 if len(pbCities) != len(newPBCities) { 393 pbCities = newPBCities 394 } 395 } 396 397 // 运营商 398 for k, count := range providerMap { 399 var pieces = strings.SplitN(k, "@", 2) 400 var serverId = types.Int64(pieces[0]) 401 pbProviders = append(pbProviders, &pb.UploadServerHTTPRequestStatRequest_RegionProvider{ 402 ServerId: serverId, 403 ProviderId: types.Int64(pieces[1]), 404 Count: count, 405 }) 406 } 407 if len(providerMap) > int(maxProviders) { 408 var newPBProviders = []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{} 409 sort.Slice(pbProviders, func(i, j int) bool { 410 return pbProviders[i].Count > pbProviders[j].Count 411 }) 412 var serverCountMap = map[int64]int16{} 413 for _, provider := range pbProviders { 414 serverCountMap[provider.ServerId]++ 415 if serverCountMap[provider.ServerId] > maxProviders { 416 continue 417 } 418 newPBProviders = append(newPBProviders, provider) 419 } 420 if len(pbProviders) != len(newPBProviders) { 421 pbProviders = newPBProviders 422 } 423 } 424 425 // 操作系统 426 for k, count := range systemMap { 427 var pieces = strings.SplitN(k, "@", 3) 428 var serverId = types.Int64(pieces[0]) 429 pbSystems = append(pbSystems, &pb.UploadServerHTTPRequestStatRequest_System{ 430 ServerId: serverId, 431 Name: pieces[1], 432 Version: pieces[2], 433 Count: count, 434 }) 435 } 436 if len(systemMap) > int(maxSystems) { 437 var newPBSystems = []*pb.UploadServerHTTPRequestStatRequest_System{} 438 sort.Slice(pbSystems, func(i, j int) bool { 439 return pbSystems[i].Count > pbSystems[j].Count 440 }) 441 var serverCountMap = map[int64]int16{} 442 for _, system := range pbSystems { 443 serverCountMap[system.ServerId]++ 444 if serverCountMap[system.ServerId] > maxSystems { 445 continue 446 } 447 newPBSystems = append(newPBSystems, system) 448 } 449 if len(pbSystems) != len(newPBSystems) { 450 pbSystems = newPBSystems 451 } 452 } 453 454 // 浏览器 455 for k, count := range browserMap { 456 var pieces = strings.SplitN(k, "@", 3) 457 var serverId = types.Int64(pieces[0]) 458 pbBrowsers = append(pbBrowsers, &pb.UploadServerHTTPRequestStatRequest_Browser{ 459 ServerId: serverId, 460 Name: pieces[1], 461 Version: pieces[2], 462 Count: count, 463 }) 464 } 465 if len(browserMap) > int(maxBrowsers) { 466 var newPBBrowsers = []*pb.UploadServerHTTPRequestStatRequest_Browser{} 467 sort.Slice(pbBrowsers, func(i, j int) bool { 468 return pbBrowsers[i].Count > pbBrowsers[j].Count 469 }) 470 var serverCountMap = map[int64]int16{} 471 for _, browser := range pbBrowsers { 472 serverCountMap[browser.ServerId]++ 473 if serverCountMap[browser.ServerId] > maxBrowsers { 474 continue 475 } 476 newPBBrowsers = append(newPBBrowsers, browser) 477 } 478 if len(pbBrowsers) != len(newPBBrowsers) { 479 pbBrowsers = newPBBrowsers 480 } 481 } 482 483 // 防火墙相关 484 var pbFirewallRuleGroups = []*pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{} 485 for k, count := range dailyFirewallRuleGroupMap { 486 var pieces = strings.SplitN(k, "@", 3) 487 pbFirewallRuleGroups = append(pbFirewallRuleGroups, &pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{ 488 ServerId: types.Int64(pieces[0]), 489 HttpFirewallRuleGroupId: types.Int64(pieces[1]), 490 Action: pieces[2], 491 Count: count, 492 }) 493 } 494 495 // 检查是否有数据 496 if len(pbCities) == 0 && 497 len(pbProviders) == 0 && 498 len(pbSystems) == 0 && 499 len(pbBrowsers) == 0 && 500 len(pbFirewallRuleGroups) == 0 { 501 return nil 502 } 503 504 // 上传数据 505 _, err = rpcClient.ServerRPC.UploadServerHTTPRequestStat(rpcClient.Context(), &pb.UploadServerHTTPRequestStatRequest{ 506 Month: timeutil.Format("Ym"), 507 Day: timeutil.Format("Ymd"), 508 RegionCities: pbCities, 509 RegionProviders: pbProviders, 510 Systems: pbSystems, 511 Browsers: pbBrowsers, 512 HttpFirewallRuleGroups: pbFirewallRuleGroups, 513 }) 514 if err != nil { 515 // 是否包含了invalid UTF-8 516 if strings.Contains(err.Error(), "string field contains invalid UTF-8") { 517 for _, system := range pbSystems { 518 system.Name = utils.ToValidUTF8string(system.Name) 519 system.Version = utils.ToValidUTF8string(system.Version) 520 } 521 for _, browser := range pbBrowsers { 522 browser.Name = utils.ToValidUTF8string(browser.Name) 523 browser.Version = utils.ToValidUTF8string(browser.Version) 524 } 525 526 // 再次尝试 527 _, err = rpcClient.ServerRPC.UploadServerHTTPRequestStat(rpcClient.Context(), &pb.UploadServerHTTPRequestStatRequest{ 528 Month: timeutil.Format("Ym"), 529 Day: timeutil.Format("Ymd"), 530 RegionCities: pbCities, 531 RegionProviders: pbProviders, 532 Systems: pbSystems, 533 Browsers: pbBrowsers, 534 HttpFirewallRuleGroups: pbFirewallRuleGroups, 535 }) 536 if err != nil { 537 return err 538 } 539 } 540 } 541 542 return nil 543 }