import datetime import asyncio import httpx from loguru import logger from utils.decorators import handle_exceptions_unified class BrowserApi: """ 浏览器接口 """ def __init__(self): self.local_url = 'http://127.0.0.1:54345' self.headers = {'Content-Type': 'application/json'} # 使用异步 HTTP 客户端,启用连接池和超时设置 self.client = httpx.AsyncClient( base_url=self.local_url, headers=self.headers, timeout=httpx.Timeout(30.0, connect=10.0), # 总超时30秒,连接超时10秒 limits=httpx.Limits(max_keepalive_connections=50, max_connections=100), # 连接池配置 ) async def __aenter__(self): """异步上下文管理器入口""" return self async def __aexit__(self, exc_type, exc_val, exc_tb): """异步上下文管理器出口,关闭客户端""" await self.aclose() async def aclose(self): """关闭 HTTP 客户端""" if self.client: await self.client.aclose() # 打开指纹浏览器 @handle_exceptions_unified() async def open_browser(self, id: str, jc: int = 0): """ 打开指纹浏览器(异步优化版本) :param jc: 计次 :param id: 浏览器id :return:http, pid """ if jc > 3: return None, None url = '/browser/open' data = { 'id': id } try: res = await self.client.post(url, json=data) res.raise_for_status() # 检查 HTTP 状态码 res_data = res.json() logger.info(f'打开指纹浏览器: {res_data}') if not res_data.get('success'): logger.error(f'打开指纹浏览器失败: {res_data}') return await self.open_browser(id, jc + 1) data = res_data.get('data') http = data.get('http') pid = data.get('pid') logger.info(f'打开指纹浏览器成功: {http}, {pid}') return http, pid except httpx.TimeoutException as e: logger.error(f'打开指纹浏览器超时: {e}') if jc < 3: return await self.open_browser(id, jc + 1) return None, None except httpx.RequestError as e: logger.error(f'打开指纹浏览器请求错误: {e}') if jc < 3: return await self.open_browser(id, jc + 1) return None, None except Exception as e: logger.error(f'打开指纹浏览器异常: {e}') if jc < 3: return await self.open_browser(id, jc + 1) return None, None # 关闭指纹浏览器 @handle_exceptions_unified() async def close_browser(self, id: str, jc: int = 0): """ 关闭指纹浏览器(异步优化版本) :param jc: 计次 :param id: 浏览器id :return: """ if jc > 3: return None url = '/browser/close' data = { 'id': id } try: res = await self.client.post(url, json=data) res.raise_for_status() # 检查 HTTP 状态码 res_data = res.json() logger.info(f'关闭指纹浏览器: {res_data}') if not res_data.get('success'): msg = res_data.get('msg', '') # 如果浏览器正在打开中,等待后重试(不是真正的错误) if '正在打开中' in msg or 'opening' in msg.lower(): if jc < 3: # 等待 1-3 秒后重试(根据重试次数递增等待时间) wait_time = (jc + 1) * 1.0 # 第1次重试等1秒,第2次等2秒,第3次等3秒 logger.info(f'浏览器正在打开中,等待 {wait_time} 秒后重试关闭: browser_id={id}') await asyncio.sleep(wait_time) return await self.close_browser(id, jc + 1) else: # 超过重试次数,记录警告但不作为错误 logger.warning(f'关闭指纹浏览器失败(浏览器正在打开中,已重试3次): browser_id={id}') return None else: # 其他错误,记录为错误并重试 logger.error(f'关闭指纹浏览器失败: {res_data}') if jc < 3: await asyncio.sleep(0.5) # 短暂等待后重试 return await self.close_browser(id, jc + 1) return None logger.info(f'关闭指纹浏览器成功: browser_id={id}') return True except httpx.TimeoutException as e: logger.error(f'关闭指纹浏览器超时: {e}') if jc < 3: await asyncio.sleep(1.0) return await self.close_browser(id, jc + 1) return None except httpx.RequestError as e: logger.error(f'关闭指纹浏览器请求错误: {e}') if jc < 3: await asyncio.sleep(1.0) return await self.close_browser(id, jc + 1) return None except Exception as e: logger.error(f'关闭指纹浏览器异常: {e}') if jc < 3: await asyncio.sleep(1.0) return await self.close_browser(id, jc + 1) return None browser_api = BrowserApi()