| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package dbdata
- import (
- "container/list"
- "errors"
- "fmt"
- "strconv"
- "sync"
- "time"
- "github.com/bjdgyc/anylink/base"
- )
- const (
- LayoutTimeFormat = "2006-01-02 15:04:05"
- LayoutTimeFormatMin = "2006-01-02 15:04"
- RealTimeMaxSize = 120 // 实时数据最大保存条数
- )
- type StatsInfo struct {
- RealtimeData map[string]*list.List
- Actions []string
- Scopes []string
- mux sync.Mutex
- }
- type ScopeDetail struct {
- sTime time.Time
- eTime time.Time
- minutes int
- fsTime string
- feTime string
- }
- var StatsInfoIns *StatsInfo
- func init() {
- StatsInfoIns = &StatsInfo{
- Actions: []string{"online", "network", "cpu", "mem"},
- Scopes: []string{"rt", "1h", "24h", "3d", "7d", "30d"},
- RealtimeData: make(map[string]*list.List),
- }
- for _, v := range StatsInfoIns.Actions {
- StatsInfoIns.RealtimeData[v] = list.New()
- }
- }
- // 校验统计类型值
- func (s *StatsInfo) ValidAction(action string) bool {
- for _, item := range s.Actions {
- if item == action {
- return true
- }
- }
- return false
- }
- // 校验日期范围值
- func (s *StatsInfo) ValidScope(scope string) bool {
- for _, item := range s.Scopes {
- if item == scope {
- return true
- }
- }
- return false
- }
- // 设置实时统计数据
- func (s *StatsInfo) SetRealTime(action string, val interface{}) {
- s.mux.Lock()
- defer s.mux.Unlock()
- if s.RealtimeData[action].Len() >= RealTimeMaxSize {
- ele := s.RealtimeData[action].Front()
- s.RealtimeData[action].Remove(ele)
- }
- s.RealtimeData[action].PushBack(val)
- }
- // 获取实时统计数据
- func (s *StatsInfo) GetRealTime(action string) (res []interface{}) {
- s.mux.Lock()
- defer s.mux.Unlock()
- for e := s.RealtimeData[action].Front(); e != nil; e = e.Next() {
- res = append(res, e.Value)
- }
- return
- }
- // 保存数据至数据库
- func (s *StatsInfo) SaveStatsInfo(so StatsOnline, sn StatsNetwork, sc StatsCpu, sm StatsMem) {
- if so.Num != 0 {
- _ = Add(so)
- }
- if sn.Up != 0 || sn.Down != 0 {
- _ = Add(sn)
- }
- if sc.Percent != 0 {
- _ = Add(sc)
- }
- if sm.Percent != 0 {
- _ = Add(sm)
- }
- }
- // 获取统计数据
- func (s *StatsInfo) GetData(action string, scope string) (res []interface{}, err error) {
- if scope == "rt" {
- return s.GetRealTime(action), nil
- }
- statsMaps := make(map[string]interface{})
- currSec := fmt.Sprintf("%02d", time.Now().Second())
- // 获取时间段数据
- sd := s.getScopeDetail(scope)
- timeList := s.getTimeList(sd)
- res = make([]interface{}, len(timeList))
- // 获取数据库查询条件
- where := s.getStatsWhere(sd)
- if where == "" {
- return nil, errors.New("不支持的数据库类型: " + base.Cfg.DbType)
- }
- // 查询数据表
- switch action {
- case "online":
- statsRes := []StatsOnline{}
- FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
- for _, v := range statsRes {
- t := v.CreatedAt.Format(LayoutTimeFormatMin)
- statsMaps[t] = v
- }
- case "network":
- statsRes := []StatsNetwork{}
- FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
- for _, v := range statsRes {
- t := v.CreatedAt.Format(LayoutTimeFormatMin)
- statsMaps[t] = v
- }
- case "cpu":
- statsRes := []StatsCpu{}
- FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
- for _, v := range statsRes {
- t := v.CreatedAt.Format(LayoutTimeFormatMin)
- statsMaps[t] = v
- }
- case "mem":
- statsRes := []StatsMem{}
- FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
- for _, v := range statsRes {
- t := v.CreatedAt.Format(LayoutTimeFormatMin)
- statsMaps[t] = v
- }
- }
- // 整合数据
- for i, v := range timeList {
- if mv, ok := statsMaps[v]; ok {
- res[i] = mv
- continue
- }
- t, _ := time.ParseInLocation(LayoutTimeFormat, v+":"+currSec, time.Local)
- switch action {
- case "online":
- res[i] = StatsOnline{CreatedAt: t}
- case "network":
- res[i] = StatsNetwork{CreatedAt: t}
- case "cpu":
- res[i] = StatsCpu{CreatedAt: t}
- case "mem":
- res[i] = StatsMem{CreatedAt: t}
- }
- }
- return
- }
- // 获取日期范围的明细值
- func (s *StatsInfo) getScopeDetail(scope string) (sd *ScopeDetail) {
- sd = &ScopeDetail{}
- t := time.Now()
- sd.eTime = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 59, t.Nanosecond(), time.Local)
- sd.minutes = 0
- switch scope {
- case "1h":
- sd.sTime = sd.eTime.Add(-time.Minute * 60)
- sd.minutes = 1
- case "24h":
- sd.sTime = sd.eTime.AddDate(0, 0, -1)
- sd.minutes = 5
- case "7d":
- sd.sTime = sd.eTime.AddDate(0, 0, -7)
- sd.minutes = 30
- case "30d":
- sd.sTime = sd.eTime.AddDate(0, 0, -30)
- sd.minutes = 150
- }
- if sd.minutes != 0 {
- sd.sTime = sd.sTime.Add(-time.Minute * time.Duration(sd.minutes))
- }
- sd.fsTime = sd.sTime.Format(LayoutTimeFormat)
- sd.feTime = sd.eTime.Format(LayoutTimeFormat)
- return
- }
- // 针对日期范围进行拆解
- func (s *StatsInfo) getTimeList(sd *ScopeDetail) []string {
- subSec := int64(60 * sd.minutes)
- count := (sd.eTime.Unix()-sd.sTime.Unix())/subSec - 1
- eTime := sd.eTime.Unix() - subSec
- timeLists := make([]string, count)
- for i := count - 1; i >= 0; i-- {
- timeLists[i] = time.Unix(eTime, 0).Format(LayoutTimeFormatMin)
- eTime = eTime - subSec
- }
- return timeLists
- }
- // 获取where条件
- func (s *StatsInfo) getStatsWhere(sd *ScopeDetail) (where string) {
- where = "created_at BETWEEN ? AND ?"
- min := strconv.Itoa(sd.minutes)
- switch base.Cfg.DbType {
- case "mysql":
- where += " AND floor(TIMESTAMPDIFF(SECOND, created_at, '" + sd.feTime + "') / 60) % " + min + " = 0"
- case "sqlite3":
- where += " AND CAST(ROUND((JULIANDAY('" + sd.feTime + "') - JULIANDAY(created_at)) * 86400) / 60 as integer) % " + min + " = 0"
- case "postgres":
- where += " AND floor((EXTRACT(EPOCH FROM " + sd.feTime + ") - EXTRACT(EPOCH FROM created_at)) / 60) % " + min + " = 0"
- default:
- where = ""
- }
- return
- }
- func (s *StatsInfo) ClearStatsInfo(action string, ts string) (affected int64, err error) {
- switch action {
- case "online":
- affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsOnline{})
- case "network":
- affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsNetwork{})
- case "cpu":
- affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsCpu{})
- case "mem":
- affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsMem{})
- }
- return affected, err
- }
|