匿名访客会话池,支持最小负载获取与失败替换。
| 185 | |
| 186 | |
| 187 | class GuestSessionPool: |
| 188 | """匿名访客会话池,支持最小负载获取与失败替换。""" |
| 189 | |
| 190 | def __init__(self, pool_size: int = 3): |
| 191 | self.pool_size = max(1, pool_size) |
| 192 | self._lock = Lock() |
| 193 | self._sessions: Dict[str, GuestSession] = {} |
| 194 | self._maintenance_task: Optional[asyncio.Task] = None |
| 195 | self._http_client: Optional[httpx.AsyncClient] = None |
| 196 | self._client_lock = asyncio.Lock() |
| 197 | self._capacity_lock = asyncio.Lock() |
| 198 | self._background_tasks: Set[asyncio.Task] = set() |
| 199 | self._cleanup_parallelism = GUEST_CLEANUP_PARALLELISM |
| 200 | self._maintenance_interval = GUEST_POOL_MAINTENANCE_INTERVAL_SECONDS |
| 201 | |
| 202 | async def _get_http_client(self) -> httpx.AsyncClient: |
| 203 | """获取可复用的 HTTP 客户端,减少频繁建连开销。""" |
| 204 | if self._http_client is not None: |
| 205 | return self._http_client |
| 206 | |
| 207 | async with self._client_lock: |
| 208 | if self._http_client is None: |
| 209 | self._http_client = _build_async_client() |
| 210 | return self._http_client |
| 211 | |
| 212 | async def _close_http_client(self): |
| 213 | """关闭可复用的 HTTP 客户端。""" |
| 214 | async with self._client_lock: |
| 215 | client = self._http_client |
| 216 | self._http_client = None |
| 217 | |
| 218 | if client is not None: |
| 219 | await client.aclose() |
| 220 | |
| 221 | def _track_background_task(self, coro) -> asyncio.Task: |
| 222 | """跟踪后台任务,避免清理阻塞前台重试路径。""" |
| 223 | task = asyncio.create_task(coro) |
| 224 | self._background_tasks.add(task) |
| 225 | |
| 226 | def _on_done(done_task: asyncio.Task): |
| 227 | self._background_tasks.discard(done_task) |
| 228 | try: |
| 229 | done_task.result() |
| 230 | except asyncio.CancelledError: |
| 231 | pass |
| 232 | except Exception as exc: |
| 233 | logger.warning(f"⚠️ 匿名会话后台任务异常: {exc}") |
| 234 | |
| 235 | task.add_done_callback(_on_done) |
| 236 | return task |
| 237 | |
| 238 | async def _wait_background_tasks(self): |
| 239 | """等待当前已注册的后台任务结束。""" |
| 240 | pending = list(self._background_tasks) |
| 241 | if pending: |
| 242 | await asyncio.gather(*pending, return_exceptions=True) |
| 243 | |
| 244 | async def _delete_sessions_concurrently(self, sessions: List[GuestSession]): |
no outgoing calls