You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SSE接口本地正常远程一次性返回所有数据问题求助

SSE接口本地正常、远程批量返回的问题排查与解决

问题描述

编写的SSE测试接口本地调用时每秒返回一条消息,但部署到远程云服务器后,接口会等待所有消息生成完毕一次性返回。

接口代码

后端SSE接口实现:

public SseEmitter testStream(String question) {
        SseEmitter emitter = new SseEmitter();

        // 启动异步处理
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    // 生成事件数据
                    String eventData = "Event data " + i;

                    // 创建Server-Sent Event对象
                    ServerSentEvent event = ServerSentEvent.builder()
                            .event("message")
                            .data(eventData)
                            .build();

                    // 序列化事件并发送给客户端
                    String serializedEvent = JSON.toJSONString(event);
                    emitter.send(serializedEvent);

                    // 等待1秒后发送下一条事件
                    Thread.sleep(1000);
                }

                // 结束SSE流
                emitter.complete();
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();

        return emitter;
    }

REST接口暴露:

@GetMapping(path="/test",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
SseEmitter testStream(@RequestParam(required = true) String question);

测试命令

# 本地测试
curl -X GET -H 'Content-Type: application/json' -N http://localhost:11014/ai/stream/chat/test\?question\=1
# 远程测试
curl -X GET -H 'Content-Type: application/json' -H 'x-request-id:1'  -H 'x-access-token: eyJhbGciOiJIUzUx.balabala.sDM53g' -N https://ai.example.top/ai/stream/chat/test\?question\=1

请求链路

客户端用户→DNS→Nginx→Kubernetes Traefik→Kubernetes内的Spring Gateway→Kubernetes Service→Kubernetes Pod

已尝试的配置:

  • 请求头添加Cache-Control:no-cache
  • 响应头设置text/event-stream、Cache-Control:no-cache、Connection:keep-alive
  • Nginx配置proxy_cache_bypass $http_pragma

解决方案

问题核心是链路中各代理层的缓冲区/缓存机制拦截了实时推送,需逐个环节禁用缓冲:

1. Nginx配置调整

针对/ai/stream路径强制禁用代理缓冲,确保实时转发响应:

location ^~ /ai/stream {
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8000;
    # 禁用代理缓冲,实时转发每段响应
    proxy_buffering off;
    # 完全禁用缓存
    proxy_cache off;
    # 设置长连接超时,适配SSE长时间连接
    proxy_read_timeout 3600;
    # 传递长连接相关头
    proxy_set_header Connection '';
    chunked_transfer_encoding off;
}

说明:Nginx默认会缓冲后端响应,直到缓冲区满或响应结束才发送给客户端,proxy_buffering off强制实时推送每一段数据。

2. Kubernetes Traefik配置调整

通过Ingress注解禁用Traefik的响应缓冲:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ai-ingress
  annotations:
    # 禁用响应缓冲
    traefik.ingress.kubernetes.io/response-buffering: "false"
    # 设置长连接超时
    traefik.ingress.kubernetes.io/proxy-read-timeout: "3600"
spec:
  rules:
  - host: ai.example.top
    http:
      paths:
      - path: /ai/stream
        pathType: Prefix
        backend:
          service:
            name: ai-service
            port:
              number: 80

3. Spring Gateway配置调整

在网关路由中禁用响应缓冲,并配置长连接:

spring:
  cloud:
    gateway:
      routes:
      - id: ai-stream-route
        uri: lb://ai-service
        predicates:
        - Path=/ai/stream/**
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_FIRST
      httpclient:
        # 禁用响应缓冲
        response-timeout: 3600000
        connect-timeout: 10000
        pool:
          max-idle-time: 300000

也可通过自定义过滤器强制开启分块传输:

@Component
public class DisableBufferingFilter implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getResponse().getHeaders().set("Cache-Control", "no-cache, no-store, must-revalidate");
        exchange.getResponse().getHeaders().set("Pragma", "no-cache");
        exchange.getResponse().getHeaders().set("Expires", "0");
        // 开启分块传输,禁用缓冲
        exchange.getResponse().setChunked(true);
        return chain.filter(exchange);
    }
}

4. 后端SSE接口优化

使用Spring提供的标准SSE发送方法,避免自定义序列化可能导致的格式问题:

// 替换原序列化代码,直接使用SseEmitter的事件构造器
emitter.send(SseEmitter.event()
        .name("message")
        .data(eventData));

说明:Spring会自动生成符合SSE规范的格式(包含event:、data:字段及换行分隔符),确保代理层正确识别分段响应。


内容的提问来源于stack exchange,提问作者Dolphin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:54:54