实现集群看板

This commit is contained in:
GoEdgeLab
2021-07-05 11:37:22 +08:00
parent ed79eba802
commit a539bcfdf8
38 changed files with 1267 additions and 75 deletions

View File

@@ -379,6 +379,14 @@ func (this *NSNodeDAO) ComposeNodeConfig(tx *dbs.Tx, nodeId int64) (*dnsconfigs.
return config, nil
}
// FindNodeClusterId 获取节点的集群ID
func (this *NSNodeDAO) FindNodeClusterId(tx *dbs.Tx, nodeId int64) (int64, error) {
return this.Query(tx).
Pk(nodeId).
Result("clusterId").
FindInt64Col(0)
}
// NotifyUpdate 通知更新
func (this *NSNodeDAO) NotifyUpdate(tx *dbs.Tx, nodeId int64) error {
// TODO 先什么都不做

View File

@@ -33,13 +33,14 @@ func init() {
}
// CreateValue 创建值
func (this *NodeValueDAO) CreateValue(tx *dbs.Tx, role nodeconfigs.NodeRole, nodeId int64, item string, valueJSON []byte, createdAt int64) error {
func (this *NodeValueDAO) CreateValue(tx *dbs.Tx, clusterId int64, role nodeconfigs.NodeRole, nodeId int64, item string, valueJSON []byte, createdAt int64) error {
day := timeutil.FormatTime("Ymd", createdAt)
hour := timeutil.FormatTime("YmdH", createdAt)
minute := timeutil.FormatTime("YmdHi", createdAt)
return this.Query(tx).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"item": item,
@@ -90,6 +91,31 @@ func (this *NodeValueDAO) ListValues(tx *dbs.Tx, role string, nodeId int64, item
return
}
// ListValuesWithClusterId 列出集群最近的的平均数据
func (this *NodeValueDAO) ListValuesWithClusterId(tx *dbs.Tx, clusterId int64, role string, item string, key string, timeRange nodeconfigs.NodeValueRange) (result []*NodeValue, err error) {
query := this.Query(tx).
Attr("role", role).
Attr("clusterId", clusterId).
Attr("item", item).
Result("AVG(JSON_EXTRACT(value, '$." + key + "')) AS value, MIN(createdAt) AS createdAt")
switch timeRange {
// TODO 支持更多的时间范围
case nodeconfigs.NodeValueRangeMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-3600) // 一个小时之前的
query.Gte("minute", fromMinute)
query.Result("minute")
query.Group("minute")
default:
err = errors.New("invalid 'range' value: '" + timeRange + "'")
return
}
_, err = query.Slice(&result).
FindAll()
return
}
// SumValues 计算某项参数值
func (this *NodeValueDAO) SumValues(tx *dbs.Tx, role string, nodeId int64, item string, param string, method nodeconfigs.NodeValueSumMethod, duration int32, durationUnit nodeconfigs.NodeValueDurationUnit) (float64, error) {
if duration <= 0 {
@@ -110,10 +136,10 @@ func (this *NodeValueDAO) SumValues(tx *dbs.Tx, role string, nodeId int64, item
}
switch durationUnit {
case nodeconfigs.NodeValueDurationUnitMinute:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration * 60))
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration*60))
query.Gte("minute", fromMinute)
default:
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration * 60))
fromMinute := timeutil.FormatTime("YmdHi", time.Now().Unix()-int64(duration*60))
query.Gte("minute", fromMinute)
}
return query.FindFloat64Col(0)

View File

