statsinfo.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package dbdata
  2. import (
  3. "container/list"
  4. "errors"
  5. "fmt"
  6. "strconv"
  7. "sync"
  8. "time"
  9. "github.com/bjdgyc/anylink/base"
  10. )
  11. const (
  12. LayoutTimeFormat = "2006-01-02 15:04:05"
  13. LayoutTimeFormatMin = "2006-01-02 15:04"
  14. RealTimeMaxSize = 120 // 实时数据最大保存条数
  15. )
  16. type StatsInfo struct {
  17. RealtimeData map[string]*list.List
  18. Actions []string
  19. Scopes []string
  20. mux sync.Mutex
  21. }
  22. type ScopeDetail struct {
  23. sTime time.Time
  24. eTime time.Time
  25. minutes int
  26. fsTime string
  27. feTime string
  28. }
  29. var StatsInfoIns *StatsInfo
  30. func init() {
  31. StatsInfoIns = &StatsInfo{
  32. Actions: []string{"online", "network", "cpu", "mem"},
  33. Scopes: []string{"rt", "1h", "24h", "3d", "7d", "30d"},
  34. RealtimeData: make(map[string]*list.List),
  35. }
  36. for _, v := range StatsInfoIns.Actions {
  37. StatsInfoIns.RealtimeData[v] = list.New()
  38. }
  39. }
  40. // 校验统计类型值
  41. func (s *StatsInfo) ValidAction(action string) bool {
  42. for _, item := range s.Actions {
  43. if item == action {
  44. return true
  45. }
  46. }
  47. return false
  48. }
  49. // 校验日期范围值
  50. func (s *StatsInfo) ValidScope(scope string) bool {
  51. for _, item := range s.Scopes {
  52. if item == scope {
  53. return true
  54. }
  55. }
  56. return false
  57. }
  58. // 设置实时统计数据
  59. func (s *StatsInfo) SetRealTime(action string, val interface{}) {
  60. s.mux.Lock()
  61. defer s.mux.Unlock()
  62. if s.RealtimeData[action].Len() >= RealTimeMaxSize {
  63. ele := s.RealtimeData[action].Front()
  64. s.RealtimeData[action].Remove(ele)
  65. }
  66. s.RealtimeData[action].PushBack(val)
  67. }
  68. // 获取实时统计数据
  69. func (s *StatsInfo) GetRealTime(action string) (res []interface{}) {
  70. s.mux.Lock()
  71. defer s.mux.Unlock()
  72. for e := s.RealtimeData[action].Front(); e != nil; e = e.Next() {
  73. res = append(res, e.Value)
  74. }
  75. return
  76. }
  77. // 保存数据至数据库
  78. func (s *StatsInfo) SaveStatsInfo(so StatsOnline, sn StatsNetwork, sc StatsCpu, sm StatsMem) {
  79. if so.Num != 0 {
  80. _ = Add(so)
  81. }
  82. if sn.Up != 0 || sn.Down != 0 {
  83. _ = Add(sn)
  84. }
  85. if sc.Percent != 0 {
  86. _ = Add(sc)
  87. }
  88. if sm.Percent != 0 {
  89. _ = Add(sm)
  90. }
  91. }
  92. // 获取统计数据
  93. func (s *StatsInfo) GetData(action string, scope string) (res []interface{}, err error) {
  94. if scope == "rt" {
  95. return s.GetRealTime(action), nil
  96. }
  97. statsMaps := make(map[string]interface{})
  98. currSec := fmt.Sprintf("%02d", time.Now().Second())
  99. // 获取时间段数据
  100. sd := s.getScopeDetail(scope)
  101. timeList := s.getTimeList(sd)
  102. res = make([]interface{}, len(timeList))
  103. // 获取数据库查询条件
  104. where := s.getStatsWhere(sd)
  105. if where == "" {
  106. return nil, errors.New("不支持的数据库类型: " + base.Cfg.DbType)
  107. }
  108. // 查询数据表
  109. switch action {
  110. case "online":
  111. statsRes := []StatsOnline{}
  112. FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
  113. for _, v := range statsRes {
  114. t := v.CreatedAt.Format(LayoutTimeFormatMin)
  115. statsMaps[t] = v
  116. }
  117. case "network":
  118. statsRes := []StatsNetwork{}
  119. FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
  120. for _, v := range statsRes {
  121. t := v.CreatedAt.Format(LayoutTimeFormatMin)
  122. statsMaps[t] = v
  123. }
  124. case "cpu":
  125. statsRes := []StatsCpu{}
  126. FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
  127. for _, v := range statsRes {
  128. t := v.CreatedAt.Format(LayoutTimeFormatMin)
  129. statsMaps[t] = v
  130. }
  131. case "mem":
  132. statsRes := []StatsMem{}
  133. FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
  134. for _, v := range statsRes {
  135. t := v.CreatedAt.Format(LayoutTimeFormatMin)
  136. statsMaps[t] = v
  137. }
  138. }
  139. // 整合数据
  140. for i, v := range timeList {
  141. if mv, ok := statsMaps[v]; ok {
  142. res[i] = mv
  143. continue
  144. }
  145. t, _ := time.ParseInLocation(LayoutTimeFormat, v+":"+currSec, time.Local)
  146. switch action {
  147. case "online":
  148. res[i] = StatsOnline{CreatedAt: t}
  149. case "network":
  150. res[i] = StatsNetwork{CreatedAt: t}
  151. case "cpu":
  152. res[i] = StatsCpu{CreatedAt: t}
  153. case "mem":
  154. res[i] = StatsMem{CreatedAt: t}
  155. }
  156. }
  157. return
  158. }
  159. // 获取日期范围的明细值
  160. func (s *StatsInfo) getScopeDetail(scope string) (sd *ScopeDetail) {
  161. sd = &ScopeDetail{}
  162. t := time.Now()
  163. sd.eTime = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 59, t.Nanosecond(), time.Local)
  164. sd.minutes = 0
  165. switch scope {
  166. case "1h":
  167. sd.sTime = sd.eTime.Add(-time.Minute * 60)
  168. sd.minutes = 1
  169. case "24h":
  170. sd.sTime = sd.eTime.AddDate(0, 0, -1)
  171. sd.minutes = 5
  172. case "7d":
  173. sd.sTime = sd.eTime.AddDate(0, 0, -7)
  174. sd.minutes = 30
  175. case "30d":
  176. sd.sTime = sd.eTime.AddDate(0, 0, -30)
  177. sd.minutes = 150
  178. }
  179. if sd.minutes != 0 {
  180. sd.sTime = sd.sTime.Add(-time.Minute * time.Duration(sd.minutes))
  181. }
  182. sd.fsTime = sd.sTime.Format(LayoutTimeFormat)
  183. sd.feTime = sd.eTime.Format(LayoutTimeFormat)
  184. return
  185. }
  186. // 针对日期范围进行拆解
  187. func (s *StatsInfo) getTimeList(sd *ScopeDetail) []string {
  188. subSec := int64(60 * sd.minutes)
  189. count := (sd.eTime.Unix()-sd.sTime.Unix())/subSec - 1
  190. eTime := sd.eTime.Unix() - subSec
  191. timeLists := make([]string, count)
  192. for i := count - 1; i >= 0; i-- {
  193. timeLists[i] = time.Unix(eTime, 0).Format(LayoutTimeFormatMin)
  194. eTime = eTime - subSec
  195. }
  196. return timeLists
  197. }
  198. // 获取where条件
  199. func (s *StatsInfo) getStatsWhere(sd *ScopeDetail) (where string) {
  200. where = "created_at BETWEEN ? AND ?"
  201. min := strconv.Itoa(sd.minutes)
  202. switch base.Cfg.DbType {
  203. case "mysql":
  204. where += " AND floor(TIMESTAMPDIFF(SECOND, created_at, '" + sd.feTime + "') / 60) % " + min + " = 0"
  205. case "sqlite3":
  206. where += " AND CAST(ROUND((JULIANDAY('" + sd.feTime + "') - JULIANDAY(created_at)) * 86400) / 60 as integer) % " + min + " = 0"
  207. case "postgres":
  208. where += " AND floor((EXTRACT(EPOCH FROM " + sd.feTime + ") - EXTRACT(EPOCH FROM created_at)) / 60) % " + min + " = 0"
  209. default:
  210. where = ""
  211. }
  212. return
  213. }
  214. func (s *StatsInfo) ClearStatsInfo(action string, ts string) (affected int64, err error) {
  215. switch action {
  216. case "online":
  217. affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsOnline{})
  218. case "network":
  219. affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsNetwork{})
  220. case "cpu":
  221. affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsCpu{})
  222. case "mem":
  223. affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsMem{})
  224. }
  225. return affected, err
  226. }