simCardTaskQueue.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package simCardQueue
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "fwh-toolbox-server/domain"
  7. "fwh-toolbox-server/repositories"
  8. task_core "fwh-toolbox-server/task/core"
  9. task_pg "fwh-toolbox-server/task/plugin"
  10. "time"
  11. "github.com/google/uuid"
  12. "github.com/hibiken/asynq"
  13. "go.uber.org/zap"
  14. )
  15. /***********************************************************************
  16. * 为了控制流量采用单线程队列查询
  17. * SyncOnePage Task * 1 查询单页数据,并创建数据聚合任务
  18. * |
  19. * ▼
  20. * SyncOnePage Task * 2 查询单页数据,并创建数据聚合任务
  21. * ...
  22. * ▼
  23. * SyncOnePage Task * N 查询单页数据,并创建数据聚合任务
  24. * |
  25. * ▼
  26. * Aggregation (Group Aggregator) 聚合数据并创建,批量数据处理任务
  27. * |
  28. * ▼
  29. * Bulk Task 将聚合的数据批量写入数据库
  30. ***********************************************************************/
  31. const (
  32. TaskTypeSimCardAggregation = "SimCard:Aggregation"
  33. GroupNameSimCardAggregation = "SimCard:GroupAggregation"
  34. TaskTypeSimCardBulk = "SimCard:Bulk"
  35. )
  36. type TaskCore interface {
  37. CreateTask() *asynq.Client
  38. }
  39. type SimCardSyncOnePagePayload struct {
  40. Size uint64 `json:"size"`
  41. Page uint64 `json:"page"`
  42. }
  43. type SimCardProcessor struct {
  44. // ... fields for struct
  45. infoCacheMap map[string][]domain.SimCardInfo
  46. }
  47. var TaskClient *asynq.Client
  48. func InitSimCardTaskQueue(tc *task_core.TaskCore) {
  49. processor := &SimCardProcessor{
  50. infoCacheMap: make(map[string][]domain.SimCardInfo),
  51. }
  52. // 注册SmartHub收集任务处理函数
  53. mux := asynq.NewServeMux()
  54. mux.HandleFunc(TaskTypeSimCardSmartHubSyncOnePage, processor.handleSimCardSmartHubSyncOnePageTask)
  55. // 注册Acceleronix收集任务处理函数
  56. mux.HandleFunc(TaskTypeSimCardAcceleronixSyncOnePage, processor.handleSimCardAcceleronixSyncOnePageTask)
  57. // 注册SimCard批处理任务
  58. mux.HandleFunc(TaskTypeSimCardBulk, processor.handleSimCardBulkTask)
  59. // 注册SimCard组聚合器
  60. aggManager := task_pg.NewAggregatorManager()
  61. err := aggManager.Register(GroupNameSimCardAggregation, func(tasks []*asynq.Task) *asynq.Task {
  62. var aggInfoList []domain.SimCardInfo
  63. for _, t := range tasks {
  64. payloadStr := t.Payload()
  65. infoList := processor.infoCacheMap[string(payloadStr)]
  66. defer delete(processor.infoCacheMap, string(payloadStr))
  67. aggInfoList = append(aggInfoList, infoList...)
  68. }
  69. uuidStr := uuid.NewString()
  70. processor.infoCacheMap[uuidStr] = aggInfoList
  71. zap.L().Debug("SimCardBulk get aggInfoList.", zap.Int("len", len(aggInfoList)))
  72. bulkTask := asynq.NewTask(
  73. TaskTypeSimCardBulk,
  74. []byte(uuidStr),
  75. asynq.MaxRetry(5),
  76. asynq.Queue("default"),
  77. asynq.Timeout(time.Minute),
  78. asynq.Unique(5*time.Minute),
  79. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  80. )
  81. return bulkTask
  82. })
  83. if err != nil {
  84. zap.L().Error("GroupNameSimCardAggregation register failed.", zap.String("err", err.Error()))
  85. }
  86. TaskClient = tc.CreateTask(task_core.TaskConfig{
  87. Name: "SimCardCollectTask",
  88. SvrCfg: asynq.Config{
  89. // Specify how many concurrent workers to use
  90. Concurrency: 1, // 串行任务,只配置1个worker
  91. // Optionally specify multiple queues with different priority.
  92. Queues: map[string]int{
  93. "critical": 6,
  94. "default": 3,
  95. "low": 1,
  96. },
  97. // See the godoc for other configuration options
  98. GroupAggregator: asynq.GroupAggregatorFunc(aggManager.Aggregate),
  99. GroupGracePeriod: 30 * time.Second, // 宽限期,30秒没人提交开始聚合
  100. GroupMaxDelay: 5 * time.Minute, // 最长等待时间,5分钟必须聚合
  101. GroupMaxSize: 100, // 最大数量,数据总量到达100必须聚合
  102. },
  103. Mux: mux,
  104. Agg: aggManager,
  105. })
  106. err = CreateSchedulerSimCardCollect(tc)
  107. if err != nil {
  108. zap.L().Error("CreateSchedulerSimCardCollect failed.", zap.String("err", err.Error()))
  109. }
  110. }
  111. func CreateSchedulerSimCardCollect(tc *task_core.TaskCore) error {
  112. payload := SimCardSyncOnePagePayload{
  113. Size: SmartHubSimCardInfoPageSize,
  114. Page: 0, // SmartHub从第0页开始
  115. }
  116. payloadStr, err := json.Marshal(payload)
  117. if err != nil {
  118. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  119. return err
  120. }
  121. // 创建SmartHub收集任务
  122. taskSmartHub := asynq.NewTask(
  123. TaskTypeSimCardSmartHubSyncOnePage,
  124. payloadStr,
  125. asynq.MaxRetry(5),
  126. asynq.Queue("default"),
  127. asynq.Timeout(time.Minute),
  128. asynq.Unique(5*time.Minute),
  129. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  130. )
  131. err = tc.Scheduler.RegisterUtcScheduler("SmartHubSyncOnePageScheduler", "0 23 * * *", taskSmartHub)
  132. if err != nil {
  133. zap.L().Error("TaskTypeSimCardSmartHubSyncOnePage RegisterUtcScheduler failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  134. return err
  135. }
  136. payload.Page = 1 // Acceleronix从第1页开始
  137. payloadStr, err = json.Marshal(payload)
  138. if err != nil {
  139. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  140. return err
  141. }
  142. // 创建Acceleronix收集任务
  143. taskAcceleronix := asynq.NewTask(
  144. TaskTypeSimCardAcceleronixSyncOnePage,
  145. payloadStr,
  146. asynq.MaxRetry(5),
  147. asynq.Queue("default"),
  148. asynq.Timeout(30*time.Minute),
  149. asynq.Unique(5*time.Minute),
  150. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  151. )
  152. err = tc.Scheduler.RegisterUtcScheduler("AcceleronixSyncOnePageScheduler", "0 23 * * *", taskAcceleronix)
  153. if err != nil {
  154. zap.L().Error("TaskTypeSimCardAcceleronixSyncOnePage RegisterUtcScheduler failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  155. return err
  156. }
  157. return nil
  158. }
  159. // 创建SimCard信息收集任务
  160. func CreateTaskSimCardCollect() error {
  161. if TaskClient == nil {
  162. return fmt.Errorf("*asynq.Client Uninitialized.")
  163. }
  164. payload := SimCardSyncOnePagePayload{
  165. Size: SmartHubSimCardInfoPageSize,
  166. Page: 0, // SmartHub从第0页开始
  167. }
  168. payloadStr, err := json.Marshal(payload)
  169. if err != nil {
  170. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  171. return err
  172. }
  173. // 创建SmartHub收集任务
  174. taskSmartHub := asynq.NewTask(
  175. TaskTypeSimCardSmartHubSyncOnePage,
  176. payloadStr,
  177. asynq.MaxRetry(5),
  178. asynq.Queue("default"),
  179. asynq.Timeout(time.Minute),
  180. asynq.Unique(5*time.Minute),
  181. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  182. )
  183. _, err = TaskClient.Enqueue(taskSmartHub)
  184. if err != nil {
  185. zap.L().Error("TaskTypeSimCardSmartHubSyncOnePage Enqueue failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  186. return err
  187. }
  188. payload.Page = 1 // Acceleronix从第1页开始
  189. payloadStr, err = json.Marshal(payload)
  190. if err != nil {
  191. zap.L().Error("SimCardSyncOnePagePayload Marshal failed.", zap.String("err", err.Error()))
  192. return err
  193. }
  194. // 创建Acceleronix收集任务
  195. taskAcceleronix := asynq.NewTask(
  196. TaskTypeSimCardAcceleronixSyncOnePage,
  197. payloadStr,
  198. asynq.MaxRetry(5),
  199. asynq.Queue("default"),
  200. asynq.Timeout(30*time.Minute),
  201. asynq.Unique(5*time.Minute),
  202. asynq.Retention(7*24*time.Hour), // 成功后保留7天
  203. )
  204. _, err = TaskClient.Enqueue(taskAcceleronix)
  205. if err != nil {
  206. zap.L().Error("TaskTypeSimCardAcceleronixSyncOnePage Enqueue failed.", zap.String("err", err.Error()), zap.Uint64("size", payload.Size), zap.Uint64("page", payload.Page))
  207. return err
  208. }
  209. return nil
  210. }
  211. // 保存SimCard信息到数据库
  212. func (processor *SimCardProcessor) saveSimCardInfo(infoList []domain.SimCardInfo) error {
  213. zap.L().Info("saveSimCardInfo", zap.Int("Num", len(infoList)))
  214. // 保存基本信息
  215. _, err := repositories.UpdateSimCardBatchInfo(infoList)
  216. if err != nil {
  217. zap.L().Error("UpdateSimCardInfo failed.", zap.String("err", err.Error()), zap.Int("infoNum", len(infoList)))
  218. return err
  219. }
  220. // 提取日数据
  221. dayDataList := []domain.SimCardDayData{}
  222. for _, info := range infoList {
  223. dayData := domain.SimCardDayData{
  224. SimId: info.GICCID, // GICCID
  225. UpdateTime: info.UpdateTime, // UTC时间
  226. Month: info.Month, // 统计月份 格式:yyyy-MM
  227. AllDataUsage: info.AllDataUsage, // 月数据用量(KB)
  228. Day: uint8(time.Now().UTC().Day()), // 当天日期值 1-31
  229. DailyUsage: domain.SimCardDailyUsage{
  230. UpdateTime: info.UpdateTime, // UTC时间
  231. Day: time.Now().UTC().Format(time.DateOnly), // 统计日期 格式:yyyy-MM-DD
  232. DataUsage: info.AllDataUsage, // 数据用量(KB)
  233. }, // 日用量数据信息
  234. }
  235. dayDataList = append(dayDataList, dayData)
  236. }
  237. // 保存月数据信息
  238. _, err = repositories.UpdateSimCardBatchDayData(dayDataList)
  239. if err != nil {
  240. zap.L().Error("UpdateSimCardBatchMonthlyData failed.", zap.String("err", err.Error()), zap.Int("infoNum", len(infoList)))
  241. return err
  242. }
  243. return nil
  244. }
  245. // 处理SimCard批处理任务
  246. func (processor *SimCardProcessor) handleSimCardBulkTask(ctx context.Context, task *asynq.Task) error {
  247. payloadStr := task.Payload()
  248. originalList := processor.infoCacheMap[string(payloadStr)]
  249. defer delete(processor.infoCacheMap, string(payloadStr))
  250. // infoList := make([]domain.SimCardInfo, len(originalList))
  251. // copy(infoList, originalList) // 这里使用拷贝,防止引用被删除
  252. infoList := originalList
  253. return processor.saveSimCardInfo(infoList)
  254. }