@@ -3,6 +3,7 @@ package models
// NodeValue 节点监控数据
type NodeValue struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
Role string `field:"role"` // 节点角色
Item string `field:"item"` // 监控项
@@ -15,6 +16,7 @@ type NodeValue struct {
type NodeValueOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
Role interface{} // 节点角色
Item interface{} // 监控项

View File

@@ -5,13 +5,30 @@ import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"regexp"
"time"
)
type ServerDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedServerDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
logs.Println("ServerDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewServerDailyStatDAO() *ServerDailyStatDAO {
return dbs.NewDAO(&ServerDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -35,6 +52,7 @@ func init() {
func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailyStat) error {
for _, stat := range stats {
day := timeutil.FormatTime("Ymd", stat.CreatedAt)
hour := timeutil.FormatTime("YmdH", stat.CreatedAt)
timeFrom := timeutil.FormatTime("His", stat.CreatedAt)
timeTo := timeutil.FormatTime("His", stat.CreatedAt+5*60-1) // 5分钟
@@ -51,6 +69,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"day": day,
"hour": hour,
"timeFrom": timeFrom,
"timeTo": timeTo,
}, maps.Map{
@@ -217,3 +236,12 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, serverId int64, day str
stat.CountCachedRequests = one.GetInt64("countCachedRequests")
return
}
// Clean 清理历史数据
func (this *ServerDailyStatDAO) 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
}

View File

@@ -10,6 +10,7 @@ type ServerDailyStat struct {
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
Day string `field:"day"` // 日期YYYYMMDD
Hour string `field:"hour"` // YYYYMMDDHH
TimeFrom string `field:"timeFrom"` // 开始时间HHMMSS
TimeTo string `field:"timeTo"` // 结束时间
IsCharged uint8 `field:"isCharged"` // 是否已计算费用
@@ -24,6 +25,7 @@ type ServerDailyStatOperator struct {
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
Day interface{} // 日期YYYYMMDD
Hour interface{} // YYYYMMDDHH
TimeFrom interface{} // 开始时间HHMMSS
TimeTo interface{} // 结束时间
IsCharged interface{} // 是否已计算费用

View File

@@ -0,0 +1,93 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
ServerStatBoardChartStateEnabled = 1 // 已启用
ServerStatBoardChartStateDisabled = 0 // 已禁用
)
type ServerStatBoardChartDAO dbs.DAO
func NewServerStatBoardChartDAO() *ServerStatBoardChartDAO {
return dbs.NewDAO(&ServerStatBoardChartDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerStatBoardCharts",
Model: new(ServerStatBoardChart),
PkName: "id",
},
}).(*ServerStatBoardChartDAO)
}
var SharedServerStatBoardChartDAO *ServerStatBoardChartDAO
func init() {
dbs.OnReady(func() {
SharedServerStatBoardChartDAO = NewServerStatBoardChartDAO()
})
}
// EnableServerStatBoardChart 启用条目
func (this *ServerStatBoardChartDAO) EnableServerStatBoardChart(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardChartStateEnabled).
Update()
return err
}
// DisableServerStatBoardChart 禁用条目
func (this *ServerStatBoardChartDAO) DisableServerStatBoardChart(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardChartStateDisabled).
Update()
return err
}
// FindEnabledServerStatBoardChart 查找启用中的条目
func (this *ServerStatBoardChartDAO) FindEnabledServerStatBoardChart(tx *dbs.Tx, id uint64) (*ServerStatBoardChart, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ServerStatBoardChartStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ServerStatBoardChart), err
}
// EnableChart 启用图表
func (this *ServerStatBoardChartDAO) EnableChart(tx *dbs.Tx, boardId int64, chartId int64) error {
op := NewServerStatBoardChartOperator()
op.BoardId = boardId
op.ChartId = chartId
op.State = ServerStatBoardChartStateEnabled
return this.Save(tx, op)
}
// DisableChart 禁用图表
func (this *ServerStatBoardChartDAO) DisableChart(tx *dbs.Tx, boardId int64, chartId int64) error {
return this.Query(tx).
Attr("borderId", boardId).
Attr("chartId", chartId).
Set("state", ServerStatBoardChartStateDisabled).
UpdateQuickly()
}
// FindAllEnabledCharts 查找看板中所有图表
func (this *ServerStatBoardChartDAO) FindAllEnabledCharts(tx *dbs.Tx, boardId int64) (result []*ServerStatBoardChart, err error) {
_, err = this.Query(tx).
Attr("boardId", boardId).
Desc("order").
AscPk().
State(ServerStatBoardChartStateEnabled).
Slice(&result).
FindAll()
return
}

