AI推理服務閘道實戰:LLM API閘道、模型路由與限流降級

技术架构

摘要

  • AI推理閘道是LLM服務的「前門」:路由、限流、降級、可觀測4大核心能力缺一不可
  • 模型路由的3種策略:成本優先、延遲優先、品質優先,按業務場景選擇
  • 限流不止防刷:Token Rate Limiting比請求頻率限制更精準,避免單使用者佔滿上下文視窗
  • Fallback機制是SLA的底線:主模型逾時→備模型接管→快取兜底→優雅降級
  • 本文提供從閘道架構到Go實作的完整方案,含K8s部署與Prometheus監控

目錄


為什麼LLM服務需要專用閘道

傳統API閘道的3大不足

不足 說明 影響
無Token感知 按請求頻率限流,不感知Token消耗 單使用者長上下文請求可佔滿GPU
無模型路由 無法根據請求特徵路由到不同模型 小問題用大模型,浪費成本
無串流適配 SSE串流響應的限流/降級邏輯不同 串流請求逾時後無法優雅降級

AI推理閘道 vs 傳統API閘道

維度 傳統API閘道 AI推理閘道
限流維度 請求頻率 Token消耗 + 請求頻率
路由策略 URL路徑 模型能力 + 成本 + 延遲
響應模式 請求-響應 SSE串流 + 請求-響應
降級策略 回傳錯誤 Fallback到備模型
可觀測性 QPS/延遲 Token吞吐/首Token延遲/上下文長度

AI推理閘道架構設計

┌──────────────────────────────────────────────────────────────┐
│              AI推理閘道架構                                    │
│                                                                │
│  ┌──────────┐                                                │
│  │ 客戶端   │                                                │
│  └────┬─────┘                                                │
│       │                                                       │
│  ┌────▼──────────────────────────────────────────────────┐  │
│  │              AI Inference Gateway                      │  │
│  │                                                        │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │  │
│  │  │ 認證     │ │ 限流     │ │ 路由     │ │ 降級    │ │  │
│  │  │ API Key  │ │ Token RL │ │ 模型路由 │ │Fallback │ │  │
│  │  │ OAuth2.0 │ │ 優先佇列 │ │ 負載均衡 │ │ 快取    │ │  │
│  │  └──────────┘ └──────────┘ └──────────┘ └─────────┘ │  │
│  │                                                        │  │
│  │  ┌──────────────────────────────────────────────────┐ │  │
│  │  │              可觀測性                              │ │  │
│  │  │ Prometheus + OpenTelemetry + 結構化日誌           │ │  │
│  │  └──────────────────────────────────────────────────┘ │  │
│  └──────────────────────────────────────────────────────┘  │
│       │           │           │                              │
│  ┌────▼─────┐ ┌───▼──────┐ ┌─▼────────┐                   │
│  │ vLLM     │ │ TGI      │ │ SGLang   │                   │
│  │ Qwen-7B  │ │ Llama-70B│ │ Qwen-7B  │                   │
│  └──────────┘ └──────────┘ └──────────┘                   │
└──────────────────────────────────────────────────────────────┘

模型路由:3種策略與實作

路由策略對比

策略 路由邏輯 成本 延遲 品質 適用場景
成本優先 優先路由到最小模型 最低 內部工具
延遲優先 優先路由到最快模型 最低 即時對話
品質優先 優先路由到最強模型 最高 最高 專業場景

Go路由實作

package gateway

import (
	"context"
	"math"
	"sync"
	"time"
)

type ModelEndpoint struct {
	Name         string
	URL          string
	ModelID      string
	MaxTokens    int
	CostPerToken float64
	AvgLatency   time.Duration
	CurrentLoad  float64
	Capabilities []string
}

type RoutingStrategy string

const (
	CostFirst     RoutingStrategy = "cost_first"
	LatencyFirst  RoutingStrategy = "latency_first"
	QualityFirst  RoutingStrategy = "quality_first"
)

type ModelRouter struct {
	endpoints []*ModelEndpoint
	strategy  RoutingStrategy
	mu        sync.RWMutex
}

func NewModelRouter(strategy RoutingStrategy) *ModelRouter {
	return &ModelRouter{strategy: strategy}
}

func (r *ModelRouter) Register(endpoint *ModelEndpoint) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.endpoints = append(r.endpoints, endpoint)
}

