修复并发账号分配到同一个代理的问题

问题:get_proxy每次返回池中第一个代理,导致并发账号用同一个代理,
容易触发斗鱼/极验风控。

修复:
- 增加_in_use集合,get_proxy取代理时标记为使用中,避免重复分配
- 代理池空但所有代理都在使用中时,允许复用(兜底)
- 新增release_proxy方法,登录完成后归还代理到池
- login.py的finally块中调用release_proxy,确保成功/失败都归还
- mark_bad同时从_in_use移除
This commit is contained in:
yml2213
2026-06-23 01:41:46 +08:00
parent c1c9d186d1
commit 2ccc2a9336
2 changed files with 22 additions and 2 deletions
+4
View File
@@ -313,6 +313,10 @@ class DouyuLogin:
except Exception as e: except Exception as e:
logger.error(f"登录失败: {e}") logger.error(f"登录失败: {e}")
return LoginResult(success=False, message=str(e)) return LoginResult(success=False, message=str(e))
finally:
# 归还代理到池,让其他账号可以复用
if self.proxy_manager and self._current_proxy_url:
self.proxy_manager.release_proxy(self._current_proxy_url)
def _first_login(self) -> Tuple[str, str, str, dict]: def _first_login(self) -> Tuple[str, str, str, dict]:
""" """
+18 -2
View File
@@ -20,17 +20,26 @@ class ProxyManager:
self._cond = threading.Condition() self._cond = threading.Condition()
# 已验证可用的代理池: {proxy_url: validated_timestamp} # 已验证可用的代理池: {proxy_url: validated_timestamp}
self._verified_pool: dict[str, float] = {} self._verified_pool: dict[str, float] = {}
# 正在使用中的代理(取走但未归还),避免并发账号用同一个代理
self._in_use: set[str] = set()
self._pool_ttl = 90 # 代理验证后90秒内可复用 self._pool_ttl = 90 # 代理验证后90秒内可复用
self._fetching = False # 是否有线程正在获取代理 self._fetching = False # 是否有线程正在获取代理
def _pick_from_pool_locked(self) -> Optional[str]: def _pick_from_pool_locked(self) -> Optional[str]:
"""从池中取一个未过期的代理(调用前需持有锁)""" """从池中取一个未过期且未在使用的代理(调用前需持有锁)"""
now = time.time() now = time.time()
# 清理过期代理 # 清理过期代理
expired = [p for p, t in self._verified_pool.items() if now - t > self._pool_ttl] expired = [p for p, t in self._verified_pool.items() if now - t > self._pool_ttl]
for p in expired: for p in expired:
del self._verified_pool[p] del self._verified_pool[p]
# 取第一个可用的 self._in_use.discard(p)
# 取一个不在使用中的
for proxy in self._verified_pool:
if proxy not in self._in_use:
self._in_use.add(proxy)
self.current_proxy = proxy
return proxy
# 所有代理都在使用中,但池非空(并发数>代理数),允许复用第一个
for proxy in self._verified_pool: for proxy in self._verified_pool:
self.current_proxy = proxy self.current_proxy = proxy
return proxy return proxy
@@ -151,12 +160,19 @@ class ProxyManager:
"""标记代理为不可用,从池中移除(极验失败/代理连接失败时调用)""" """标记代理为不可用,从池中移除(极验失败/代理连接失败时调用)"""
with self._cond: with self._cond:
removed = self._verified_pool.pop(proxy_url, None) removed = self._verified_pool.pop(proxy_url, None)
self._in_use.discard(proxy_url)
if self.current_proxy == proxy_url: if self.current_proxy == proxy_url:
self.current_proxy = None self.current_proxy = None
if removed: if removed:
logger.info(f"代理标记为不可用并移出池: {proxy_url} (池剩余 {len(self._verified_pool)})") logger.info(f"代理标记为不可用并移出池: {proxy_url} (池剩余 {len(self._verified_pool)})")
self._cond.notify_all() self._cond.notify_all()
def release_proxy(self, proxy_url: str) -> None:
"""归还代理到池(登录完成后调用,让其他账号可以复用)"""
with self._cond:
self._in_use.discard(proxy_url)
self._cond.notify_all()
def get_proxies_dict(self, proxy: str = None) -> dict: def get_proxies_dict(self, proxy: str = None) -> dict:
"""获取requests使用的proxies字典""" """获取requests使用的proxies字典"""
proxy = proxy or self.current_proxy proxy = proxy or self.current_proxy