ForcePilot/backend/server/utils/inflight_tracker.py
Kris 12ec5af0de feat: 新增全链路追踪、优雅关停支持与统一异常处理,优化渠道查询接口
1. 新增TraceId中间件实现全链路请求追踪
2. 新增在途请求计数器与中间件支持优雅关停
3. 重构全局异常处理器,统一三模块异常响应格式
4. 优化渠道查询接口,新增多维度过滤与模糊匹配
5. 重构channels模块数据库会话管理,避免事务冲突
6. 新增目录搜索建议与目录导出功能
2026-07-07 16:25:47 +08:00

79 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""在途请求计数器与中间件,用于优雅关停时排空在途请求。
``InflightRequestTracker`` 通过 ``increment`` / ``decrement`` 维护在途请求
计数,计数归零时 ``wait_drained`` 立即返回;超时未归零则抛 ``TimeoutError``
由 ``HostShutdown._waitForInflightRequests`` 捕获并记录告警后强制进入下一步
§11.9)。
``InflightTrackingMiddleware`` 在每个 HTTP 请求进入时 ``increment``、结束时
``decrement````finally`` 保证异常路径也执行),确保计数准确。
"""
from __future__ import annotations
import asyncio
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
__all__ = ["InflightRequestTracker", "InflightTrackingMiddleware"]
class InflightRequestTracker:
"""在途请求计数器,用于优雅关停时排空在途请求。
关键不变量:
- ``_count`` 归零时 ``_zero_event`` 被 set``wait_drained`` 立即返回。
- ``decrement`` 防御性归零,避免计数变为负数。
"""
def __init__(self) -> None:
self._count = 0
self._zero_event = asyncio.Event()
self._zero_event.set() # 初始为 0已触发
@property
def count(self) -> int:
"""当前在途请求数。"""
return self._count
def increment(self) -> None:
"""在途请求计数 +1清除归零事件。"""
self._count += 1
self._zero_event.clear()
def decrement(self) -> None:
"""在途请求计数 -1归零时触发归零事件。"""
self._count -= 1
if self._count <= 0:
self._count = 0
self._zero_event.set()
async def wait_drained(self, timeout: float) -> None:
"""等待在途请求排空。
参数:
timeout: 等待超时(秒),超时后抛 ``TimeoutError``。
Raises:
TimeoutError: 超时未排空。
"""
await asyncio.wait_for(self._zero_event.wait(), timeout=timeout)
class InflightTrackingMiddleware(BaseHTTPMiddleware):
"""在途请求计数中间件。
每个请求进入时 ``increment``、结束时 ``decrement````finally`` 保证
异常路径也执行。注册时应最后注册LIFO 顺序中最先执行),确保覆盖
所有后续中间件与路由处理。
"""
async def dispatch(self, request: Request, call_next):
tracker: InflightRequestTracker = request.app.state.inflight_tracker
tracker.increment()
try:
return await call_next(request)
finally:
tracker.decrement()