View File

@@ -0,0 +1,6 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,26 @@
package models
// ServerStatBoardChart 服务看板中的图表
type ServerStatBoardChart struct {
Id uint64 `field:"id"` // ID
BoardId uint64 `field:"boardId"` // 看板ID
Code string `field:"code"` // 内置图表代码
ItemId uint32 `field:"itemId"` // 指标ID
ChartId uint32 `field:"chartId"` // 图表ID
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type ServerStatBoardChartOperator struct {
Id interface{} // ID
BoardId interface{} // 看板ID
Code interface{} // 内置图表代码
ItemId interface{} // 指标ID
ChartId interface{} // 图表ID
Order interface{} // 排序
State interface{} // 状态
}
func NewServerStatBoardChartOperator() *ServerStatBoardChartOperator {
return &ServerStatBoardChartOperator{}
}

View File

@@ -0,0 +1 @@
package models

View File

@@ -0,0 +1,83 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
)
const (
ServerStatBoardStateEnabled = 1 // 已启用
ServerStatBoardStateDisabled = 0 // 已禁用
)
type ServerStatBoardDAO dbs.DAO
func NewServerStatBoardDAO() *ServerStatBoardDAO {
return dbs.NewDAO(&ServerStatBoardDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerStatBoards",
Model: new(ServerStatBoard),
PkName: "id",
},
}).(*ServerStatBoardDAO)
}
var SharedServerStatBoardDAO *ServerStatBoardDAO
func init() {
dbs.OnReady(func() {
SharedServerStatBoardDAO = NewServerStatBoardDAO()
})
}
// EnableServerStatBoard 启用条目
func (this *ServerStatBoardDAO) EnableServerStatBoard(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardStateEnabled).
Update()
return err
}
// DisableServerStatBoard 禁用条目
func (this *ServerStatBoardDAO) DisableServerStatBoard(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", ServerStatBoardStateDisabled).
Update()
return err
}
// FindEnabledServerStatBoard 查找启用中的条目
func (this *ServerStatBoardDAO) FindEnabledServerStatBoard(tx *dbs.Tx, id uint64) (*ServerStatBoard, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", ServerStatBoardStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*ServerStatBoard), err
}
// FindServerStatBoardName 根据主键查找名称
func (this *ServerStatBoardDAO) FindServerStatBoardName(tx *dbs.Tx, id uint64) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindAllEnabledBoards 查找看板
func (this *ServerStatBoardDAO) FindAllEnabledBoards(tx *dbs.Tx, clusterId int64) (result []*ServerStatBoard, err error) {
_, err = this.Query(tx).
Attr("clusterId", clusterId).
State(ServerStatBoardStateEnabled).
Slice(&result).
Desc("order").
AscPk().
FindAll()
return
}

View File

@@ -0,0 +1,6 @@
package models
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,24 @@
package models
// ServerStatBoard 服务统计看板
type ServerStatBoard struct {
Id uint64 `field:"id"` // ID
Name string `field:"name"` // 名称
ClusterId uint32 `field:"clusterId"` // 集群ID
IsOn uint8 `field:"isOn"` // 是否启用
Order uint32 `field:"order"` // 排序
State uint8 `field:"state"` // 状态
}
type ServerStatBoardOperator struct {
Id interface{} // ID
Name interface{} // 名称
ClusterId interface{} // 集群ID
IsOn interface{} // 是否启用
Order interface{} // 排序
State interface{} // 状态
}
func NewServerStatBoardOperator() *ServerStatBoardOperator {
return &ServerStatBoardOperator{}
}

View File

