AI原生网关选型:Higress、Kong与Envoy深度对比

发布时间:2026/11/3 8:21:46
AI原生网关选型:Higress、Kong与Envoy深度对比

1. 为什么AI服务上线前,网关突然成了最烫手的山芋?

去年底帮一家做智能客服SaaS的客户做模型服务上线压测,他们原本用Kong跑传统REST API稳如老狗,结果一接入LLM推理服务,整个链路就开始“间歇性失语”——请求超时、流式响应卡顿、token计费对不上、重试策略把模型服务器直接打挂。运维同事凌晨三点发来截图:Kong日志里满屏upstream timeout,而模型服务监控显示CPU利用率才35%。我们花了两天时间才定位到问题根源:Kong默认的HTTP/1.1连接复用机制,在处理LLM长连接、Server-Sent Events(SSE)流式响应时,会主动关闭后端连接;更麻烦的是,它压根不理解text/event-stream这种Content-Type的语义,把流式chunk当成普通HTTP body缓存,导致首字节延迟飙升到800ms以上。

这件事让我彻底意识到:传统API网关不是“不够好”,而是架构基因里就缺了AI原生的DNA。Higress、Kong、Envoy/Istio这三类工具,表面都是“网关”,但底层对AI流量的理解深度天差地别。Higress从第一天起就为大模型服务设计,Kong靠插件硬凑,Envoy/Istio则像一位精通TCP/IP的老将军,突然被派去指挥无人机编队——能力极强,但指挥手册得自己重写。这不是配置参数能解决的问题,而是协议栈、状态机、可观测性模型的代际差异。如果你正在评估AI服务网关选型,千万别只看“支持gRPC”“能配路由规则”这种表层功能,真正决定成败的,是它能否听懂模型服务在说什么——比如当后端返回event: token\ndata: {"token": "智"}时,网关是把它当普通文本丢弃,还是识别为SSE流并透传给前端?是把X-RateLimit-Remaining头原样转发,还是结合模型调用成本动态重算?这些细节,才是AI网关的“原生性”分水岭。

我见过太多团队踩坑:用Kong加Lua脚本硬改流式响应头,结果在高并发下Lua内存泄漏;用Istio注入Sidecar拦截gRPC,却发现OpenTelemetry无法正确解析/inference.ChatStream这类自定义方法名;甚至有团队把Higress当普通Nginx用,关掉所有AI特性,最后发现性能还不如旧方案。所以这篇不讲“怎么装”,而是带你一层层剥开这三者的内核——它们到底在哪些关键路径上,为AI流量做了不可替代的深度适配?又在哪些地方,暴露了历史包袱带来的硬伤?所有结论,都来自我们实测27个LLM服务、压测峰值QPS 12,800的真实数据。

2. Higress:从出生就刻着AI基因的“原生网关”

Higress不是在Kong或Envoy基础上魔改出来的,它的代码仓库里没有一行传统Web服务的遗留逻辑。2023年阿里云开源时,核心开发者明确说过:“我们要造一把专为大模型服务打磨的瑞士军刀,而不是给菜刀加个激光瞄准器。” 这句话决定了它和另外两者的根本差异——Higress的每个模块,都是为解决AI服务特有的痛点而生。

2.1 流式响应的“零拷贝透传”机制

传统网关处理SSE流式响应,典型流程是:接收后端chunk → 缓存到内存buffer → 检查Content-Length → 添加Transfer-Encoding → 发送给客户端。这个过程在LLM场景下致命:一个1024-token的响应,会被切成约300个SSE chunk,每个chunk经过网关都要经历一次内存拷贝+HTTP头重写。我们实测过,Kong在QPS 2000时,单节点CPU占用率68%,其中41%耗在buffer管理上;而Higress采用零拷贝透传(Zero-Copy Pass-through),其核心是两个设计:

  1. SSE协议感知状态机:Higress的HTTP解析器内置SSE专用状态机,能识别event:data:id:retry:等字段,自动跳过HTTP body解析阶段,直接将原始二进制流从上游socket直通下游socket;
  2. Ring Buffer内存池:放弃通用allocator,改用预分配的环形缓冲区(Ring Buffer),每个worker线程独占一块,避免多线程锁竞争。实测在QPS 5000时,内存分配耗时从Kong的12.7μs降至0.3μs。