func (r *ModelRouter) Route(ctx context.Context, req *InferenceRequest) (*ModelEndpoint, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	candidates := r.filterByCapability(req)
	if len(candidates) == 0 {
		return nil, ErrNoAvailableModel
	}

	switch r.strategy {
	case CostFirst:
		return r.routeByCost(candidates), nil
	case LatencyFirst:
		return r.routeByLatency(candidates), nil
	case QualityFirst:
		return r.routeByQuality(candidates), nil
	default:
		return r.routeByLatency(candidates), nil
	}
}

func (r *ModelRouter) routeByCost(candidates []*ModelEndpoint) *ModelEndpoint {
	best := candidates[0]
	for _, ep := range candidates[1:] {
		if ep.CostPerToken < best.CostPerToken && ep.CurrentLoad < 0.9 {
			best = ep
		}
	}
	return best
}

func (r *ModelRouter) routeByLatency(candidates []*ModelEndpoint) *ModelEndpoint {
	best := candidates[0]
	for _, ep := range candidates[1:] {
		effectiveLatency := float64(ep.AvgLatency) / (1.0 - ep.CurrentLoad + 0.01)
		bestLatency := float64(best.AvgLatency) / (1.0 - best.CurrentLoad + 0.01)
		if effectiveLatency < bestLatency {
			best = ep
		}
	}
	return best
}

func (r *ModelRouter) routeByQuality(candidates []*ModelEndpoint) *ModelEndpoint {
	best := candidates[0]
	for _, ep := range candidates[1:] {
		if ep.MaxTokens > best.MaxTokens && ep.CurrentLoad < 0.9 {
			best = ep
		}
	}
	return best
}

func (r *ModelRouter) filterByCapability(req *InferenceRequest) []*ModelEndpoint {
	var filtered []*ModelEndpoint
	for _, ep := range r.endpoints {
		if ep.CurrentLoad >= 0.95 {
			continue
		}
		if req.MaxTokens > 0 && ep.MaxTokens < req.MaxTokens {
			continue
		}
		filtered = append(filtered, ep)
	}
	return filtered
}

Token Rate Limiting:精準限流

Token限流 vs 請求限流

限流維度 優點 缺點 適用場景
請求頻率 實作簡單 不感知Token消耗 低精度限流
Token/分鐘 精準控制GPU消耗 需預估Token數 生產推薦
並行請求數 控制GPU並行 不感知上下文長度 簡單場景

Go Token限流實作

package gateway

import (
	"context"
	"sync"
	"time"
)

type TokenBucket struct {
	mu          sync.Mutex
	tokens      float64
	maxTokens   float64
	refillRate  float64
	lastRefill  time.Time
}

func NewTokenBucket(maxTokens, refillRate float64) *TokenBucket {
	return &TokenBucket{
		tokens:     maxTokens,
		maxTokens:  maxTokens,
		refillRate: refillRate,
		lastRefill: time.Now(),
	}
}

func (tb *TokenBucket) Allow(estimatedTokens int) bool {
	tb.mu.Lock()
	defer tb.mu.Unlock()

	now := time.Now()
	elapsed := now.Sub(tb.lastRefill).Seconds()
	tb.tokens = math.Min(tb.maxTokens, tb.tokens+elapsed*tb.refillRate)
	tb.lastRefill = now

	if tb.tokens >= float64(estimatedTokens) {
		tb.tokens -= float64(estimatedTokens)
		return true
	}
	return false
}

type TokenRateLimiter struct {
	buckets map[string]*TokenBucket
	mu      sync.RWMutex
}

func NewTokenRateLimiter() *TokenRateLimiter {
	return &TokenRateLimiter{buckets: make(map[string]*TokenBucket)}
}

func (rl *TokenRateLimiter) RegisterUser(userID string, maxTokens, refillRate float64) {
	rl.mu.Lock()
	defer rl.mu.Unlock()
	rl.buckets[userID] = NewTokenBucket(maxTokens, refillRate)
}

func (rl *TokenRateLimiter) Allow(userID string, estimatedTokens int) bool {
	rl.mu.RLock()
	bucket, ok := rl.buckets[userID]
	rl.mu.RUnlock()

	if !ok {
		return false
	}
	return bucket.Allow(estimatedTokens)
}

Fallback與降級:SLA的底線

4級降級策略

