修复并发账号分配到同一个代理的问题
问题:get_proxy每次返回池中第一个代理,导致并发账号用同一个代理, 容易触发斗鱼/极验风控。 修复: - 增加_in_use集合,get_proxy取代理时标记为使用中,避免重复分配 - 代理池空但所有代理都在使用中时,允许复用(兜底) - 新增release_proxy方法,登录完成后归还代理到池 - login.py的finally块中调用release_proxy,确保成功/失败都归还 - mark_bad同时从_in_use移除
This commit is contained in:
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user