proxy-buffering
proxy-buffering 插件为指定路由禁用 NGINX proxy_buffering 指令。这样,上游逐步发送的响应可以立即到达客户端,无需在代理缓冲区中等待。
此行 为适用于服务器发送事件(SSE)、Watch API,以及其他发送增量或分块响应的上游服务。
示例
比较缓冲与增量传输
以下示例比较 NGINX 的默认响应缓冲行为与通过 proxy-buffering 禁用缓冲的路由。测试上游声明固定的响应长度,并每隔两秒发送一行,共发送三行。启用缓冲的路由会一次性传输全部三行,而禁用缓冲的路由会在每行到达时立即传输。
为流式上游创建以下 Python 脚本:
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import time
class Handler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_GET(self):
chunks = [b"one\n", b"two\n", b"three\n"]
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(sum(map(len, chunks))))
self.send_header("Connection", "close")
self.end_headers()
for chunk in chunks:
self.wfile.write(chunk)
self.wfile.flush()
time.sleep(2)
self.close_connection = True
def log_message(self, *_):
pass
ThreadingHTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
在网关使用的环境中启动上游:
- Docker
- Kubernetes
将 GATEWAY_CONTAINER 设置为正在运行的 APISIX 或 API7 Gateway 容器名称。创建专用网络并将网关连接到该网络:
export GATEWAY_CONTAINER=replace-with-gateway-container-name
docker network create gateway-stream-net
docker network connect gateway-stream-net "$GATEWAY_CONTAINER"
在包含 stream-server.py 的目录中启动流式服务器:
docker run -d \
--name stream-server \
--network gateway-stream-net \
-v "$PWD/stream-server.py:/stream-server.py:ro" \
python:3.13.15-alpine3.24 \
python -u /stream-server.py
创建流式服务器的配置、Deployment 和 Service:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: aic
name: stream-server
data:
stream-server.py: |
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import time
class Handler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_GET(self):
chunks = [b"one\n", b"two\n", b"three\n"]
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(sum(map(len, chunks))))
self.send_header("Connection", "close")
self.end_headers()
for chunk in chunks:
self.wfile.write(chunk)
self.wfile.flush()
time.sleep(2)
self.close_connection = True
def log_message(self, *_):
pass
ThreadingHTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: stream-server
spec:
replicas: 1
selector:
matchLabels:
app: stream-server
template:
metadata:
labels:
app: stream-server
spec:
containers:
- name: stream-server
image: python:3.13.15-alpine3.24
command:
- python
- -u
- /stream-server.py
ports:
- name: http
containerPort: 8000
readinessProbe:
tcpSocket:
port: http
volumeMounts:
- name: script
mountPath: /stream-server.py
subPath: stream-server.py
volumes:
- name: script
configMap:
name: stream-server
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: stream-server
spec:
selector:
app: stream-server
ports:
- name: http
port: 8000
targetPort: http
应用清单并等待 Deployment 就绪:
kubectl apply -f stream-server.yaml
kubectl rollout status deployment/stream-server -n aic
创建两条指向同一上游的路由。第一条路由使用 NGINX 默认缓冲行为,第二条路由通过 proxy-buffering 禁用缓冲:
- APISIX Admin API
- ADC
- Ingress Controller(Kubernetes)
创建不使用 proxy-buffering 的基准路由:
curl "http://127.0.0.1:9180/apisix/admin/routes/proxy-buffering-on" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/buffered",
"upstream": {
"type": "roundrobin",
"nodes": {
"stream-server:8000": 1
}
}
}'
创建禁用缓冲的对比路由:
curl "http://127.0.0.1:9180/apisix/admin/routes/proxy-buffering-off" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/unbuffered",
"plugins": {
"proxy-buffering": {
"disable_proxy_buffering": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"stream-server:8000": 1
}
}
}'
创建一个服务,其中包含一条基准路由和一条禁用缓冲的路由:
services:
- name: proxy-buffering-service
labels:
docs-example: proxy-buffering
routes:
- name: proxy-buffering-on
uris:
- /buffered
- name: proxy-buffering-off
uris:
- /unbuffered
plugins:
proxy-buffering:
disable_proxy_buffering: true
upstream:
type: roundrobin
nodes:
- host: stream-server
port: 8000
weight: 1
预览此示例的变更:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=proxy-buffering
检查差异,然后仅同步带有相应标签的服务:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=proxy-buffering
- Gateway API
- APISIX CRD
创建插件配置和路由,并为每个路径配置一条规则:
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: proxy-buffering-off
spec:
plugins:
- name: proxy-buffering
config:
disable_proxy_buffering: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: proxy-buffering
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /buffered
backendRefs:
- name: stream-server
port: 8000
- matches:
- path:
type: Exact
value: /unbuffered
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: proxy-buffering-off
backendRefs:
- name: stream-server
port: 8000
创建一个 ApisixRoute,其中包含一条基准路由和一条禁用缓冲的路由:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: proxy-buffering
spec:
ingressClassName: apisix
http:
- name: proxy-buffering-on
match:
paths:
- /buffered
upstreams:
- serviceName: stream-server
servicePort: 8000
- name: proxy-buffering-off
match:
paths:
- /unbuffered
upstreams:
- serviceName: stream-server
servicePort: 8000
plugins:
- name: proxy-buffering
config:
disable_proxy_buffering: true
应用 Ingress Controller 配置:
kubectl apply -f proxy-buffering-ic.yaml
创建一个 Python 客户端,用于记录每一行响应可用的时间:
import time
import urllib.request
for path in ("buffered", "unbuffered"):
start = time.monotonic()
print(path)
with urllib.request.urlopen(f"http://127.0.0.1:9080/{path}") as response:
for line in response:
elapsed = time.monotonic() - start
print(f"{elapsed:.1f}s {line.decode().strip()}")
运行客户端:
python stream-client.py
基准路由应在约四秒后一次性返回全部三行。使用 proxy-buffering 的路由则应在上游写入每一行时立即返回:
buffered
4.0s one
4.0s two
4.0s three
unbuffered
0.0s one
2.0s two
4.0s three
此差异表明,proxy-buffering 已为配置的路由禁用 NGINX 响应缓冲。