@@ -0,0 +1 @@
package models

View File

@@ -2,14 +2,33 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type NodeClusterTrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeClusterTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNodeClusterTrafficDailyStatDAO() *NodeClusterTrafficDailyStatDAO {
return dbs.NewDAO(&NodeClusterTrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -29,22 +48,69 @@ func init() {
})
}
// 增加流量
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, clusterId int64, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests 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).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"day": day,
"bytes": bytes,
"clusterId": clusterId,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
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
}
// Clean 清理历史数据
func (this *NodeClusterTrafficDailyStatDAO) 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
}

View File

@@ -1,18 +1,24 @@
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"` // 流量字节
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"` // 缓存的请求数
}
type NodeClusterTrafficDailyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
ClusterId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
}
func NewNodeClusterTrafficDailyStatOperator() *NodeClusterTrafficDailyStatOperator {

View File

@@ -2,14 +2,32 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type NodeTrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("NodeTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewNodeTrafficDailyStatDAO() *NodeTrafficDailyStatDAO {
return dbs.NewDAO(&NodeTrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -29,22 +47,42 @@ func init() {
})
}
// 增加流量
func (this *NodeTrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, nodeId int64, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *NodeTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, role string, nodeId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests 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).
InsertOrUpdateQuickly(maps.Map{
"nodeId": nodeId,
"day": day,
"bytes": bytes,
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
if err != nil {
return err
}
return nil
}
// Clean 清理历史数据
func (this *NodeTrafficDailyStatDAO) 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
}

View File

@@ -1,18 +1,28 @@
package stats
// 总的流量统计(按天)
// NodeTrafficDailyStat 总的流量统计(按天)
type NodeTrafficDailyStat struct {
Id uint64 `field:"id"` // ID
NodeId uint32 `field:"nodeId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
Id uint64 `field:"id"` // ID
Role string `field:"role"` // 节点角色
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
}
type NodeTrafficDailyStatOperator struct {
Id interface{} // ID
NodeId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
Role interface{} // 节点角色
ClusterId interface{} // 集群ID
NodeId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
}
func NewNodeTrafficDailyStatOperator() *NodeTrafficDailyStatOperator {

View File

@@ -0,0 +1,134 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type NodeTrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedNodeTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60天
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) 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).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
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").
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
}
// FindTopNodeStatsWithClusterId 取得一定时间内的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
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").
Group("nodeId").
Desc("countRequests").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *NodeTrafficHourlyStatDAO) 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
}

View File

@@ -0,0 +1,6 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,30 @@
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"` // 缓存的请求数
}
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{} // 缓存的请求数
}
func NewNodeTrafficHourlyStatOperator() *NodeTrafficHourlyStatOperator {
return &NodeTrafficHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,103 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type ServerDomainHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedServerDomainHourlyStatDAO.Clean(nil, 60) // 只保留60天
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()
})
}
// 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) 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).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"hour": hour,
"domain": domain,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
if err != nil {
return err
}
return nil
}
// FindTopDomainStatsWithClusterId 取得一定时间内的节点排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string) (result []*ServerDomainHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("clusterId", clusterId).
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").
Group("domain").
Desc("countRequests").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerDomainHourlyStatDAO) 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
}

View File

