01_LLM服务的健康检查该怎么写

发布时间:2026/10/3 11:49:20
01_LLM服务的健康检查该怎么写 LLM 服务的健康检查该怎么写liveness 和 readiness 必须分开健康检查大概是整个 LLM 服务里最不起眼、也最容易写错的一块。大多数人会写成查一下依赖通不通通就 200这在普通 Web 服务里问题不大放在 LLM 服务上会出事。一、混在一起写会发生什么先看一个典型的错误写法app.get(/health)defhealth():okping_llm_provider()# 探一下上游return{status:okifokelsedown}在 Kubernetes 里/health通常被配成liveness 探针。liveness 的语义是进程还活着吗失败就重启容器。问题就在这上游抖了一下你的全部实例被重启。LLM 上游比普通依赖更容易抖限流、排队、区域故障我实测过同一批请求早上 p95 能到 22.85 秒、晚上只有 3.33 秒。一次上游抖动 → 健康检查全失败 → 全部实例重启 → 重启后冷启动又慢 → 健康检查继续失败。一次抖动被放大成一次雪崩。这不是理论是健康检查最经典的自伤式配置。二、正确的切分探针回答什么能不能碰网络失败后果liveness进程活着吗绝对不能重启容器readiness能接客吗可以从负载均衡摘掉对应到代码app.get(/health,response_modelHealthOut)defhealth():K8s liveness 用的那种进程活着就 200不碰网络永远不会因外部故障而假死。returnHealthOut(statusok,uptime_sround(time.time()-START_TIME,3),checked_atdatetime.now().isoformat(timespecseconds),)app.get(/health/ready,response_modelReadyOut)defready(ping:boolQuery(False)):providersall_provider_status(settings,do_pingping).../health里一行网络调用都没有。它只回答这个进程还在不在。/health/ready才查依赖而且默认不真探活——要加?ping1才去打各家/models。原因有两条就绪探针被调得很频繁每次都打上游是浪费只列模型不产生 token 费用但依然要网络往返没必要每次都做。三、降级态也要能表达就绪探针的返回值不是只有好/坏两态还有中间态——部分可用。configured[pforpinprovidersifp[configured]]ifnotconfigured:statusnot_readyelifping:reachable[pforpinconfiguredifp[status]ready]ifnotreachable:statusnot_readyeliflen(reachable)len(configured):statusdegraded# 降级态主用挂了备胎还行degraded很关键。三家 provider 里挂了一家服务其实还能用有降级链这时候该做的是告警而不是把它摘掉或重启。另外我把成本闸门也接进了就绪探针costsummarize(settings)ifcost[over_limit]:statusdegradeddetailf本地成本账本已达闸门 ¥{cost[limit_cny]}禁止继续调用为什么成本要参与健康判断因为对一个 LLM 服务来说钱花超了和依赖挂了是同一级别的不可用。早发现比事后对账强。四、请求 ID没有它线上只能靠猜classRequestIdMiddleware:纯 ASGI 中间件不依赖 Starlette 的高层 API升级不易碎。asyncdef__call__(self,scope,receive,send):riddict(scope[headers]).get(bx-request-id)ridrid.decode()ifridelseuuid.uuid4().hex[:16]scope[request_id]ridasyncdefsend_wrapper(message):ifmessage[type]http.response.start:message.setdefault(headers,[]).append((bx-request-id,rid.encode()))awaitsend(message)awaitself.app(scope,receive,send_wrapper)两个要点支持透传客户端带了就用客户端的这样跨服务的链路能串起来。写成纯 ASGI而不是继承BaseHTTPMiddleware后者会在每次请求额外起一个 anyio 任务组有已知的性能和流式问题。纯 ASGI 就是处理三个 message 类型没有隐藏开销。五、成本账本为什么用 append 而不是重写defappend_entry(s:Settings,entry:dict)-None:entry.setdefault(ts,datetime.now().isoformat(timespecseconds))withopen(ledger_path(s),a,encodingutf-8)asf:f.write(json.dumps(entry,ensure_asciiFalse)\n)一行调用一个 JSON 对象JSONL 格式。选 append 的理由很实在进程崩了也不丢历史。如果是攒在内存里定期重写崩掉的那一刻最近一批记录全没了——而那批往往正是出问题时最需要的。汇总时按行解析顺便按 provider / model 聚合{total_cny:0.103152,calls:201,limit_cny:20.0,remaining_cny:19.896848,by_provider:{deepseek:0.103152,zhipu:0.0,dashscope:0.0},by_model:{deepseek-flash:0.103152,...}}by_model这一层不是凑数。真正排障时你要回答的是钱是哪个模型烧掉的只看总数答不了这个问题。小结要点做法liveness只回答进程存活绝不碰网络readiness才查依赖默认不真探活?ping1才打中间态用degraded表达部分可用别只有好坏两态成本闸门状态参与健康判断请求 ID支持透传纯 ASGI 中间件实现账本追加写 JSONL进程崩了不丢历史下一篇模型输出的 JSON 不可信——结构化输出的四层保障。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询