硅基流动API中转服务实战:如何用FastAPI添加监控和缓存功能
在构建面向生产环境的AI应用时,一个稳定、高效且可观测的API服务层往往是决定项目成败的关键。许多开发者在使用硅基流动这类大模型服务时,会直接调用其官方SDK,这在原型验证阶段无可厚非。然而,当应用规模扩大,面临复杂的流量管理、成本控制、性能优化和故障排查需求时,一个简单的客户端调用就显得捉襟见肘了。这时,一个自定义的API中转服务就成了架构中的“瑞士军刀”。
这个中转服务不仅仅是请求的“二传手”。它的核心价值在于,在你和上游AI服务商之间,建立了一个完全可控的中间层。你可以在这里植入业务逻辑,比如统一的鉴权、请求的审计与计费、响应内容的过滤与格式化。更重要的是,你可以深度集成监控与缓存这两大生产级系统的支柱功能。监控让你对服务的健康状态、性能瓶颈和异常情况了如指掌;缓存则能直接降低延迟、节省成本,并提升系统在高并发下的韧性。
本文面向的是已经熟悉FastAPI基础,并希望将服务推向更高可用性、可维护性阶段的中级开发者。我们将抛开简单的代理转发,聚焦于如何为你的硅基流动API中转服务,系统地注入监控与缓存能力。我会分享从日志结构化、指标收集到多级缓存策略的实战代码与设计思路,这些方案都经过真实项目的检验,你可以直接借鉴并应用到自己的系统中。
1. 构建可观测的监控体系:从日志到指标
一个没有监控的服务,就像在黑夜中驾驶没有仪表的汽车。对于API中转服务,监控的首要目标是清晰地回答:谁、在什么时候、调用了什么、结果如何、花了多长时间。我们将从基础的日志增强开始,逐步构建一个完整的监控仪表盘。
1.1 结构化日志与请求追踪
FastAPI默认的访问日志信息有限。我们需要一个中间件来捕获每个请求的完整上下文。关键在于为每个请求生成一个唯一的追踪ID(request_id),并将其贯穿整个请求生命周期,包括对上游硅基流动服务的调用。
首先,安装必要的依赖:
pip install python-json-logger httpx
接下来,我们创建一个增强的日志配置和追踪中间件。在 app.py 或新建的 middleware.py 中:
import logging
import uuid
import time
from contextvars import ContextVar
from typing import Callable
import json_log_formatter
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
# 创建用于存储请求上下文的ContextVar
_request_id_ctx_var: ContextVar[str] = ContextVar("request_id", default="")
def get_request_id() -> str:
"""获取当前请求的ID"""
return _request_id_ctx_var.get()
class StructuredFormatter(json_log_formatter.JSONFormatter):
"""JSON结构化日志格式化器"""
def json_record(self, message: str, extra: dict, record: logging.LogRecord):
extra["message"] = message
extra["level"] = record.levelname
extra["module"] = record.module
extra["funcName"] = record.funcName
# 注入请求ID
request_id = get_request_id()
if request_id:
extra["request_id"] = request_id
# 添加时间戳
extra["timestamp"] = record.created
return extra
# 配置日志
def setup_logging():
formatter = StructuredFormatter()
json_handler = logging.StreamHandler()
json_handler.setFormatter(formatter)
logger = logging.getLogger("api_proxy")
logger.setLevel(logging.INFO)
logger.addHandler(json_handler)
# 避免uvicorn等库的日志干扰
logging.getLogger("uvicorn.access").handlers = []
logging.getLogger("uvicorn").handlers = []
return logger
logger = setup_logging()
class RequestLoggingMiddleware(BaseHTTPMiddleware):
"""请求日志与追踪中间件"""
async def dispatch(self, request: Request, call_next: Callable) -> Response:
# 生成或获取请求ID
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
_request_id_ctx_var.set(request_id)
# 记录请求开始
start_time = time.time()
logger.info("Request started", extra={
"method": request.method,
"url": str(request.url),
"client_host": request.client.host if request.client else None,
"user_agent": request.headers.get("user-agent"),
"request_id": request_id
})
# 处理请求
response = await call_next(request)
# 计算处理时间
process_time = time.time() - start_time
response.headers["X-Request-ID"] = request_id
response.headers["X-Process-Time"] = str(process_time)
# 记录请求完成
logger.info("Request completed", extra={
"method": request.method,
"url": str(request.url),
"status_code": response.status_code,
"process_time_sec": round(process_time, 4),
"request_id": request_id
})
return response
将这个中间件添加到你的FastAPI应用中:
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(RequestLoggingMiddleware)
现在,你的日志不再是杂乱的文本行,而是结构化的JSON对象。每一条日志都包含了 request_id,使得你能够轻松地将分散的日志条目串联成一个完整的请求故事线。这种格式非常适合被日志收集系统(如ELK Stack、Loki)摄取和分析。
1.2 关键性能指标(KPM)收集
除了日志,我们还需要量化的指标来评估系统健康度。我们将使用 prometheus-client 来暴露符合Prometheus格式的指标。这些指标可以被Prometheus抓取,并在Grafana中可视化。
安装依赖:
pip install prometheus-client
创建一个 metrics.py 文件来定义和收集指标:
from prometheus_client import Counter, Histogram, Gauge, generate_latest, REGISTRY
from typing import Optional
# 定义指标
REQUEST_COUNT = Counter(
'api_proxy_requests_total',
'Total number of API requests',
['method', 'endpoint', 'status_code']
)
REQUEST_LATENCY = Histogram(
'api_proxy_request_duration_seconds',
'Request latency in seconds',
['method', 'endpoint'],
buckets=(0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0)
)
UPSTREAM_LATENCY = Histogram(
'api_proxy_upstream_duration_seconds',
'Upstream (SiliconFlow) API latency in seconds',
['upstream_service', 'model'],
buckets=(0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 30.0)
)
ACTIVE_REQUESTS = Gauge(
'api_proxy_requests_in_progress',
'Number of requests currently being processed',
['endpoint']
)
TOKEN_USAGE = Counter(
'api_proxy_tokens_total',
'Total tokens consumed',
['model', 'type'] # type: 'prompt' or 'completion'
)
# 指标收集工具类
class MetricsCollector:
@staticmethod
def record_request(method: str, endpoint: str, status_code: int, duration: float):
"""记录一次请求"""
REQUEST_COUNT.labels(method=method, endpoint=endpoint, status_code=str(status_code)).inc()
REQUEST_LATENCY.labels(method=method, endpoint=endpoint).observe(duration)
@staticmethod
def record_upstream_call(service: str, model: str, duration: float):
"""记录一次上游API调用"""
UPSTREAM_LATENCY.labels(upstream_service=service, model=model).observe(duration)
@staticmethod
def record_token_usage(model: str, token_type: str, count: int):
"""记录Token使用量"""
TOKEN_USAGE.labels(model=model, type=token_type).inc(count)
@staticmethod
def track_active_request(endpoint: str):
"""追踪活跃请求(用于上下文管理器)"""
return ActiveRequestTracker(endpoint)
class ActiveRequestTracker:
def __init__(self, endpoint: str):
self.endpoint = endpoint
ACTIVE_REQUESTS.labels(endpoint=endpoint).inc()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
ACTIVE_REQUESTS.label



被折叠的 条评论
为什么被折叠?



