Initial commit (code only without large binaries)
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NodeClusterTrafficDailyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedNodeClusterTrafficDailyStatDAO.CleanDefaultDays(nil, 30) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewNodeClusterTrafficDailyStatDAO() *NodeClusterTrafficDailyStatDAO {
|
||||
return dbs.NewDAO(&NodeClusterTrafficDailyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeNodeClusterTrafficDailyStats",
|
||||
Model: new(NodeClusterTrafficDailyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*NodeClusterTrafficDailyStatDAO)
|
||||
}
|
||||
|
||||
var SharedNodeClusterTrafficDailyStatDAO *NodeClusterTrafficDailyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedNodeClusterTrafficDailyStatDAO = NewNodeClusterTrafficDailyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseDailyStat 增加统计数据
|
||||
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
|
||||
if len(day) != 8 {
|
||||
return errors.New("invalid day '" + day + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("bytes", bytes).
|
||||
Param("cachedBytes", cachedBytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("countCachedRequests", countCachedRequests).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"clusterId": clusterId,
|
||||
"day": day,
|
||||
"bytes": bytes,
|
||||
"cachedBytes": cachedBytes,
|
||||
"countRequests": countRequests,
|
||||
"countCachedRequests": countCachedRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
"attackBytes": attackBytes,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindDailyStats 获取日期之间统计
|
||||
func (this *NodeClusterTrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, clusterId int64, dayFrom string, dayTo string) (result []*NodeClusterTrafficDailyStat, err error) {
|
||||
ones, err := this.Query(tx).
|
||||
Attr("clusterId", clusterId).
|
||||
Between("day", dayFrom, dayTo).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dayMap := map[string]*NodeClusterTrafficDailyStat{} // day => Stat
|
||||
for _, one := range ones {
|
||||
stat := one.(*NodeClusterTrafficDailyStat)
|
||||
dayMap[stat.Day] = stat
|
||||
}
|
||||
days, err := utils.RangeDays(dayFrom, dayTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, day := range days {
|
||||
stat, ok := dayMap[day]
|
||||
if ok {
|
||||
result = append(result, stat)
|
||||
} else {
|
||||
result = append(result, &NodeClusterTrafficDailyStat{Day: day})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SumDailyStat 计算当月总流量
|
||||
func (this *NodeClusterTrafficDailyStatDAO) SumDailyStat(tx *dbs.Tx, clusterId int64, dayFrom string, dayTo string) (*NodeClusterTrafficDailyStat, error) {
|
||||
one, err := this.Query(tx).
|
||||
Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes").
|
||||
Attr("clusterId", clusterId).
|
||||
Between("day", dayFrom, dayTo).
|
||||
Find()
|
||||
if err != nil || one == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return one.(*NodeClusterTrafficDailyStat), nil
|
||||
}
|
||||
|
||||
// CleanDays 清理历史数据
|
||||
func (this *NodeClusterTrafficDailyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
|
||||
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("day", day).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *NodeClusterTrafficDailyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
|
||||
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseConfig != nil && databaseConfig.NodeClusterTrafficDailyStat.Clean.Days > 0 {
|
||||
defaultDays = databaseConfig.NodeClusterTrafficDailyStat.Clean.Days
|
||||
}
|
||||
if defaultDays <= 0 {
|
||||
defaultDays = 30
|
||||
}
|
||||
|
||||
return this.CleanDays(tx, defaultDays)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
package stats
|
||||
|
||||
// NodeClusterTrafficDailyStat 总的流量统计(按天)
|
||||
type NodeClusterTrafficDailyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ClusterId uint32 `field:"clusterId"` // 集群ID
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
Bytes uint64 `field:"bytes"` // 流量字节
|
||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
}
|
||||
|
||||
type NodeClusterTrafficDailyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ClusterId interface{} // 集群ID
|
||||
Day interface{} // YYYYMMDD
|
||||
Bytes interface{} // 流量字节
|
||||
CachedBytes interface{} // 缓存流量
|
||||
CountRequests interface{} // 请求数
|
||||
CountCachedRequests interface{} // 缓存的请求数
|
||||
CountAttackRequests interface{} // 攻击请求数
|
||||
AttackBytes interface{} // 攻击流量
|
||||
}
|
||||
|
||||
func NewNodeClusterTrafficDailyStatOperator() *NodeClusterTrafficDailyStatOperator {
|
||||
return &NodeClusterTrafficDailyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
224
EdgeAPI/internal/db/models/stats/node_traffic_hourly_stat_dao.go
Normal file
224
EdgeAPI/internal/db/models/stats/node_traffic_hourly_stat_dao.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NodeTrafficHourlyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedNodeTrafficHourlyStatDAO.CleanDefaultDays(nil, 15) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("NodeTrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewNodeTrafficHourlyStatDAO() *NodeTrafficHourlyStatDAO {
|
||||
return dbs.NewDAO(&NodeTrafficHourlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeNodeTrafficHourlyStats",
|
||||
Model: new(NodeTrafficHourlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*NodeTrafficHourlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedNodeTrafficHourlyStatDAO *NodeTrafficHourlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedNodeTrafficHourlyStatDAO = NewNodeTrafficHourlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseHourlyStat 增加统计数据
|
||||
func (this *NodeTrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, role string, nodeId int64, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
|
||||
if len(hour) != 10 {
|
||||
return errors.New("invalid hour '" + hour + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("bytes", bytes).
|
||||
Param("cachedBytes", cachedBytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("countCachedRequests", countCachedRequests).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"clusterId": clusterId,
|
||||
"role": role,
|
||||
"nodeId": nodeId,
|
||||
"hour": hour,
|
||||
"bytes": bytes,
|
||||
"cachedBytes": cachedBytes,
|
||||
"countRequests": countRequests,
|
||||
"countCachedRequests": countCachedRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
"attackBytes": attackBytes,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindHourlyStatsWithClusterId 获取小时之间统计
|
||||
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
|
||||
ones, err := this.Query(tx).
|
||||
Attr("clusterId", clusterId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("hour").
|
||||
FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
|
||||
for _, one := range ones {
|
||||
stat := one.(*NodeTrafficHourlyStat)
|
||||
hourMap[stat.Hour] = stat
|
||||
}
|
||||
hours, err := utils.RangeHours(hourFrom, hourTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, hour := range hours {
|
||||
stat, ok := hourMap[hour]
|
||||
if ok {
|
||||
result = append(result, stat)
|
||||
} else {
|
||||
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FindTopNodeStats 取得一定时间内的节点排行数据
|
||||
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStats(tx *dbs.Tx, role string, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err = this.Query(tx).
|
||||
Attr("role", role).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("nodeId").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopNodeStatsWithAttack 取得防火墙相关的节点排行数据
|
||||
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithAttack(tx *dbs.Tx, role string, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err = this.Query(tx).
|
||||
Attr("role", role).
|
||||
Gt("countAttackRequests", 0).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("nodeId").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopNodeStatsWithClusterId 取得集群一定时间内的节点排行数据
|
||||
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithClusterId(tx *dbs.Tx, role string, clusterId int64, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err = this.Query(tx).
|
||||
Attr("role", role).
|
||||
Attr("clusterId", clusterId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("nodeId").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindHourlyStatsWithNodeId 获取节点小时之间统计
|
||||
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithNodeId(tx *dbs.Tx, role string, nodeId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
|
||||
ones, err := this.Query(tx).
|
||||
Attr("role", role).
|
||||
Attr("nodeId", nodeId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("hour").
|
||||
FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
|
||||
for _, one := range ones {
|
||||
stat := one.(*NodeTrafficHourlyStat)
|
||||
hourMap[stat.Hour] = stat
|
||||
}
|
||||
hours, err := utils.RangeHours(hourFrom, hourTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, hour := range hours {
|
||||
stat, ok := hourMap[hour]
|
||||
if ok {
|
||||
result = append(result, stat)
|
||||
} else {
|
||||
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CleanDays 清理历史数据
|
||||
func (this *NodeTrafficHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
|
||||
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("hour", hour).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *NodeTrafficHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
|
||||
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseConfig != nil && databaseConfig.NodeTrafficHourlyStat.Clean.Days > 0 {
|
||||
defaultDays = databaseConfig.NodeTrafficHourlyStat.Clean.Days
|
||||
}
|
||||
if defaultDays <= 0 {
|
||||
defaultDays = 15
|
||||
}
|
||||
|
||||
return this.CleanDays(tx, defaultDays)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
package stats
|
||||
|
||||
// NodeTrafficHourlyStat 总的流量统计(按天)
|
||||
type NodeTrafficHourlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
Role string `field:"role"` // 节点角色
|
||||
ClusterId uint32 `field:"clusterId"` // 集群ID
|
||||
NodeId uint32 `field:"nodeId"` // 集群ID
|
||||
Hour string `field:"hour"` // YYYYMMDDHH
|
||||
Bytes uint64 `field:"bytes"` // 流量字节
|
||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
}
|
||||
|
||||
type NodeTrafficHourlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
Role interface{} // 节点角色
|
||||
ClusterId interface{} // 集群ID
|
||||
NodeId interface{} // 集群ID
|
||||
Hour interface{} // YYYYMMDDHH
|
||||
Bytes interface{} // 流量字节
|
||||
CachedBytes interface{} // 缓存流量
|
||||
CountRequests interface{} // 请求数
|
||||
CountCachedRequests interface{} // 缓存的请求数
|
||||
CountAttackRequests interface{} // 攻击请求数
|
||||
AttackBytes interface{} // 攻击流量
|
||||
}
|
||||
|
||||
func NewNodeTrafficHourlyStatOperator() *NodeTrafficHourlyStatOperator {
|
||||
return &NodeTrafficHourlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,95 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerClientBrowserMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerClientBrowserMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerClientBrowserMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerClientBrowserMonthlyStatDAO() *ServerClientBrowserMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerClientBrowserMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerClientBrowserMonthlyStats",
|
||||
Model: new(ServerClientBrowserMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerClientBrowserMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerClientBrowserMonthlyStatDAO *ServerClientBrowserMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerClientBrowserMonthlyStatDAO = NewServerClientBrowserMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerClientBrowserMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, browserId int64, version string, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"browserId": browserId,
|
||||
"version": version,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerClientBrowserMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientBrowserMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerClientBrowserMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerClientBrowserMonthlyStatDAO_IncreaseMonthlyCount(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var tx *dbs.Tx
|
||||
err := SharedServerClientBrowserMonthlyStatDAO.IncreaseMonthlyCount(tx, 1, 1, "1.0", "202101", 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
|
||||
func TestServerClientBrowserMonthlyStatDAO_Clean(t *testing.T) {
|
||||
var dao = NewServerClientBrowserMonthlyStatDAO()
|
||||
err := dao.Clean(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package stats
|
||||
|
||||
// 浏览器统计(按月)
|
||||
type ServerClientBrowserMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
BrowserId uint32 `field:"browserId"` // 浏览器ID
|
||||
Month string `field:"month"` // YYYYMM
|
||||
Version string `field:"version"` // 主版本号
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerClientBrowserMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
BrowserId interface{} // 浏览器ID
|
||||
Month interface{} // YYYYMM
|
||||
Version interface{} // 主版本号
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerClientBrowserMonthlyStatOperator() *ServerClientBrowserMonthlyStatOperator {
|
||||
return &ServerClientBrowserMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,95 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerClientSystemMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerClientSystemMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerClientSystemMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerClientSystemMonthlyStatDAO() *ServerClientSystemMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerClientSystemMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerClientSystemMonthlyStats",
|
||||
Model: new(ServerClientSystemMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerClientSystemMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerClientSystemMonthlyStatDAO *ServerClientSystemMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerClientSystemMonthlyStatDAO = NewServerClientSystemMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerClientSystemMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, systemId int64, version string, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"systemId": systemId,
|
||||
"version": version,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerClientSystemMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientSystemMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerClientSystemMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerClientSystemMonthlyStatDAO_Clean(t *testing.T) {
|
||||
var dao = NewServerClientSystemMonthlyStatDAO()
|
||||
err := dao.Clean(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package stats
|
||||
|
||||
// 操作系统统计(按月)
|
||||
type ServerClientSystemMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
SystemId uint32 `field:"systemId"` // 系统ID
|
||||
Version string `field:"version"` // 主版本号
|
||||
Month string `field:"month"` // YYYYMM
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerClientSystemMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
SystemId interface{} // 系统ID
|
||||
Version interface{} // 主版本号
|
||||
Month interface{} // YYYYMM
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerClientSystemMonthlyStatOperator() *ServerClientSystemMonthlyStatOperator {
|
||||
return &ServerClientSystemMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,399 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ServerDomainHourlyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerDomainHourlyStatDAO.CleanDefaultDays(nil, 7) // 只保留 N 天
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerDomainHourlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewServerDomainHourlyStatDAO() *ServerDomainHourlyStatDAO {
|
||||
return dbs.NewDAO(&ServerDomainHourlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerDomainHourlyStats",
|
||||
Model: new(ServerDomainHourlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerDomainHourlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerDomainHourlyStatDAO *ServerDomainHourlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerDomainHourlyStatDAO = NewServerDomainHourlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// PartitionTable 获取分区表格名称
|
||||
func (this *ServerDomainHourlyStatDAO) PartitionTable(domain string) string {
|
||||
if len(domain) == 0 {
|
||||
return this.Table + "_0"
|
||||
}
|
||||
if (domain[0] >= '0' && domain[0] <= '9') || (domain[0] >= 'a' && domain[0] <= 'z') || (domain[0] >= 'A' && domain[0] <= 'Z') {
|
||||
return this.Table + "_" + strings.ToLower(string(domain[0]))
|
||||
}
|
||||
|
||||
return this.Table + "_0"
|
||||
}
|
||||
|
||||
// FindAllPartitionTables 获取所有表格名称
|
||||
func (this *ServerDomainHourlyStatDAO) FindAllPartitionTables() []string {
|
||||
var tables = []string{}
|
||||
for i := '0'; i <= '9'; i++ {
|
||||
tables = append(tables, this.Table+"_"+string(i))
|
||||
}
|
||||
for i := 'a'; i <= 'z'; i++ {
|
||||
tables = append(tables, this.Table+"_"+string(i))
|
||||
}
|
||||
return tables
|
||||
}
|
||||
|
||||
// IncreaseHourlyStat 增加统计数据
|
||||
func (this *ServerDomainHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, nodeId int64, serverId int64, domain string, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
|
||||
if len(hour) != 10 {
|
||||
return errors.New("invalid hour '" + hour + "'")
|
||||
}
|
||||
if len(domain) == 0 || len(domain) > 64 {
|
||||
return nil
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Table(this.PartitionTable(domain)).
|
||||
Param("bytes", bytes).
|
||||
Param("cachedBytes", cachedBytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("countCachedRequests", countCachedRequests).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"clusterId": clusterId,
|
||||
"nodeId": nodeId,
|
||||
"serverId": serverId,
|
||||
"hour": hour,
|
||||
"domain": domain,
|
||||
"bytes": bytes,
|
||||
"cachedBytes": cachedBytes,
|
||||
"countRequests": countRequests,
|
||||
"countCachedRequests": countCachedRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
"attackBytes": attackBytes,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindTopDomainStats 取得一定时间内的域名排行数据
|
||||
func (this *ServerDomainHourlyStatDAO) FindTopDomainStats(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
|
||||
var tables = this.FindAllPartitionTables()
|
||||
var wg = sync.WaitGroup{}
|
||||
wg.Add(len(tables))
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
for _, table := range tables {
|
||||
go func(table string) {
|
||||
defer wg.Done()
|
||||
|
||||
var topResults = []*ServerDomainHourlyStat{}
|
||||
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("domain").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&topResults).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(topResults) > 0 {
|
||||
locker.Lock()
|
||||
result = append(result, topResults...)
|
||||
locker.Unlock()
|
||||
}
|
||||
}(table)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].CountRequests > result[j].CountRequests
|
||||
})
|
||||
|
||||
if len(result) > types.Int(size) {
|
||||
result = result[:types.Int(size)]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopDomainStatsWithAttack 取得一定时间内的域名排行数据
|
||||
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithAttack(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
|
||||
var tables = this.FindAllPartitionTables()
|
||||
var wg = sync.WaitGroup{}
|
||||
wg.Add(len(tables))
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
for _, table := range tables {
|
||||
go func(table string) {
|
||||
defer wg.Done()
|
||||
|
||||
var topResults = []*ServerDomainHourlyStat{}
|
||||
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Gt("countAttackRequests", 0).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("domain").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&topResults).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(topResults) > 0 {
|
||||
locker.Lock()
|
||||
result = append(result, topResults...)
|
||||
locker.Unlock()
|
||||
}
|
||||
}(table)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].CountRequests > result[j].CountRequests
|
||||
})
|
||||
|
||||
if len(result) > types.Int(size) {
|
||||
result = result[:types.Int(size)]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopDomainStatsWithClusterId 取得集群上的一定时间内的域名排行数据
|
||||
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
|
||||
var tables = this.FindAllPartitionTables()
|
||||
var wg = sync.WaitGroup{}
|
||||
wg.Add(len(tables))
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
for _, table := range tables {
|
||||
go func(table string) {
|
||||
defer wg.Done()
|
||||
|
||||
var topResults = []*ServerDomainHourlyStat{}
|
||||
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Attr("clusterId", clusterId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
UseIndex("hour").
|
||||
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("domain").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&topResults).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(topResults) > 0 {
|
||||
locker.Lock()
|
||||
result = append(result, topResults...)
|
||||
locker.Unlock()
|
||||
}
|
||||
}(table)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].CountRequests > result[j].CountRequests
|
||||
})
|
||||
|
||||
if len(result) > types.Int(size) {
|
||||
result = result[:types.Int(size)]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopDomainStatsWithNodeId 取得节点上的一定时间内的域名排行数据
|
||||
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithNodeId(tx *dbs.Tx, nodeId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
|
||||
var tables = this.FindAllPartitionTables()
|
||||
var wg = sync.WaitGroup{}
|
||||
wg.Add(len(tables))
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
for _, table := range tables {
|
||||
go func(table string) {
|
||||
defer wg.Done()
|
||||
|
||||
var topResults = []*ServerDomainHourlyStat{}
|
||||
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Attr("nodeId", nodeId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
UseIndex("hour").
|
||||
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("domain").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&topResults).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(topResults) > 0 {
|
||||
locker.Lock()
|
||||
result = append(result, topResults...)
|
||||
locker.Unlock()
|
||||
}
|
||||
}(table)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].CountRequests > result[j].CountRequests
|
||||
})
|
||||
|
||||
if len(result) > types.Int(size) {
|
||||
result = result[:types.Int(size)]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindTopDomainStatsWithServerId 取得某个服务的一定时间内的域名排行数据
|
||||
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithServerId(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
|
||||
var tables = this.FindAllPartitionTables()
|
||||
var wg = sync.WaitGroup{}
|
||||
wg.Add(len(tables))
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
for _, table := range tables {
|
||||
go func(table string) {
|
||||
defer wg.Done()
|
||||
|
||||
var topResults = []*ServerDomainHourlyStat{}
|
||||
|
||||
// TODO 节点如果已经被删除,则忽略
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Attr("serverId", serverId).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
UseIndex("serverId", "hour").
|
||||
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
|
||||
Group("domain").
|
||||
Desc("countRequests").
|
||||
Limit(size).
|
||||
Slice(&topResults).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if len(topResults) > 0 {
|
||||
locker.Lock()
|
||||
result = append(result, topResults...)
|
||||
locker.Unlock()
|
||||
}
|
||||
}(table)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].CountRequests > result[j].CountRequests
|
||||
})
|
||||
|
||||
if len(result) > types.Int(size) {
|
||||
result = result[:types.Int(size)]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CleanDays 清理历史数据
|
||||
func (this *ServerDomainHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
|
||||
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
|
||||
for _, table := range this.FindAllPartitionTables() {
|
||||
_, err := this.Query(tx).
|
||||
Table(table).
|
||||
Lt("hour", hour).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ServerDomainHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
|
||||
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseConfig != nil && databaseConfig.ServerDomainHourlyStat.Clean.Days > 0 {
|
||||
defaultDays = databaseConfig.ServerDomainHourlyStat.Clean.Days
|
||||
}
|
||||
if defaultDays <= 0 {
|
||||
defaultDays = 7
|
||||
}
|
||||
|
||||
return this.CleanDays(tx, defaultDays)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestServerDomainHourlyStatDAO_PartitionTable(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
var dao = NewServerDomainHourlyStatDAO()
|
||||
a.IsTrue(dao.PartitionTable("") == "edgeServerDomainHourlyStats_0")
|
||||
a.IsTrue(dao.PartitionTable("a1") == "edgeServerDomainHourlyStats_a")
|
||||
a.IsTrue(dao.PartitionTable("Y1") == "edgeServerDomainHourlyStats_y")
|
||||
a.IsTrue(dao.PartitionTable("z1") == "edgeServerDomainHourlyStats_z")
|
||||
a.IsTrue(dao.PartitionTable("A1") == "edgeServerDomainHourlyStats_a")
|
||||
a.IsTrue(dao.PartitionTable("Z1") == "edgeServerDomainHourlyStats_z")
|
||||
a.IsTrue(dao.PartitionTable("中国") == "edgeServerDomainHourlyStats_0")
|
||||
a.IsTrue(dao.PartitionTable("_") == "edgeServerDomainHourlyStats_0")
|
||||
a.IsTrue(dao.PartitionTable(" ") == "edgeServerDomainHourlyStats_0")
|
||||
}
|
||||
|
||||
func TestServerDomainHourlyStatDAO_FindAllPartitionTables(t *testing.T) {
|
||||
var dao = NewServerDomainHourlyStatDAO()
|
||||
t.Log(dao.FindAllPartitionTables())
|
||||
}
|
||||
|
||||
func TestServerDomainHourlyStatDAO_InsertManyHourlyStat(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var count = 1
|
||||
for i := 0; i < count; i++ {
|
||||
var f = string([]rune{int32(rands.Int('0', '9'))})
|
||||
if i%30 > 0 {
|
||||
f = string([]rune{int32(rands.Int('a', 'z'))})
|
||||
}
|
||||
|
||||
err := NewServerDomainHourlyStatDAO().IncreaseHourlyStat(nil, 18, 48, 23, f+"rand"+types.String(i%500_000)+".com", timeutil.Format("Ymd")+fmt.Sprintf("%02d", rands.Int(0, 23)), 1, 1, 1, 1, 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i%10000 == 0 {
|
||||
t.Log(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerDomainHourlyStatDAO_FindTopDomainStats(t *testing.T) {
|
||||
var dao = NewServerDomainHourlyStatDAO()
|
||||
var before = time.Now()
|
||||
defer func() {
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}()
|
||||
stats, err := dao.FindTopDomainStats(nil, timeutil.Format("Ymd00"), timeutil.Format("Ymd23"), 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, stat := range stats {
|
||||
t.Log(stat.Domain, stat.CountRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerDomainHourlyStatDAO_Clean(t *testing.T) {
|
||||
var dao = NewServerDomainHourlyStatDAO()
|
||||
err := dao.CleanDays(nil, 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package stats
|
||||
|
||||
// ServerDomainHourlyStat 服务域名统计
|
||||
type ServerDomainHourlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ClusterId uint32 `field:"clusterId"` // 集群ID
|
||||
NodeId uint32 `field:"nodeId"` // 节点ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
Domain string `field:"domain"` // 域名
|
||||
Hour string `field:"hour"` // YYYYMMDDHH
|
||||
Bytes uint64 `field:"bytes"` // 流量
|
||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
}
|
||||
|
||||
type ServerDomainHourlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ClusterId interface{} // 集群ID
|
||||
NodeId interface{} // 节点ID
|
||||
ServerId interface{} // 服务ID
|
||||
Domain interface{} // 域名
|
||||
Hour interface{} // YYYYMMDDHH
|
||||
Bytes interface{} // 流量
|
||||
CachedBytes interface{} // 缓存流量
|
||||
CountRequests interface{} // 请求数
|
||||
CountCachedRequests interface{} // 缓存请求
|
||||
CountAttackRequests interface{} // 攻击请求数
|
||||
AttackBytes interface{} // 攻击流量
|
||||
}
|
||||
|
||||
func NewServerDomainHourlyStatOperator() *ServerDomainHourlyStatOperator {
|
||||
return &ServerDomainHourlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,139 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ServerHTTPFirewallDailyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerHTTPFirewallDailyStatDAO.Clean(nil, 30) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerHTTPFirewallDailyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewServerHTTPFirewallDailyStatDAO() *ServerHTTPFirewallDailyStatDAO {
|
||||
return dbs.NewDAO(&ServerHTTPFirewallDailyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerHTTPFirewallDailyStats",
|
||||
Model: new(ServerHTTPFirewallDailyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerHTTPFirewallDailyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerHTTPFirewallDailyStatDAO *ServerHTTPFirewallDailyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerHTTPFirewallDailyStatDAO = NewServerHTTPFirewallDailyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseDailyCount 增加数量
|
||||
func (this *ServerHTTPFirewallDailyStatDAO) IncreaseDailyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, day string, count int64) error {
|
||||
if len(day) != 8 {
|
||||
return errors.New("invalid day '" + day + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"day": day,
|
||||
"httpFirewallRuleGroupId": firewallRuleGroupId,
|
||||
"action": action,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SumDailyCount 计算某天的数据
|
||||
func (this *ServerHTTPFirewallDailyStatDAO) SumDailyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
|
||||
query := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
if len(action) > 0 {
|
||||
query.Attr("action", action)
|
||||
}
|
||||
return query.SumInt64("count", 0)
|
||||
}
|
||||
|
||||
// GroupDailyCount 查询规则分组数量
|
||||
func (this *ServerHTTPFirewallDailyStatDAO) GroupDailyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallDailyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
_, err = query.Group("httpFirewallRuleGroupId").
|
||||
Result("httpFirewallRuleGroupId, SUM(count) AS count").
|
||||
Desc("count").
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindDailyStats 查询某个日期段内的记录
|
||||
func (this *ServerHTTPFirewallDailyStatDAO) FindDailyStats(tx *dbs.Tx, userId int64, serverId int64, actions []string, dayFrom string, dayTo string) (result []*ServerHTTPFirewallDailyStat, err error) {
|
||||
if len(actions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
query := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo).
|
||||
Attr("action", actions)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
_, err = query.Group("day").
|
||||
Result("day, SUM(count) AS count").
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理历史数据
|
||||
func (this *ServerHTTPFirewallDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
|
||||
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("day", day).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package stats
|
||||
|
||||
// WAF统计
|
||||
type ServerHTTPFirewallDailyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
HttpFirewallRuleGroupId uint32 `field:"httpFirewallRuleGroupId"` // WAF分组ID
|
||||
Action string `field:"action"` // 采取的动作
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerHTTPFirewallDailyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
Day interface{} // YYYYMMDD
|
||||
HttpFirewallRuleGroupId interface{} // WAF分组ID
|
||||
Action interface{} // 采取的动作
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerHTTPFirewallDailyStatOperator() *ServerHTTPFirewallDailyStatOperator {
|
||||
return &ServerHTTPFirewallDailyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,137 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ServerHTTPFirewallHourlyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerHTTPFirewallHourlyStatDAO.Clean(nil, 15) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerHTTPFirewallHourlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewServerHTTPFirewallHourlyStatDAO() *ServerHTTPFirewallHourlyStatDAO {
|
||||
return dbs.NewDAO(&ServerHTTPFirewallHourlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerHTTPFirewallHourlyStats",
|
||||
Model: new(ServerHTTPFirewallHourlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerHTTPFirewallHourlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerHTTPFirewallHourlyStatDAO *ServerHTTPFirewallHourlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerHTTPFirewallHourlyStatDAO = NewServerHTTPFirewallHourlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseHourlyCount 增加数量
|
||||
func (this *ServerHTTPFirewallHourlyStatDAO) IncreaseHourlyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, hour string, count int64) error {
|
||||
if len(hour) != 10 {
|
||||
return errors.New("invalid hour '" + hour + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"day": hour[:8],
|
||||
"hour": hour,
|
||||
"httpFirewallRuleGroupId": firewallRuleGroupId,
|
||||
"action": action,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SumHourlyCount 计算某天的数据
|
||||
func (this *ServerHTTPFirewallHourlyStatDAO) SumHourlyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
|
||||
query := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
if len(action) > 0 {
|
||||
query.Attr("action", action)
|
||||
}
|
||||
return query.SumInt64("count", 0)
|
||||
}
|
||||
|
||||
// GroupHourlyCount 查询规则分组数量
|
||||
func (this *ServerHTTPFirewallHourlyStatDAO) GroupHourlyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallHourlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
_, err = query.Group("httpFirewallRuleGroupId").
|
||||
Result("httpFirewallRuleGroupId, SUM(count) AS count").
|
||||
Desc("count").
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// FindHourlyStats 查询某个日期段内的记录
|
||||
func (this *ServerHTTPFirewallHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, userId int64, serverId int64, action string, hourFrom string, hourTo string) (result []*ServerHTTPFirewallHourlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Attr("action", action)
|
||||
if serverId > 0 {
|
||||
query.Attr("serverId", serverId)
|
||||
} else if userId > 0 {
|
||||
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
|
||||
Param("userId", userId)
|
||||
}
|
||||
_, err = query.Group("hour").
|
||||
Result("hour, SUM(count) AS count").
|
||||
Slice(&result).
|
||||
FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理历史数据
|
||||
func (this *ServerHTTPFirewallHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
|
||||
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("hour", hour).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package stats
|
||||
|
||||
// ServerHTTPFirewallHourlyStat WAF统计
|
||||
type ServerHTTPFirewallHourlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
Hour string `field:"hour"` // YYYYMMDDHH
|
||||
HttpFirewallRuleGroupId uint32 `field:"httpFirewallRuleGroupId"` // WAF分组ID
|
||||
Action string `field:"action"` // 采取的动作
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerHTTPFirewallHourlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
Day interface{} // YYYYMMDD
|
||||
Hour interface{} // YYYYMMDDHH
|
||||
HttpFirewallRuleGroupId interface{} // WAF分组ID
|
||||
Action interface{} // 采取的动作
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerHTTPFirewallHourlyStatOperator() *ServerHTTPFirewallHourlyStatOperator {
|
||||
return &ServerHTTPFirewallHourlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,103 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerRegionCityMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("SharedServerRegionCityMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerRegionCityMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerRegionCityMonthlyStatDAO() *ServerRegionCityMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerRegionCityMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerRegionCityMonthlyStats",
|
||||
Model: new(ServerRegionCityMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerRegionCityMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerRegionCityMonthlyStatDAO *ServerRegionCityMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerRegionCityMonthlyStatDAO = NewServerRegionCityMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerRegionCityMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, cityId int64, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"cityId": cityId,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerRegionCityMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, provinceId int64, offset int64, size int64) (result []*ServerRegionCityMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
if countryId > 0 {
|
||||
query.Where("cityId IN (SELECT id FROM "+regions.SharedRegionCityDAO.Table+" WHERE provinceId IN (SELECT id FROM "+regions.SharedRegionProvinceDAO.Table+" WHERE countryId=:countryId AND state=1) AND state=1)").
|
||||
Param("countryId", countryId)
|
||||
}
|
||||
if provinceId > 0 {
|
||||
query.Where("cityId IN (SELECT id FROM "+regions.SharedRegionCityDAO.Table+" WHERE provinceId=:provinceId AND state=1)").
|
||||
Param("provinceId", provinceId)
|
||||
}
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerRegionCityMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerRegionCityMonthlyStatDAO_Clean(t *testing.T) {
|
||||
var dao = NewServerRegionCityMonthlyStatDAO()
|
||||
err := dao.Clean(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package stats
|
||||
|
||||
// 服务用户省份分布统计(按天)
|
||||
type ServerRegionCityMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
CityId uint32 `field:"cityId"` // 城市ID
|
||||
Month string `field:"month"` // 月份YYYYMM
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerRegionCityMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
CityId interface{} // 城市ID
|
||||
Month interface{} // 月份YYYYMM
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerRegionCityMonthlyStatOperator() *ServerRegionCityMonthlyStatOperator {
|
||||
return &ServerRegionCityMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,173 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerRegionCountryDailyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("ServerRegionCountryDailyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerRegionCountryDailyStatDAO dbs.DAO
|
||||
|
||||
func NewServerRegionCountryDailyStatDAO() *ServerRegionCountryDailyStatDAO {
|
||||
return dbs.NewDAO(&ServerRegionCountryDailyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerRegionCountryDailyStats",
|
||||
Model: new(ServerRegionCountryDailyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerRegionCountryDailyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerRegionCountryDailyStatDAO *ServerRegionCountryDailyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerRegionCountryDailyStatDAO = NewServerRegionCountryDailyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseDailyStat 增加统计
|
||||
func (this *ServerRegionCountryDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, serverId int64, countryId int64, day string, bytes int64, countRequests int64, attackBytes int64, countAttackRequests int64) error {
|
||||
if len(day) != 8 {
|
||||
return errors.New("invalid day '" + day + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("bytes", bytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"countryId": countryId,
|
||||
"day": day,
|
||||
"bytes": bytes,
|
||||
"attackBytes": attackBytes,
|
||||
"countRequests": countRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListServerStats 查找单页数据
|
||||
func (this *ServerRegionCountryDailyStatDAO) ListServerStats(tx *dbs.Tx, serverId int64, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("day", day).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result)
|
||||
|
||||
switch orderField {
|
||||
case "bytes":
|
||||
query.Desc("bytes")
|
||||
case "countRequests":
|
||||
query.Desc("countRequests")
|
||||
case "attackBytes":
|
||||
query.Desc("attackBytes")
|
||||
query.Gt("attackBytes", 0)
|
||||
case "countAttackRequests":
|
||||
query.Desc("countAttackRequests")
|
||||
query.Gt("countAttackRequests", 0)
|
||||
}
|
||||
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// ListSumStats 查找总体数据
|
||||
func (this *ServerRegionCountryDailyStatDAO) ListSumStats(tx *dbs.Tx, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("day", day).
|
||||
Result("countryId", "SUM(bytes) AS bytes", "SUM(countRequests) AS countRequests", "SUM(attackBytes) AS attackBytes", "SUM(countAttackRequests) AS countAttackRequests").
|
||||
Group("countryId").
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result)
|
||||
|
||||
switch orderField {
|
||||
case "bytes":
|
||||
query.Desc("bytes")
|
||||
case "countRequests":
|
||||
query.Desc("countRequests")
|
||||
case "attackBytes":
|
||||
query.Desc("attackBytes")
|
||||
query.Gt("attackBytes", 0)
|
||||
case "countAttackRequests":
|
||||
query.Desc("countAttackRequests")
|
||||
query.Gt("countAttackRequests", 0)
|
||||
}
|
||||
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// SumDailyTotalBytes 计算总流量
|
||||
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytes(tx *dbs.Tx, day string) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Attr("day", day).
|
||||
SumInt64("bytes", 0)
|
||||
}
|
||||
|
||||
// SumDailyTotalAttackRequests 计算总攻击次数
|
||||
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequests(tx *dbs.Tx, day string) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Attr("day", day).
|
||||
SumInt64("countAttackRequests", 0)
|
||||
}
|
||||
|
||||
// SumDailyTotalBytesWithServerId 计算单个服务的总流量
|
||||
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytesWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Attr("day", day).
|
||||
Attr("serverId", serverId).
|
||||
SumInt64("bytes", 0)
|
||||
}
|
||||
|
||||
// SumDailyTotalAttackRequestsWithServerId 计算单个服务的总攻击次数
|
||||
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequestsWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
Attr("day", day).
|
||||
Attr("serverId", serverId).
|
||||
SumInt64("countAttackRequests", 0)
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerRegionCountryDailyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留7天的
|
||||
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -7))
|
||||
_, err := this.Query(tx).
|
||||
Lte("day", day).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerRegionCountryDailyStatDAO_IncreaseDailyStat(t *testing.T) {
|
||||
var tx *dbs.Tx
|
||||
err := NewServerRegionCountryDailyStatDAO().IncreaseDailyStat(tx, 1, 3, timeutil.Format("Ymd"), 2, 2, 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
|
||||
func TestServerRegionCountryDailyStatDAO_ListSumStats(t *testing.T) {
|
||||
var tx *dbs.Tx
|
||||
stats, err := NewServerRegionCountryDailyStatDAO().ListSumStats(tx, timeutil.Format("Ymd"), "countAttackRequests", 0, 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, stat := range stats {
|
||||
statJSON, err := json.Marshal(stat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(statJSON))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package stats
|
||||
|
||||
// ServerRegionCountryDailyStat 服务用户区域分布统计(按天)
|
||||
type ServerRegionCountryDailyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
CountryId uint32 `field:"countryId"` // 国家/区域ID
|
||||
Day string `field:"day"` // 日期YYYYMMDD
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数量
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数量
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
Bytes uint64 `field:"bytes"` // 总流量
|
||||
}
|
||||
|
||||
type ServerRegionCountryDailyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
CountryId interface{} // 国家/区域ID
|
||||
Day interface{} // 日期YYYYMMDD
|
||||
CountRequests interface{} // 请求数量
|
||||
CountAttackRequests interface{} // 攻击数量
|
||||
AttackBytes interface{} // 攻击流量
|
||||
Bytes interface{} // 总流量
|
||||
}
|
||||
|
||||
func NewServerRegionCountryDailyStatOperator() *ServerRegionCountryDailyStatOperator {
|
||||
return &ServerRegionCountryDailyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,94 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerRegionCountryMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("SharedServerRegionCountryMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerRegionCountryMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerRegionCountryMonthlyStatDAO() *ServerRegionCountryMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerRegionCountryMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerRegionCountryMonthlyStats",
|
||||
Model: new(ServerRegionCountryMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerRegionCountryMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerRegionCountryMonthlyStatDAO *ServerRegionCountryMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerRegionCountryMonthlyStatDAO = NewServerRegionCountryMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerRegionCountryMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, countryId int64, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"countryId": countryId,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerRegionCountryMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionCountryMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerRegionCountryMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package stats
|
||||
|
||||
// 服务用户区域分布统计(按天)
|
||||
type ServerRegionCountryMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
CountryId uint32 `field:"countryId"` // 国家/区域ID
|
||||
Month string `field:"month"` // 月份YYYYMM
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerRegionCountryMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
CountryId interface{} // 国家/区域ID
|
||||
Month interface{} // 月份YYYYMM
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerRegionCountryMonthlyStatOperator() *ServerRegionCountryMonthlyStatOperator {
|
||||
return &ServerRegionCountryMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,94 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerRegionProviderMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("SharedServerRegionProviderMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerRegionProviderMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerRegionProviderMonthlyStatDAO() *ServerRegionProviderMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerRegionProviderMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerRegionProviderMonthlyStats",
|
||||
Model: new(ServerRegionProviderMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerRegionProviderMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerRegionProviderMonthlyStatDAO *ServerRegionProviderMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerRegionProviderMonthlyStatDAO = NewServerRegionProviderMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerRegionProviderMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, providerId int64, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"providerId": providerId,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerRegionProviderMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionProviderMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerRegionProviderMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerRegionProviderMonthlyStatDAO_Clean(t *testing.T) {
|
||||
var dao = NewServerRegionProviderMonthlyStatDAO()
|
||||
err := dao.Clean(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok")
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package stats
|
||||
|
||||
// 服务用户省份分布统计(按天)
|
||||
type ServerRegionProviderMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
ProviderId uint32 `field:"providerId"` // 运营商ID
|
||||
Month string `field:"month"` // 月份YYYYMM
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerRegionProviderMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
ProviderId interface{} // 运营商ID
|
||||
Month interface{} // 月份YYYYMM
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerRegionProviderMonthlyStatOperator() *ServerRegionProviderMonthlyStatOperator {
|
||||
return &ServerRegionProviderMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
@@ -0,0 +1,100 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedServerRegionProvinceMonthlyStatDAO.Clean(nil)
|
||||
if err != nil {
|
||||
remotelogs.Error("SharedServerRegionProvinceMonthlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type ServerRegionProvinceMonthlyStatDAO dbs.DAO
|
||||
|
||||
func NewServerRegionProvinceMonthlyStatDAO() *ServerRegionProvinceMonthlyStatDAO {
|
||||
return dbs.NewDAO(&ServerRegionProvinceMonthlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeServerRegionProvinceMonthlyStats",
|
||||
Model: new(ServerRegionProvinceMonthlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*ServerRegionProvinceMonthlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedServerRegionProvinceMonthlyStatDAO *ServerRegionProvinceMonthlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedServerRegionProvinceMonthlyStatDAO = NewServerRegionProvinceMonthlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseMonthlyCount 增加数量
|
||||
func (this *ServerRegionProvinceMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, provinceId int64, month string, count int64) error {
|
||||
if len(month) != 6 {
|
||||
return errors.New("invalid month '" + month + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("count", count).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"serverId": serverId,
|
||||
"provinceId": provinceId,
|
||||
"month": month,
|
||||
"count": count,
|
||||
}, maps.Map{
|
||||
"count": dbs.SQL("count+:count"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListStats 查找单页数据
|
||||
func (this *ServerRegionProvinceMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, offset int64, size int64) (result []*ServerRegionProvinceMonthlyStat, err error) {
|
||||
query := this.Query(tx).
|
||||
Attr("serverId", serverId).
|
||||
Attr("month", month).
|
||||
Offset(offset).
|
||||
Limit(size).
|
||||
Slice(&result).
|
||||
Desc("count")
|
||||
if countryId > 0 {
|
||||
query.Where("id IN (SELECT id FROM "+regions.SharedRegionProvinceDAO.Table+" WHERE countryId=:countryId AND state=1)").
|
||||
Param("countryId", countryId)
|
||||
}
|
||||
|
||||
_, err = query.FindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Clean 清理统计数据
|
||||
func (this *ServerRegionProvinceMonthlyStatDAO) Clean(tx *dbs.Tx) error {
|
||||
// 只保留两个月的
|
||||
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
|
||||
_, err := this.Query(tx).
|
||||
Lte("month", month).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package stats
|
||||
|
||||
// 服务用户省份分布统计(按天)
|
||||
type ServerRegionProvinceMonthlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
ServerId uint32 `field:"serverId"` // 服务ID
|
||||
ProvinceId uint32 `field:"provinceId"` // 省份ID
|
||||
Month string `field:"month"` // 月份YYYYMM
|
||||
Count uint64 `field:"count"` // 数量
|
||||
}
|
||||
|
||||
type ServerRegionProvinceMonthlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
ServerId interface{} // 服务ID
|
||||
ProvinceId interface{} // 省份ID
|
||||
Month interface{} // 月份YYYYMM
|
||||
Count interface{} // 数量
|
||||
}
|
||||
|
||||
func NewServerRegionProvinceMonthlyStatOperator() *ServerRegionProvinceMonthlyStatOperator {
|
||||
return &ServerRegionProvinceMonthlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
167
EdgeAPI/internal/db/models/stats/traffic_daily_stat_dao.go
Normal file
167
EdgeAPI/internal/db/models/stats/traffic_daily_stat_dao.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrafficDailyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedTrafficDailyStatDAO.CleanDefaultDays(nil, 30) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("TrafficDailyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewTrafficDailyStatDAO() *TrafficDailyStatDAO {
|
||||
return dbs.NewDAO(&TrafficDailyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeTrafficDailyStats",
|
||||
Model: new(TrafficDailyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*TrafficDailyStatDAO)
|
||||
}
|
||||
|
||||
var SharedTrafficDailyStatDAO *TrafficDailyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedTrafficDailyStatDAO = NewTrafficDailyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseDailyStat 增加统计数据
|
||||
func (this *TrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
|
||||
if len(day) != 8 {
|
||||
return errors.New("invalid day '" + day + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("bytes", bytes).
|
||||
Param("cachedBytes", cachedBytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("countCachedRequests", countCachedRequests).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"day": day,
|
||||
"bytes": bytes,
|
||||
"cachedBytes": cachedBytes,
|
||||
"countRequests": countRequests,
|
||||
"countCachedRequests": countCachedRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
"attackBytes": attackBytes,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncreaseIPs 增加独立IP统计数据
|
||||
func (this *TrafficDailyStatDAO) IncreaseIPs(tx *dbs.Tx, day string, countIPs int64) error {
|
||||
if len(day) != 8 {
|
||||
return errors.New("invalid day '" + day + "'")
|
||||
}
|
||||
|
||||
return this.Query(tx).
|
||||
Param("countIPs", countIPs).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"day": day,
|
||||
"countIPs": countIPs,
|
||||
}, maps.Map{
|
||||
"countIPs": dbs.SQL("countIPs+:countIPs"),
|
||||
})
|
||||
}
|
||||
|
||||
// FindDailyStats 获取日期之间统计
|
||||
func (this *TrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*TrafficDailyStat, err error) {
|
||||
ones, err := this.Query(tx).
|
||||
Between("day", dayFrom, dayTo).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dayMap := map[string]*TrafficDailyStat{} // day => Stat
|
||||
for _, one := range ones {
|
||||
stat := one.(*TrafficDailyStat)
|
||||
dayMap[stat.Day] = stat
|
||||
}
|
||||
days, err := utils.RangeDays(dayFrom, dayTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, day := range days {
|
||||
stat, ok := dayMap[day]
|
||||
if ok {
|
||||
result = append(result, stat)
|
||||
} else {
|
||||
result = append(result, &TrafficDailyStat{Day: day})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FindDailyStat 查找某天的统计
|
||||
func (this *TrafficDailyStatDAO) FindDailyStat(tx *dbs.Tx, day string) (*TrafficDailyStat, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("day", day).
|
||||
Find()
|
||||
if err != nil || one == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return one.(*TrafficDailyStat), nil
|
||||
}
|
||||
|
||||
// CleanDays 清理历史数据
|
||||
func (this *TrafficDailyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
|
||||
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("day", day).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *TrafficDailyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
|
||||
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseConfig != nil && databaseConfig.TrafficDailyStat.Clean.Days > 0 {
|
||||
defaultDays = databaseConfig.TrafficDailyStat.Clean.Days
|
||||
}
|
||||
if defaultDays <= 0 {
|
||||
defaultDays = 30
|
||||
}
|
||||
|
||||
return this.CleanDays(tx, defaultDays)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTrafficDailyStatDAO_IncreaseDayBytes(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var now = time.Now()
|
||||
err := SharedTrafficDailyStatDAO.IncreaseDailyStat(nil, timeutil.Format("Ymd"), 1, 1, 1, 1, 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok", time.Since(now).Seconds()*1000, "ms")
|
||||
}
|
||||
|
||||
func TestTrafficDailyStatDAO_IncreaseIPs(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
var tx *dbs.Tx
|
||||
err := SharedTrafficDailyStatDAO.IncreaseIPs(tx, timeutil.Format("Ymd"), 123)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
44
EdgeAPI/internal/db/models/stats/traffic_daily_stat_model.go
Normal file
44
EdgeAPI/internal/db/models/stats/traffic_daily_stat_model.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package stats
|
||||
|
||||
import "github.com/iwind/TeaGo/dbs"
|
||||
|
||||
const (
|
||||
TrafficDailyStatField_Id dbs.FieldName = "id" // ID
|
||||
TrafficDailyStatField_Day dbs.FieldName = "day" // YYYYMMDD
|
||||
TrafficDailyStatField_CachedBytes dbs.FieldName = "cachedBytes" // 缓存流量
|
||||
TrafficDailyStatField_Bytes dbs.FieldName = "bytes" // 流量字节
|
||||
TrafficDailyStatField_CountRequests dbs.FieldName = "countRequests" // 请求数
|
||||
TrafficDailyStatField_CountCachedRequests dbs.FieldName = "countCachedRequests" // 缓存请求数
|
||||
TrafficDailyStatField_CountAttackRequests dbs.FieldName = "countAttackRequests" // 攻击量
|
||||
TrafficDailyStatField_AttackBytes dbs.FieldName = "attackBytes" // 攻击流量
|
||||
TrafficDailyStatField_CountIPs dbs.FieldName = "countIPs" // 独立IP数
|
||||
)
|
||||
|
||||
// TrafficDailyStat 总的流量统计(按天)
|
||||
type TrafficDailyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
|
||||
Bytes uint64 `field:"bytes"` // 流量字节
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击量
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
CountIPs uint64 `field:"countIPs"` // 独立IP数
|
||||
}
|
||||
|
||||
type TrafficDailyStatOperator struct {
|
||||
Id any // ID
|
||||
Day any // YYYYMMDD
|
||||
CachedBytes any // 缓存流量
|
||||
Bytes any // 流量字节
|
||||
CountRequests any // 请求数
|
||||
CountCachedRequests any // 缓存请求数
|
||||
CountAttackRequests any // 攻击量
|
||||
AttackBytes any // 攻击流量
|
||||
CountIPs any // 独立IP数
|
||||
}
|
||||
|
||||
func NewTrafficDailyStatOperator() *TrafficDailyStatOperator {
|
||||
return &TrafficDailyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
164
EdgeAPI/internal/db/models/stats/traffic_hourly_stat_dao.go
Normal file
164
EdgeAPI/internal/db/models/stats/traffic_hourly_stat_dao.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrafficHourlyStatDAO dbs.DAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReadyDone(func() {
|
||||
// 清理数据任务
|
||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||
goman.New(func() {
|
||||
for range ticker.C {
|
||||
err := SharedTrafficHourlyStatDAO.CleanDefaultDays(nil, 15) // 只保留N天
|
||||
if err != nil {
|
||||
remotelogs.Error("TrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NewTrafficHourlyStatDAO() *TrafficHourlyStatDAO {
|
||||
return dbs.NewDAO(&TrafficHourlyStatDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeTrafficHourlyStats",
|
||||
Model: new(TrafficHourlyStat),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*TrafficHourlyStatDAO)
|
||||
}
|
||||
|
||||
var SharedTrafficHourlyStatDAO *TrafficHourlyStatDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedTrafficHourlyStatDAO = NewTrafficHourlyStatDAO()
|
||||
})
|
||||
}
|
||||
|
||||
// IncreaseHourlyStat 增加流量
|
||||
func (this *TrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
|
||||
if len(hour) != 10 {
|
||||
return errors.New("invalid hour '" + hour + "'")
|
||||
}
|
||||
err := this.Query(tx).
|
||||
Param("bytes", bytes).
|
||||
Param("cachedBytes", cachedBytes).
|
||||
Param("countRequests", countRequests).
|
||||
Param("countCachedRequests", countCachedRequests).
|
||||
Param("countAttackRequests", countAttackRequests).
|
||||
Param("attackBytes", attackBytes).
|
||||
InsertOrUpdateQuickly(maps.Map{
|
||||
"hour": hour,
|
||||
"bytes": bytes,
|
||||
"cachedBytes": cachedBytes,
|
||||
"countRequests": countRequests,
|
||||
"countCachedRequests": countCachedRequests,
|
||||
"countAttackRequests": countAttackRequests,
|
||||
"attackBytes": attackBytes,
|
||||
}, maps.Map{
|
||||
"bytes": dbs.SQL("bytes+:bytes"),
|
||||
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
|
||||
"countRequests": dbs.SQL("countRequests+:countRequests"),
|
||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindHourlyStats 获取小时之间统计
|
||||
func (this *TrafficHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*TrafficHourlyStat, err error) {
|
||||
ones, err := this.Query(tx).
|
||||
Between("hour", hourFrom, hourTo).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourMap := map[string]*TrafficHourlyStat{} // hour => Stat
|
||||
for _, one := range ones {
|
||||
stat := one.(*TrafficHourlyStat)
|
||||
hourMap[stat.Hour] = stat
|
||||
}
|
||||
hours, err := utils.RangeHours(hourFrom, hourTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, hour := range hours {
|
||||
stat, ok := hourMap[hour]
|
||||
if ok {
|
||||
result = append(result, stat)
|
||||
} else {
|
||||
result = append(result, &TrafficHourlyStat{Hour: hour})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// FindHourlyStat 查FindHourlyStat 找单个小时的统计
|
||||
func (this *TrafficHourlyStatDAO) FindHourlyStat(tx *dbs.Tx, hour string) (*TrafficHourlyStat, error) {
|
||||
one, err := this.Query(tx).
|
||||
Attr("hour", hour).
|
||||
Find()
|
||||
if err != nil || one == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return one.(*TrafficHourlyStat), err
|
||||
}
|
||||
|
||||
// SumHourlyStats 计算多个小时的统计总和
|
||||
func (this *TrafficHourlyStatDAO) SumHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (*TrafficHourlyStat, error) {
|
||||
one, err := this.Query(tx).
|
||||
Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes").
|
||||
Between("hour", hourFrom, hourTo).
|
||||
Find()
|
||||
if err != nil || one == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return one.(*TrafficHourlyStat), nil
|
||||
}
|
||||
|
||||
// CleanDays 清理历史数据
|
||||
func (this *TrafficHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
|
||||
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
|
||||
_, err := this.Query(tx).
|
||||
Lt("hour", hour).
|
||||
Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *TrafficHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
|
||||
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseConfig != nil && databaseConfig.TrafficHourlyStat.Clean.Days > 0 {
|
||||
defaultDays = databaseConfig.TrafficHourlyStat.Clean.Days
|
||||
}
|
||||
if defaultDays <= 0 {
|
||||
defaultDays = 15
|
||||
}
|
||||
|
||||
return this.CleanDays(tx, defaultDays)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTrafficHourlyStatDAO_IncreaseDayBytes(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
now := time.Now()
|
||||
err := SharedTrafficHourlyStatDAO.IncreaseHourlyStat(nil, timeutil.Format("YmdH"), 1, 1, 1, 1, 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("ok", time.Since(now).Seconds()*1000, "ms")
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package stats
|
||||
|
||||
// TrafficHourlyStat 总的流量统计(按小时)
|
||||
type TrafficHourlyStat struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
Hour string `field:"hour"` // YYYYMMDDHH
|
||||
Bytes uint64 `field:"bytes"` // 流量字节
|
||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
|
||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
|
||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数
|
||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||
}
|
||||
|
||||
type TrafficHourlyStatOperator struct {
|
||||
Id interface{} // ID
|
||||
Hour interface{} // YYYYMMDDHH
|
||||
Bytes interface{} // 流量字节
|
||||
CachedBytes interface{} // 缓存流量
|
||||
CountRequests interface{} // 请求数
|
||||
CountCachedRequests interface{} // 缓存请求数
|
||||
CountAttackRequests interface{} // 攻击数
|
||||
AttackBytes interface{} // 攻击流量
|
||||
}
|
||||
|
||||
func NewTrafficHourlyStatOperator() *TrafficHourlyStatOperator {
|
||||
return &TrafficHourlyStatOperator{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package stats
|
||||
Reference in New Issue
Block a user