subscription_maintenance_queue.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package service
  2. import (
  3. "fmt"
  4. "log"
  5. "sync"
  6. )
  7. // SubscriptionMaintenanceQueue 提供"有界队列 + 固定 worker"的后台执行器。
  8. // 用于从请求热路径触发维护动作时,避免无限 goroutine 膨胀。
  9. type SubscriptionMaintenanceQueue struct {
  10. queue chan func()
  11. wg sync.WaitGroup
  12. stop sync.Once
  13. mu sync.RWMutex // 保护 closed 标志与 channel 操作的原子性
  14. closed bool
  15. }
  16. func NewSubscriptionMaintenanceQueue(workerCount, queueSize int) *SubscriptionMaintenanceQueue {
  17. if workerCount <= 0 {
  18. workerCount = 1
  19. }
  20. if queueSize <= 0 {
  21. queueSize = 1
  22. }
  23. q := &SubscriptionMaintenanceQueue{
  24. queue: make(chan func(), queueSize),
  25. }
  26. q.wg.Add(workerCount)
  27. for i := 0; i < workerCount; i++ {
  28. go func(workerID int) {
  29. defer q.wg.Done()
  30. for fn := range q.queue {
  31. func() {
  32. defer func() {
  33. if r := recover(); r != nil {
  34. log.Printf("SubscriptionMaintenance worker panic: %v", r)
  35. }
  36. }()
  37. fn()
  38. }()
  39. }
  40. }(i)
  41. }
  42. return q
  43. }
  44. // TryEnqueue 尝试将任务入队。
  45. // 当队列已满时返回 error(调用方应该选择跳过并记录告警/限频日志)。
  46. // 当队列已关闭时返回 error,不会 panic。
  47. func (q *SubscriptionMaintenanceQueue) TryEnqueue(task func()) error {
  48. if q == nil {
  49. return fmt.Errorf("maintenance queue is nil")
  50. }
  51. if task == nil {
  52. return fmt.Errorf("maintenance task is nil")
  53. }
  54. q.mu.RLock()
  55. defer q.mu.RUnlock()
  56. if q.closed {
  57. return fmt.Errorf("maintenance queue stopped")
  58. }
  59. select {
  60. case q.queue <- task:
  61. return nil
  62. default:
  63. return fmt.Errorf("maintenance queue full")
  64. }
  65. }
  66. func (q *SubscriptionMaintenanceQueue) Stop() {
  67. if q == nil {
  68. return
  69. }
  70. q.stop.Do(func() {
  71. q.mu.Lock()
  72. q.closed = true
  73. close(q.queue)
  74. q.mu.Unlock()
  75. q.wg.Wait()
  76. })
  77. }