taskQueue.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package taskqueue
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fwh-toolbox-server/cache"
  6. "fwh-toolbox-server/logger"
  7. "log"
  8. "time"
  9. "github.com/go-redis/redis/v8"
  10. )
  11. var (
  12. ctx = context.Background()
  13. rdb = cache.C
  14. streamName = "fwhtoolbox_stream"
  15. groupName = "task_queue"
  16. consumerID = "worker-taskQueue"
  17. )
  18. type TaskPayloadBase struct {
  19. Type string `json:"type"`
  20. }
  21. type TaskPayload[T any] struct {
  22. TaskPayloadBase
  23. Payload T `json:"payload"`
  24. }
  25. func InitTaskQueue() {
  26. // 确保消费组存在(忽略已存在错误)
  27. _ = rdb.XGroupCreateMkStream(ctx, streamName, groupName, "$").Err()
  28. // 启动后台任务队列(不会阻塞主线程)
  29. go startTaskQueueWorker()
  30. }
  31. // 任务消费者(单 worker,顺序执行)
  32. func startTaskQueueWorker() {
  33. for {
  34. res, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
  35. Group: groupName,
  36. Consumer: consumerID,
  37. Streams: []string{streamName, ">"},
  38. Count: 1,
  39. Block: 5 * time.Second, // 等待任务
  40. }).Result()
  41. if err != nil {
  42. if err == redis.Nil {
  43. continue
  44. }
  45. logger.Std.Println("XReadGroup error:", err)
  46. time.Sleep(time.Second)
  47. continue
  48. }
  49. for _, stream := range res {
  50. for _, message := range stream.Messages {
  51. id := message.ID
  52. rawPayload := message.Values["payload"].(string)
  53. // 先反序列化成 map 确定 type
  54. var base TaskPayloadBase
  55. if err := json.Unmarshal([]byte(rawPayload), &base); err != nil {
  56. logger.Std.Println("unmarshal base error:", err)
  57. continue
  58. }
  59. switch base.Type {
  60. case TaskTypeSiteAnalysis:
  61. var payload TaskPayload[TaskSiteAnalysis]
  62. if err := json.Unmarshal([]byte(rawPayload), &payload); err != nil {
  63. log.Println("unmarshal error:", err)
  64. continue
  65. }
  66. logger.Std.Printf("Processing task %s: %+v\n", id, payload)
  67. handleTaskSiteAnalysis(payload.Payload)
  68. default:
  69. logger.Std.Println("Unknown task type:", base.Type)
  70. }
  71. // 确认任务完成
  72. if err := rdb.XAck(ctx, streamName, groupName, id).Err(); err != nil {
  73. logger.Std.Println("XAck error:", err)
  74. }
  75. }
  76. }
  77. }
  78. }
  79. func EnqueueTask[T any](payload TaskPayload[T]) error {
  80. data, err := json.Marshal(payload)
  81. if err != nil {
  82. return err
  83. }
  84. if _, err := rdb.XAdd(ctx, &redis.XAddArgs{
  85. Stream: streamName,
  86. Values: map[string]interface{}{"payload": string(data)},
  87. }).Result(); err != nil {
  88. return err
  89. }
  90. return nil
  91. }