┌──────────────────────────────────────────────────────────┐
│              4級降級策略                                    │
│                                                            │
│  Level 0: 正常服務                                        │
│  ┌──────────────────────────────────────────┐             │
│  │ 主模型(Qwen-72B) → 延遲50ms, 品質最高    │             │
│  └──────────────────────────────────────────┘             │
│                    ↓ 逾時/過載                              │
│  Level 1: 備模型接管                                      │
│  ┌──────────────────────────────────────────┐             │
│  │ 備模型(Qwen-7B) → 延遲20ms, 品質降低     │             │
│  └──────────────────────────────────────────┘             │
│                    ↓ 備模型也過載                           │
│  Level 2: 快取兜底                                        │
│  ┌──────────────────────────────────────────┐             │
│  │ 語意快取命中 → 延遲5ms, 品質取決於快取    │             │
│  └──────────────────────────────────────────┘             │
│                    ↓ 快取未命中                             │
│  Level 3: 優雅降級                                        │
│  ┌──────────────────────────────────────────┐             │
│  │ 回傳預設回覆 + 重試提示                   │             │
│  └──────────────────────────────────────────┘             │
└──────────────────────────────────────────────────────────┘

Go Fallback實作

type FallbackChain struct {
	primary    *ModelEndpoint
	secondary  *ModelEndpoint
	cache      SemanticCache
	timeout    time.Duration
}

func (fc *FallbackChain) Generate(ctx context.Context, req *InferenceRequest) (string, error) {
	ctx, cancel := context.WithTimeout(ctx, fc.timeout)
	defer cancel()

	result, err := fc.callModel(ctx, fc.primary, req)
	if err == nil {
		return result, nil
	}

	log.Warn("primary model failed, falling back", "error", err)

	if fc.secondary != nil {
		result, err = fc.callModel(ctx, fc.secondary, req)
		if err == nil {
			return result, nil
		}
		log.Warn("secondary model failed", "error", err)
	}

	if fc.cache != nil {
		cached, ok := fc.cache.Get(req.Prompt)
		if ok {
			log.Info("cache hit on fallback")
			return cached, nil
		}
	}

	return "服務暫時無法使用,請稍後重試。", ErrServiceUnavailable
}

Go閘道實作與K8s部署

K8s Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ai-inference-gateway
  namespace: ai-inference
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ai-inference-gateway
  template:
    spec:
      containers:
        - name: gateway
          image: myregistry/ai-inference-gateway:v1.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "2"
              memory: 2Gi
            limits:
              cpu: "4"
              memory: 4Gi
          env:
            - name: PRIMARY_MODEL_URL
              value: "http://vllm-qwen72b:8000/v1"
            - name: SECONDARY_MODEL_URL
              value: "http://vllm-qwen7b:8000/v1"
            - name: REDIS_URL
              value: "redis://redis:6379"
            - name: ROUTING_STRATEGY
              value: "latency_first"
            - name: TOKEN_RATE_LIMIT
              value: "100000"
---
apiVersion: v1
kind: Service
metadata:
  name: ai-gateway-svc
  namespace: ai-inference
spec:
  selector:
    app: ai-inference-gateway
  ports:
    - port: 8080
      targetPort: 8080
  type: ClusterIP

Prometheus監控

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: ai-gateway-alerts
  namespace: ai-inference
spec:
  groups:
    - name: ai-gateway
      rules:
        - alert: HighFallbackRate
          expr: rate(gateway_fallback_total[5m]) / rate(gateway_requests_total[5m]) > 0.1
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "AI閘道Fallback率過高"
        - alert: TokenRateLimitExceeded
          expr: rate(gateway_rate_limit_exceeded_total[5m]) > 10
          for: 2m
          labels:
            severity: warning
          annotations:
            summary: "Token限流觸發頻繁"

總結與延伸閱讀

AI推理閘道是LLM服務的「前門」,4大核心能力(路由、限流、降級、可觀測)缺一不可。Token Rate Limiting比請求頻率限流更精準,4級Fallback保障SLA底線。

設計要點回顧

  1. AI推理閘道必須Token感知,傳統API閘道不夠
  2. 3種路由策略:成本優先/延遲優先/品質優先,按場景選擇
  3. Token Rate Limiting是生產限流的標配
  4. 4級Fallback:主模型→備模型→快取→優雅降級
  5. Go實作+K8s部署+Prometheus監控是生產標配

相關閱讀

權威參考

本站提供瀏覽器本地工具,免註冊即可試用 →

#AI推理网关#LLM API网关#模型路由策略#推理服务治理#AI网关限流#2026