@@ -0,0 +1,6 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,32 @@
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"` // 缓存请求
}
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{} // 缓存请求
}
func NewServerDomainHourlyStatOperator() *ServerDomainHourlyStatOperator {
return &ServerDomainHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -2,15 +2,33 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedTrafficDailyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("TrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewTrafficDailyStatDAO() *TrafficDailyStatDAO {
return dbs.NewDAO(&TrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
@@ -30,18 +48,27 @@ func init() {
})
}
// 增加流量
func (this *TrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, day string, bytes int64) error {
// IncreaseDailyStat 增加统计数据
func (this *TrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests 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).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"bytes": bytes,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
if err != nil {
return err
@@ -49,11 +76,14 @@ func (this *TrafficDailyStatDAO) IncreaseDailyBytes(tx *dbs.Tx, day string, byte
return nil
}
// 获取日期之间统计
// 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)
@@ -73,3 +103,12 @@ func (this *TrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayT
}
return result, nil
}
// Clean 清理历史数据
func (this *TrafficDailyStatDAO) 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
}

View File

@@ -1,16 +1,22 @@
package stats
// 总的流量统计
// TrafficDailyStat 总的流量统计(按天)
type TrafficDailyStat struct {
Id uint64 `field:"id"` // ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
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"` // 缓存请求数
}
type TrafficDailyStatOperator struct {
Id interface{} // ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
Id interface{} // ID
Day interface{} // YYYYMMDD
CachedBytes interface{} // 缓存流量
Bytes interface{} // 流量字节
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求数
}
func NewTrafficDailyStatOperator() *TrafficDailyStatOperator {

View File

@@ -2,15 +2,33 @@ package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"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"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
err := SharedTrafficHourlyStatDAO.Clean(nil, 60) // 只保留60天
if err != nil {
remotelogs.Error("TrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
}()
})
}
func NewTrafficHourlyStatDAO() *TrafficHourlyStatDAO {
return dbs.NewDAO(&TrafficHourlyStatDAO{
DAOObject: dbs.DAOObject{
@@ -30,18 +48,27 @@ func init() {
})
}
// 增加流量
func (this *TrafficHourlyStatDAO) IncreaseHourlyBytes(tx *dbs.Tx, hour string, bytes int64) error {
// IncreaseHourlyStat 增加流量
func (this *TrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests 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).
InsertOrUpdateQuickly(maps.Map{
"hour": hour,
"bytes": bytes,
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
})
if err != nil {
return err
@@ -49,11 +76,14 @@ func (this *TrafficHourlyStatDAO) IncreaseHourlyBytes(tx *dbs.Tx, hour string, b
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)
@@ -73,3 +103,12 @@ func (this *TrafficHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, h
}
return result, nil
}
// Clean 清理历史数据
func (this *TrafficHourlyStatDAO) 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
}

View File

@@ -1,16 +1,22 @@
package stats
// 总的流量统计(按小时)
// TrafficHourlyStat 总的流量统计(按小时)
type TrafficHourlyStat struct {
Id uint64 `field:"id"` // ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
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"` // 缓存请求数
}
type TrafficHourlyStatOperator struct {
Id interface{} // ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
Id interface{} // ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求数
}
func NewTrafficHourlyStatOperator() *TrafficHourlyStatOperator {

View File

@@ -156,9 +156,12 @@ func (this *UserDAO) UpdateUserLogin(tx *dbs.Tx, userId int64, username string,
}
// CountAllEnabledUsers 计算用户数量
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, keyword string) (int64, error) {
func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string) (int64, error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(username LIKE :keyword OR fullname LIKE :keyword OR mobile LIKE :keyword OR email LIKE :keyword OR tel LIKE :keyword OR remark LIKE :keyword)").
Param("keyword", "%"+keyword+"%")
@@ -167,9 +170,12 @@ func (this *UserDAO) CountAllEnabledUsers(tx *dbs.Tx, keyword string) (int64, er
}
// ListEnabledUsers 列出单页用户
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*User, err error) {
func (this *UserDAO) ListEnabledUsers(tx *dbs.Tx, clusterId int64, keyword string, offset int64, size int64) (result []*User, err error) {
query := this.Query(tx)
query.State(UserStateEnabled)
if clusterId > 0 {
query.Attr("clusterId", clusterId)
}
if len(keyword) > 0 {
query.Where("(username LIKE :keyword OR fullname LIKE :keyword OR mobile LIKE :keyword OR email LIKE :keyword OR tel LIKE :keyword OR remark LIKE :keyword)").
Param("keyword", "%"+keyword+"%")