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  }