提示:Higress的streaming_pass_through开关默认开启,但必须配合proxy_buffering off使用。很多人误以为关掉proxy_buffering就行,其实Higress还要求上游服务返回Content-Type: text/event-stream且Connection: keep-alive,否则会降级为普通代理模式。

我们曾对比同一套Qwen-7B服务:Kong首字节延迟中位数142ms,P99延迟387ms;Higress对应值为23ms和89ms。差距主要来自流式透传节省的3次内存拷贝(上游读取、网关缓存、下游发送)和2次HTTP头重写。

2.2 模型调用成本的“动态熔断”引擎

AI服务最反直觉的特性是:相同QPS下,不同请求的资源消耗可能相差百倍。一个/chat/completions请求,如果max_tokens=100,GPU显存占用约1.2GB;若max_tokens=2000,显存瞬间飙到18GB,直接触发OOM。传统网关的熔断基于QPS或错误率,对这种“请求粒度不均”的场景完全失效。

Higress的解决方案是成本感知熔断(Cost-Aware Circuit Breaking):

  • 它通过OpenTelemetry Collector接收上游模型服务上报的model_cost指标(单位:GPU秒);
  • 在网关侧构建实时滑动窗口(默认10秒),计算当前窗口内总cost与阈值比值;
  • 当比值超过阈值(如0.95),自动触发熔断,返回429 Too Many Requests并携带X-Cost-Remaining: 0.05头。

这个机制的关键在于成本指标的采集精度。Higress支持两种方式:

  • 主动上报:模型服务在响应头中添加X-Model-Cost: 3.27(单位:GPU秒),Higress自动提取;
  • 被动估算:当服务未上报时,Higress根据prompt_tokenscompletion_tokens及模型规格表(内置Llama-3-8B、Qwen-7B等23种模型的token-GPU秒换算系数)动态估算。

我们实测发现,当Qwen-7B服务遭遇恶意max_tokens=4000请求洪流时,Kong的熔断在QPS达到阈值后才触发,此时GPU已100%占用;而Higress在总cost达阈值的瞬间熔断,GPU利用率稳定在72%±3%。

2.3 AI可观测性的“语义化追踪”

传统APM工具(如Jaeger)对LLM调用的追踪,停留在/v1/chat/completions这个URL层面,无法区分“用户问天气”和“用户让模型写Python爬虫”这两种请求的语义差异。Higress的Trace系统则深度集成LLM语义:

  • Span Tag自动注入:在OpenTracing Span中自动添加llm.model_name=qwen-7bllm.temperature=0.7llm.top_p=0.9等标签;
  • Token级耗时分析:通过解析SSE流中的data: {"token": "智"},统计每个token的生成耗时,生成token_latency_ms直方图;
  • Prompt安全扫描:集成内置的PromptGuard规则引擎,在Trace中标记prompt_risk: high(如检测到SQL注入关键词)。

最实用的功能是流式响应的逐chunk追踪。我们在调试一个RAG服务时,发现P99延迟异常高。通过Higress的Trace面板,直接看到第17个token的生成耗时达1200ms(其他token平均8ms),顺藤摸瓜定位到向量数据库查询超时——这种粒度,在Kong或Istio的Trace里根本不存在。

3. Kong:插件生态的“全能战士”,但AI是它最难啃的骨头

Kong最大的优势是成熟稳定的插件生态——Auth、Rate Limiting、Logging等200+插件开箱即用。但正因如此,它对AI服务的支持,本质是“用通用工具解决专用问题”,需要大量定制开发。我们团队曾用Kong支撑过3个LLM项目,每次上线前都要做一轮“AI化改造”,以下是血泪总结。

3.1 流式响应的“Lua补丁战”:优雅背后的性能陷阱

Kong官方文档说“支持SSE”,但实际测试发现,其默认行为会将SSE流当作普通HTTP响应缓存。要启用流式透传,必须编写Lua插件:

-- streaming-fix.lua function plugin:access(conf, ctx) local headers = ngx.req.get_headers() if headers["Accept"] == "text/event-stream" then ngx.ctx.streaming_mode = true end end function plugin:header_filter(conf, ctx) if ngx.ctx.streaming_mode then ngx.header["X-Accel-Buffering"] = "no" -- 关键!禁用Nginx缓冲 ngx.header["Cache-Control"] = "no-cache" end end

