binding_manager.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import threading
  2. import json
  3. import os
  4. from typing import List, Optional, Tuple, Dict, Any
  5. from vs_log_macros import VSC_DEBUG, VSC_INFO, VSC_WARN, VSC_ERROR # type: ignore
  6. class BindingManager:
  7. """
  8. 绑定管理器 (支持从配置文件加载静态绑定 + 运行时动态绑定)
  9. 读取 config/bindings.json
  10. """
  11. _instance = None
  12. _lock = threading.RLock()
  13. def __new__(cls):
  14. with cls._lock:
  15. if cls._instance is None:
  16. cls._instance = super().__new__(cls)
  17. cls._instance._init_data()
  18. return cls._instance
  19. @staticmethod
  20. def Instance():
  21. return BindingManager()
  22. def _init_data(self):
  23. # Key: (account_pool, account_id)
  24. # Value: (proxy_pool, proxy_id, bind_type)
  25. self._bindings: Dict[Tuple[str, int], Tuple[str, int, str]] = {}
  26. self._binding_lock = threading.RLock()
  27. self._config_path = "config/bindings.json"
  28. self.reload_config()
  29. def reload_config(self):
  30. """加载本地绑定配置 (通常用于静态绑定)"""
  31. if not os.path.exists(self._config_path):
  32. # 绑定文件不存在是正常的,可能全是动态绑定
  33. VSC_DEBUG("binding_mgr", f"Config file not found: {self._config_path}. No static bindings loaded.")
  34. return
  35. try:
  36. with open(self._config_path, 'r', encoding='utf-8') as f:
  37. data = json.load(f)
  38. if not isinstance(data, list):
  39. VSC_ERROR("binding_mgr", "Invalid JSON format: bindings must be a list")
  40. return
  41. count = 0
  42. with self._binding_lock:
  43. # 策略:不清除运行时生成的动态绑定,仅覆盖/添加配置文件中的静态绑定
  44. # 如果需要重置,可以先 self._bindings.clear()
  45. for item in data:
  46. # 校验字段
  47. if not all(k in item for k in ("account_pool", "account_id", "proxy_pool", "proxy_id")):
  48. continue
  49. key = (item["account_pool"], item["account_id"])
  50. val = (item["proxy_pool"], item["proxy_id"], item.get("bind_type", "static"))
  51. self._bindings[key] = val
  52. count += 1
  53. VSC_INFO("binding_mgr", f"Loaded {count} static bindings from {self._config_path}")
  54. except json.JSONDecodeError:
  55. VSC_ERROR("binding_mgr", f"Invalid JSON format in {self._config_path}")
  56. except Exception as e:
  57. VSC_ERROR("binding_mgr", f"Failed to load bindings: {e}")
  58. def get_bounded_proxy_id(self, account_pool: str, account_id: int) -> Optional[int]:
  59. """获取给定账户绑定的代理ID"""
  60. with self._binding_lock:
  61. key = (account_pool, account_id)
  62. binding = self._bindings.get(key)
  63. if binding:
  64. proxy_pool, proxy_id, b_type = binding
  65. VSC_DEBUG("binding_mgr", "Found %s binding: Acc(%s:%d) -> Proxy(%s:%d)",
  66. b_type, account_pool, account_id, proxy_pool, proxy_id)
  67. return proxy_id
  68. VSC_DEBUG("binding_mgr", "No binding found for Acc(%s:%d)", account_pool, account_id)
  69. return None
  70. def get_bounded_proxies_ids(self, account_pool: str, proxy_pool: str) -> List[int]:
  71. """
  72. 获取所有在特定代理池中被账户绑定的代理ID列表。
  73. 用于 ProxyManager 过滤掉已被占用的代理。
  74. """
  75. with self._binding_lock:
  76. bounded_ids = []
  77. for (acc_p, _), (px_p, px_id, _) in self._bindings.items():
  78. if acc_p == account_pool and px_p == proxy_pool:
  79. bounded_ids.append(px_id)
  80. # VSC_DEBUG("binding_mgr", "Bounded IDs in %s (for %s): %s", proxy_pool, account_pool, bounded_ids)
  81. return bounded_ids
  82. def create_binding(self, account_pool: str, account_id: int,
  83. proxy_pool: str, proxy_id: int, bind_type: str):
  84. """
  85. 创建绑定 (运行时调用,如动态绑定)
  86. 注意:运行时创建的绑定目前仅存在内存中,重启后丢失(除非写入文件,当前版本暂不实现回写)
  87. """
  88. with self._binding_lock:
  89. key = (account_pool, account_id)
  90. self._bindings[key] = (proxy_pool, proxy_id, bind_type)
  91. VSC_INFO("binding_mgr", "Created binding: Acc(%s:%d) -> Proxy(%s:%d) [%s]",
  92. account_pool, account_id, proxy_pool, proxy_id, bind_type)
  93. def remove_binding(self, account_pool: str, account_id: int):
  94. """移除绑定"""
  95. with self._binding_lock:
  96. key = (account_pool, account_id)
  97. if key in self._bindings:
  98. del self._bindings[key]
  99. VSC_INFO("binding_mgr", "Removed binding for Acc(%s:%d)", account_pool, account_id)