用后端底座設(shè)計與高并發(fā)支撐:并發(fā)時先看資源邊界)
大模型應(yīng)用后端底座設(shè)計與高并發(fā)支撐并發(fā)時先看資源邊界當(dāng)大模型LLM應(yīng)用的用戶規(guī)模從幾十個內(nèi)部測試人員暴增到上萬并發(fā)請求時后端架構(gòu)師面臨的挑戰(zhàn)與傳統(tǒng) Web 系統(tǒng)完全不同。傳統(tǒng) Web 微服務(wù)處理一個 HTTP 請求耗時通常在 20ms 以內(nèi)而大模型推理一個請求可能持續(xù)數(shù)秒甚至數(shù)十秒同時占用沉重的 GPU 顯存KV Cache。并發(fā)流量一旦涌入如果不加控制地把請求全量塞給推理引擎如 vLLM 或 TGI系統(tǒng)不會只是緩慢排隊而是會直接觸發(fā) GPU 顯存 OOM 崩潰或者導(dǎo)致首字延遲TTFTTime To First Token暴漲到幾十秒讓絕大多數(shù)用戶因超時而放棄連接。并發(fā)上來之后后端底座的第一條防線應(yīng)是基于 KV Cache 物理容量估算與 TTFT 要求的自適應(yīng)背壓控制。為什么大模型后端的并發(fā)瓶頸在 KV Cache在傳統(tǒng) API 后端中內(nèi)存瓶頸通常在 JVM 堆內(nèi)存或 Go 堆內(nèi)存。但在 LLM 推理 Server 中真正的命門在于GPU KV Cache 顯存容量。每一次 Context 交互模型在 Transformer 注意力層都需要為歷史 Token 維護 Key-Value Cache。一個 70B 參數(shù)的模型在 FP16 精度下單用戶 4096 長度的 Context 就要占用近 2GB 的 KV Cache 顯存。flowchart TD UserReqs[突發(fā) 500 個并發(fā) LLM 請求] --|1. 涌入 LLM Gateway| Gateway[LLM Backend Gateway] Gateway --|2. 檢查當(dāng)前 GPU KV Cache 占用| Evaluator{KV Cache 占用率 85%?} Evaluator -- 是: 觸發(fā)背壓守住線 --|3. 啟動優(yōu)先級拒絕| PriorityQueue[Priority Load Shedding] PriorityQueue --|VIP 用戶請求| ExecQueue[壓入 待推理隊列] PriorityQueue --|普通 / 匿名請求| FastReject[直接返回 429 System Busy] Evaluator -- 否 --|4. 放行給推理引擎| vLLMEngine[vLLM Inference Engine] vLLMEngine --|5. PagedAttention 動態(tài)分配| GPUCache[GPU 物理顯存 KV Cache] ExecQueue -- vLLMEngine即使采用了 PagedAttention如 vLLM 架構(gòu)顯存能夠?qū)崿F(xiàn)細粒度的物理頁復(fù)用顯存總?cè)萘恳廊粵Q定了系統(tǒng)能夠同時進行 Prefill首字填充和 DecodeToken 生成的物理并發(fā)上限。一旦并發(fā)數(shù)超過了這個物理極限vLLM 引擎不得不將部分請求的 KV Cache 搶占并 Swap 到 CPU 內(nèi)存這會導(dǎo)致推理延遲暴增 10 倍以上系統(tǒng)迅速陷入癱瘓?;谖锢盹@存與 TTFT 的容量估算公式在大模型后端底座設(shè)計中不能盲目相信“高并發(fā)支持 10,000 QPS”的宣傳。應(yīng)根據(jù) GPU 顯存大小精確計算當(dāng)前集群的最大并發(fā)推理 Slot 數(shù)量$$\text{MaxConcurrentSlots} \frac{\text{TotalVRAM} - \text{ModelWeightsVRAM} - \text{ActivationVRAM}}{\text{AvgContextLen} \times \text{KVCacheSizePerToken}}$$假設(shè)使用一張 80GB 顯存的 A100 GPU 運行 32B 模型模型權(quán)重占用約 64GB 顯存。激活值與系統(tǒng)保留占用 6GB 顯存。剩余可用于 KV Cache 的顯存為80GB - 64GB - 6GB 10GB。若平均上下文長度為 4096 Token每個 Token 消耗 KV Cache 約 1MB 顯存則單請求消耗4GB顯存。結(jié)論單張 A100 在此配置下并發(fā)支持的上下文 Slot 極限只有 2 到 3 個。超過 3 個并發(fā)應(yīng)依賴 Tensor Parallelism多卡并行或者前端 Gateway 的嚴格隊列攔截。第二個關(guān)鍵指標是首字延遲TTFT。大模型推理分為 Prefill 階段計算 Prompt和 Decode 階段逐字生成。Prefill 是 Compute-bound計算密集型如果多個大 Prompt 同時進入 PrefillGPU 會發(fā)生嚴重的算力搶占導(dǎo)致首字遲遲無法吐出。應(yīng)設(shè)定硬性 SLA 目標如P99 TTFT ≤ 1500ms。一旦當(dāng)前隊列估算的 Prefill 時間超過 1.5 秒后續(xù)請求應(yīng)在 Gateway 處強行截斷。多級背壓控制器實現(xiàn)代碼為了守護 GPU 不被 OOM 沖垮同時保證 VIP 業(yè)務(wù)不中斷后端底座應(yīng)在 LLM 引擎上游構(gòu)建多級信號量限流與自適應(yīng)背壓控制器package gateway import ( context errors net/http sync/atomic time ) var ( ErrQueueFull errors.New(llm backend inference queue full, load shed active) ) type PriorityLevel int const ( PriorityNormal PriorityLevel iota PriorityVIP ) type LLMBackpressureController struct { maxActiveSlots int64 activeSlots int64 maxQueueLen int64 currentQueue int64 } func NewLLMBackpressureController(maxSlots, maxQueue int64) *LLMBackpressureController { return LLMBackpressureController{ maxActiveSlots: maxSlots, maxQueueLen: maxQueue, } } func (c *LLMBackpressureController) AcquireSlot(ctx context.Context, priority PriorityLevel) (func(), error) { // 1. 判斷當(dāng)前活躍推理解析 Slot 是否足夠 if atomic.LoadInt64(c.activeSlots) c.maxActiveSlots { atomic.AddInt64(c.activeSlots, 1) return func() { atomic.AddInt64(c.activeSlots, -1) }, nil } // 2. Slot 滿進入背壓排隊邏輯 // 如果是普通請求且隊列已經(jīng)超過 8無直接快速拒絕Fast-Fail queueLen : atomic.LoadInt64(c.currentQueue) if priority PriorityNormal queueLen int64(float64(c.maxQueueLen)*0.8) { return nil, ErrQueueFull } if queueLen c.maxQueueLen { return nil, ErrQueueFull } atomic.AddInt64(c.currentQueue, 1) defer atomic.AddInt64(c.currentQueue, -1) // 3. 在 Queue 中等待超時或可用 Slot ticker : time.NewTicker(20 * time.Millisecond) defer ticker.Stop() for { select { case -ctx.Done(): return nil, ctx.Err() case -ticker.C: if atomic.LoadInt64(c.activeSlots) c.maxActiveSlots { atomic.AddInt64(c.activeSlots, 1) return func() { atomic.AddInt64(c.activeSlots, -1) }, nil } } } } func (c *LLMBackpressureController) HTTPMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { priority : PriorityNormal if r.Header.Get(X-User-Tier) VIP { priority PriorityVIP } // 最長等待 3 秒首字排隊超時 ctx, cancel : context.WithTimeout(r.Context(), 3*time.Second) defer cancel() release, err : c.AcquireSlot(ctx, priority) if err ! nil { w.Header().Set(Retry-After, 2) w.WriteHeader(http.StatusTooManyRequests) _, _ w.Write([]byte({error: LLM Capacity Exceeded, code: 429})) return } defer release() next.ServeHTTP(w, r.WithContext(ctx)) }) }流式斷連與 Prefill 算力回收機制除了防范入站流量過載大模型后端底座還應(yīng)處理**客戶端中途取消連接Client Disconnect**的算力浪費問題。在 SSE 模式下用戶看到吐出前 5 個字不符合預(yù)期經(jīng)常會直接關(guān)閉網(wǎng)頁或點擊“停止生成”。如果 Gateway 沒有將 TCP 顯式斷開事件透傳給底層推理 EnginevLLM Engine 還會繼續(xù)傻傻地把剩下的 2000 個 Token 吐完白白浪費巨量的 GPU 顯存與計算時間。后端底座應(yīng)建立Client Cancel Context Propagation機制當(dāng) Gateway 檢測到r.Context().Done()觸發(fā)Client 斷開時立即通過 gRPC / HTTP 向 vLLM 的/cancel接口發(fā)送request_id強行終止該 Request 的 Decode 循環(huán)瞬間釋放其占用的 KV Cache 頁。生產(chǎn)落地的底線要求在大模型應(yīng)用后端底座上線前架構(gòu)團隊應(yīng)守住三條底線盡量禁止無限制的 HTTP 長連接排隊Gateway 層面的 Queue 長度應(yīng)是有界上限超過界限立刻返回429降級提示。區(qū)分 Prefill 節(jié)點與 Decode 節(jié)點PD 分離架構(gòu)高并發(fā)場景下將 Prefill計算 Prompt與 Decode生成 Token部署在不同的 GPU 節(jié)點上避免大 Prompt 輸入卡死小生成的 Token 吐出。熔斷搶占 Swap 機制一旦發(fā)現(xiàn) GPU 顯存 Swap 到 CPU 內(nèi)存的頻率大于 0說明系統(tǒng)已經(jīng)嚴重超載背壓控制器應(yīng)立刻提高 Load Shedding 拋棄比例。守住了 KV Cache 顯存與首字延遲這條線大模型后端底座才能在應(yīng)對突發(fā)流量洪峰時做到較穩(wěn)定。