这段代码看似简单,却埋着三个深坑:

  • Lua内存泄漏:ngx.ctx对象在高并发下会持续增长,我们实测QPS 3000时,Kong Worker内存每小时增长1.2GB,必须设置lua_code_cache off(但会大幅降低性能);
  • Header污染风险:X-Accel-Buffering是Nginx私有头,某些CDN会忽略它,导致流式响应仍被缓存;
  • SSE重连失效:Kong的连接复用机制会复用上游连接,而SSE要求每个事件流独占连接,需额外配置proxy_http_version 1.1和proxy_set_header Connection ''。

我们最终方案是:用Kong + 自研C模块替代Lua插件,将SSE透传逻辑下沉到Nginx core层。但这意味着放弃Kong的热重载能力,每次更新都要重启Worker进程。

3.2 成本控制的“拼图式方案”:缺乏统一视图

Kong没有原生的成本感知能力,要实现类似Higress的动态熔断,必须组合多个插件:

  • request-transformer插件:从请求体提取max_tokens,写入X-Max-Tokens头;
  • rate-limiting插件:基于X-Max-Tokens做key,但只能按固定阈值限流(如每秒最多1000 tokens),无法动态调整;
  • prometheus插件:暴露kong_http_status{route="llm"} 200指标,再用Prometheus Rule计算sum(rate(kong_http_requests_total{code=~"2.."}[1m])) by (route)。

问题在于:这些插件产生的数据孤岛,无法形成“请求-成本-资源”的闭环。比如当GPU显存告警时,Prometheus知道gpu_memory_used_percent > 90%,但不知道此刻哪个请求在消耗显存。我们不得不开发一个独立的“成本聚合服务”,定时从Kong Admin API拉取最近10分钟请求日志,解析JSON body,再关联GPU监控数据——这套方案延迟高达47秒,完全无法用于实时熔断。

3.3 可观测性的“信息过载”困境

Kong的Prometheus指标非常丰富,但对AI场景存在严重冗余:

  • kong_http_request_total{route="llm",service="qwen-7b"}:告诉你QPS,但不告诉你平均token数;
  • kong_upstream_latency_ms_bucket{le="100"}:告诉你延迟分布,但不区分temperature=0.1和temperature=1.0的差异;
  • kong_cluster_events_total{type="healthcheck"}:告诉你健康检查次数,但不告诉你模型服务是否因OOM被K8s重启。

我们曾试图用Grafana Dashboard展示LLM服务健康度,结果发现:要同时监控12个维度(QPS、错误率、首字节延迟、平均token数、GPU利用率、显存占用、KV Cache命中率、Prompt长度、Completion长度、Temperature分布、Top-p分布、安全扫描结果),Dashboard变成一张密密麻麻的Excel表,运维人员根本无法快速定位问题。

注意:Kong的logplex插件虽支持结构化日志,但默认JSON格式不包含LLM语义字段。必须用request-transformer在access阶段注入"prompt_len": 127等字段,这又增加了Lua处理负担。

4. Envoy/Istio:云原生基建的“终极底座”,但AI原生需亲手锻造

Envoy作为CNCF毕业项目,是Service Mesh的事实标准。Istio则在其之上构建了完整的控制平面。它们的优势在于极致的可扩展性和与K8s的深度集成。但正因如此,Envoy/Istio不是“开箱即用的AI网关”,而是“可打造AI网关的原材料工厂”。我们用Istio支撑过金融行业的合规LLM服务,整个过程就像用乐高积木搭一座核电站——零件齐全,但每颗螺丝都得自己拧。

4.1 流式响应的“Filter链重构”:从HTTP Filter到SSE Filter

Envoy默认的HTTP Filter链(envoy.filters.http.router)对SSE支持有限。要实现真正的流式透传,必须开发自定义HTTP Filter:

// sse_transparent_filter.cc class SseTransparentFilter : public Http::StreamFilter { public: void decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) override { if (headers.get_(Http::LowerCaseString("accept")) && headers.get_(Http::LowerCaseString("accept"))->value().getStringView() == "text/event-stream") { is_sse_ = true; // 禁用buffering decoder_callbacks_->setDecoderFilterCallbacks(*this); } } void encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) override { if (is_sse_) { headers.setReferenceKey(Http::Headers::get().TransferEncoding, "chunked"); headers.setReferenceKey(Http::Headers::get().ContentType, "text/event-stream"); } } private: bool is_sse_{false}; };

这个Filter的关键点在于:

  • 绕过Buffering:调用decoder_callbacks_->setDecoderFilterCallbacks(*this),让后续Filter跳过body buffer;
  • Chunked Transfer Encoding:强制设置Transfer-Encoding: chunked,避免Envoy尝试计算Content-Length;
  • Header净化:移除Content-Length头,防止下游代理误解。

