tls_fingerprint_profile_cache.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package repository
  2. import (
  3. "context"
  4. "encoding/json"
  5. "log/slog"
  6. "sync"
  7. "time"
  8. "github.com/Wei-Shaw/sub2api/internal/model"
  9. "github.com/Wei-Shaw/sub2api/internal/service"
  10. "github.com/redis/go-redis/v9"
  11. )
  12. const (
  13. tlsFPProfileCacheKey = "tls_fingerprint_profiles"
  14. tlsFPProfilePubSubKey = "tls_fingerprint_profiles_updated"
  15. tlsFPProfileCacheTTL = 24 * time.Hour
  16. )
  17. type tlsFingerprintProfileCache struct {
  18. rdb *redis.Client
  19. localCache []*model.TLSFingerprintProfile
  20. localMu sync.RWMutex
  21. }
  22. // NewTLSFingerprintProfileCache 创建 TLS 指纹模板缓存
  23. func NewTLSFingerprintProfileCache(rdb *redis.Client) service.TLSFingerprintProfileCache {
  24. return &tlsFingerprintProfileCache{
  25. rdb: rdb,
  26. }
  27. }
  28. // Get 从缓存获取模板列表
  29. func (c *tlsFingerprintProfileCache) Get(ctx context.Context) ([]*model.TLSFingerprintProfile, bool) {
  30. c.localMu.RLock()
  31. if c.localCache != nil {
  32. profiles := c.localCache
  33. c.localMu.RUnlock()
  34. return profiles, true
  35. }
  36. c.localMu.RUnlock()
  37. data, err := c.rdb.Get(ctx, tlsFPProfileCacheKey).Bytes()
  38. if err != nil {
  39. if err != redis.Nil {
  40. slog.Warn("tls_fp_profile_cache_get_failed", "error", err)
  41. }
  42. return nil, false
  43. }
  44. var profiles []*model.TLSFingerprintProfile
  45. if err := json.Unmarshal(data, &profiles); err != nil {
  46. slog.Warn("tls_fp_profile_cache_unmarshal_failed", "error", err)
  47. return nil, false
  48. }
  49. c.localMu.Lock()
  50. c.localCache = profiles
  51. c.localMu.Unlock()
  52. return profiles, true
  53. }
  54. // Set 设置缓存
  55. func (c *tlsFingerprintProfileCache) Set(ctx context.Context, profiles []*model.TLSFingerprintProfile) error {
  56. data, err := json.Marshal(profiles)
  57. if err != nil {
  58. return err
  59. }
  60. if err := c.rdb.Set(ctx, tlsFPProfileCacheKey, data, tlsFPProfileCacheTTL).Err(); err != nil {
  61. return err
  62. }
  63. c.localMu.Lock()
  64. c.localCache = profiles
  65. c.localMu.Unlock()
  66. return nil
  67. }
  68. // Invalidate 使缓存失效
  69. func (c *tlsFingerprintProfileCache) Invalidate(ctx context.Context) error {
  70. c.localMu.Lock()
  71. c.localCache = nil
  72. c.localMu.Unlock()
  73. return c.rdb.Del(ctx, tlsFPProfileCacheKey).Err()
  74. }
  75. // NotifyUpdate 通知其他实例刷新缓存
  76. func (c *tlsFingerprintProfileCache) NotifyUpdate(ctx context.Context) error {
  77. return c.rdb.Publish(ctx, tlsFPProfilePubSubKey, "refresh").Err()
  78. }
  79. // SubscribeUpdates 订阅缓存更新通知
  80. func (c *tlsFingerprintProfileCache) SubscribeUpdates(ctx context.Context, handler func()) {
  81. go func() {
  82. sub := c.rdb.Subscribe(ctx, tlsFPProfilePubSubKey)
  83. defer func() { _ = sub.Close() }()
  84. ch := sub.Channel()
  85. for {
  86. select {
  87. case <-ctx.Done():
  88. slog.Debug("tls_fp_profile_cache_subscriber_stopped", "reason", "context_done")
  89. return
  90. case msg := <-ch:
  91. if msg == nil {
  92. slog.Warn("tls_fp_profile_cache_subscriber_stopped", "reason", "channel_closed")
  93. return
  94. }
  95. c.localMu.Lock()
  96. c.localCache = nil
  97. c.localMu.Unlock()
  98. handler()
  99. }
  100. }
  101. }()
  102. }