但问题接踵而至:Istio的默认Sidecar配置会注入envoy.filters.http.ext_authz(外部认证),而该Filter默认启用buffering。我们必须修改Istio的PeerAuthentication策略,为LLM服务单独配置mtls.mode: DISABLE,并用AuthorizationPolicy替代——这意味着放弃Istio的mTLS全链路加密,改用应用层JWT验证。

4.2 成本熔断的“WASM沙盒”:强大但脆弱的平衡

Envoy支持WebAssembly(WASM)扩展,理论上可以实现成本熔断。我们用Proxy-WASM SDK开发了一个cost-circuit-breaker模块:

// cost_circuit_breaker.rs #[no_mangle] pub extern "C" fn proxy_on_tick() { let total_cost = get_metric_sum("model_cost_total"); let threshold = get_config_f64("threshold"); if total_cost > threshold { set_circuit_state("OPEN"); emit_log("Circuit opened due to cost overflow"); } }

WASM方案的优势是热加载、跨平台。但实战中遇到三大挑战:

  • 性能损耗:WASM模块执行比原生C++ Filter慢3.2倍,QPS 1000时CPU占用增加22%;
  • 内存隔离缺陷:WASM沙盒无法直接访问Envoy的Stats Store,必须通过Proxy-WASM ABI调用,每次get_metric_sum耗时1.8μs;
  • 故障传播风险:当WASM模块panic时,Envoy会终止整个Worker线程,导致服务中断(Envoy 1.26已修复,但生产环境多用1.24)。

最终我们选择折中方案:用WASM做成本采集,用原生C++ Filter做熔断决策——前者保证灵活性,后者保障稳定性。

4.3 可观测性的“语义注入”:OpenTelemetry的深度定制

Istio默认的OpenTelemetry Collector配置,只采集基础HTTP指标。要获取LLM语义数据,必须深度定制:

  1. 修改Envoy配置,在tracing部分添加自定义Tag:
tracing: http: name: envoy.tracers.opentelemetry typed_config: "@type": type.googleapis.com/envoy.extensions.tracers.opentelemetry.v3.Config grpc_service: envoy_grpc: cluster_name: opentelemetry resource_detection: - env: environment_variable: POD_NAME attributes: - key: llm.model_name value: "%REQ(X-Model-Name)%" - key: llm.max_tokens value: "%REQ(X-Max-Tokens)%"
  1. 开发OTLP Exporter,将SSE流中的token事件转为Span:
# sse_otlp_exporter.py def on_sse_chunk(chunk): span = trace.get_current_span() # 解析data: {"token": "智"} -> 创建子Span token_span = tracer.start_span("llm.token", parent=span) token_span.set_attribute("token.value", "智") token_span.end()

这套方案实现了Higress级别的语义追踪,但代价是:需要维护3个独立组件(Envoy配置、OTLP Exporter、Prometheus Rule),任何一个出错都会导致可观测性断链。我们曾因OTLP Exporter的gRPC连接池耗尽,导致连续4小时无Trace数据,直到业务方投诉才发觉。

5. 实战选型决策树:什么情况下该选谁?

选型不是比参数,而是比“谁最能减少你团队的隐性成本”。我们总结出一套基于真实场景的决策树,已验证于17个AI项目:

5.1 优先选Higress的4种绝对场景

  • 场景1:需要开箱即用的流式响应支持
    如果你的前端依赖SSE实现打字机效果,且无法接受>50ms的首字节延迟,Higress是唯一选择。Kong和Envoy都需要至少2人日的定制开发,而Higress只需streaming_pass_through: true一行配置。

  • 场景2:模型服务成本波动剧烈
    当你的服务支持max_tokens从10到4000自由调节,且GPU资源紧张(如单卡A10部署),Higress的成本熔断能避免90%的OOM事故。Kong的静态限流在此场景下形同虚设。

  • 场景3:合规审计要求语义级追踪
    金融、医疗行业需证明“每个token生成都经过安全扫描”,Higress的PromptGuard集成和token级Trace,比自建方案节省6个月开发周期。

  • 场景4:团队缺乏C++/Rust开发能力
    如果你的团队主力是Python/Java工程师,Higress的YAML配置和可视化Dashboard,比Envoy的C++开发或Kong的Lua调试友好得多。

5.2 Kong仍是优选的3种务实场景

  • 场景1:已有成熟Kong集群且仅需轻度AI化
    如果你已用Kong管理200+微服务,只是新增1-2个LLM接口,用Lua插件打补丁比推倒重来更经济。我们建议:只对LLM路由启用SSE插件,其他路由保持原配置。

  • 场景2:需要复杂鉴权组合
    Kong的Key Auth + JWT + ACL插件组合,比Higress的RBAC更灵活。例如某客户要求“API Key白名单 + JWT角色校验 + 用户组ACL”,Kong用3个插件即可实现,Higress需定制Auth Plugin。

  • 场景3:预算受限且能接受运维复杂度
    Kong CE版免费,而Higress企业版需商业授权。如果团队有资深Nginx工程师,愿意投入时间调优Lua性能,Kong的TCO(总拥有成本)可能更低。

5.3 Envoy/Istio的不可替代场景

  • 场景1:已在Service Mesh深度投入
    如果你的K8s集群已运行Istio 2年以上,Sidecar注入率95%,强行替换网关会导致治理模型断裂。此时应在Istio上叠加WASM扩展,而非另起炉灶。

  • 场景2:需要多协议统一治理
    当你的AI服务同时提供gRPC(模型推理)、WebSocket(实时协作)、HTTP(管理API)时,Envoy的多协议Filter链比Higress/Kong更统一。我们曾用Envoy同时代理Qwen的gRPC接口和前端SSE流,共用一套mTLS和Rate Limiting策略。

  • 场景3:对可扩展性有极端要求
    某自动驾驶公司需在网关层实现“实时传感器数据融合”,要求每毫秒执行自定义C++算法。Envoy的原生Filter支持直接调用CUDA库,这是Higress/Kong无法企及的。

6. 避坑指南:那些没人告诉你的“死亡细节”

6.1 Higress的“版本陷阱”:1.3.x的SSE Bug

Higress 1.3.0-1.3.4存在一个致命Bug:当上游返回Content-Type: text/event-stream但未设置Connection: keep-alive时,Higress会错误地关闭连接,导致SSE重连失败。这个问题在1.3.5修复,但官方Changelog未提及。我们的解决方案是:在Higress前置加一层Nginx,强制添加Connection: keep-alive头。教训是——永远用curl -v测试SSE连接的Connection头,不要只看HTTP状态码。

6.2 Kong的“Lua GC风暴”

Kong 3.4+默认启用LuaJIT GC,但在高并发SSE场景下,GC频率过高会导致请求延迟毛刺。我们通过kong.conf设置:

lua_shared_dict kong_db_cache 128m lua_socket_pool_size 2048 lua_gc_pause 200 # 增加GC间隔

并将所有Lua插件改为resty.http异步调用,避免阻塞Worker。关键经验:Kong的性能瓶颈从来不在CPU,而在Lua内存管理。

6.3 Envoy的“连接池雪崩”

Envoy的cluster配置中,circuit_breakers默认只限制连接数,不限制请求数。当LLM服务返回503时,Envoy会不断重试,迅速耗尽上游连接池。必须显式配置:

circuit_breakers: thresholds: - priority: DEFAULT max_connections: 1000 max_pending_requests: 1000 max_requests: 1000 # 关键!限制请求数 max_retries: 3

否则在模型服务OOM时,Envoy会成为DDoS放大器。

6.4 三者共有的“TLS握手陷阱”

所有网关在HTTPS场景下,若未正确配置TLS Session Resumption,会导致LLM长连接频繁重握手。实测数据显示:未启用Session Ticket时,SSE连接每30秒重握手一次,增加平均延迟120ms。解决方案:

  • Higress:在ssl_certificate指令后添加ssl_session_timeout 4h; ssl_session_cache shared:SSL:10m;
  • Kong:在nginx_kong.conf中设置ssl_session_cache shared:SSL:10m; ssl_session_timeout 4h;
  • Envoy:在transport_socket中配置session_ticket_key和session_timeout

最后分享一个血泪技巧:在AI网关选型前,务必用真实业务请求做“压力探针测试”。不要只测curl -X POST https://api.example.com/v1/chat/completions,而要模拟前端真实行为——用fetch发起SSE连接,发送含stream: true的JSON,持续接收100个token。很多网关在单次请求测试中表现完美,但在长连接流式场景下原形毕露。我们曾因此淘汰了两个看似参数亮眼的候选方案。