Hi I’m Shendi
最近在编写SSE接口遇到了缓存问题,导致实时性失效,在这里记录一下。
问题
环境:SpringBoot+Nginx+AnythingLLM
SSE接口调用拥有缓存,导致数据被缓存后才返回给前端。
排查
对SpringBoot接口的检查:
检查接口代码,直接使用 resp.OutputStream
或者使用 SseEmitter
皆有此问题
检查接口中对AnythingLLM调用的代码,发现使用BufferedReader.readLine,将其直接改为InputStream.read判断\n,效果的确好了一点,但依然有此问题。
编写测试代码,看是否是接口问题:
@Anonymous
@GetMapping(value = "/test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public void test(HttpServletResponse resp) throws Exception {
resp.setContentType("text/event-stream;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
// 禁止压缩,防止流式被缓存
resp.setHeader("Content-Encoding", "identity");
resp.setHeader("Cache-Control", "no-cache");
OutputStream output = resp.getOutputStream();
for (int i = 0; i < 20; i++) {
output.write(("data: " + RespCode.toResp(RespCode.PARAM_NULL) + "\n\n").getBytes());
output.flush();
Thread.sleep(2000);
}
}
测试发现无问题,前端稳定的两秒收到数据(皆使用Nginx反向代理)
对 AnythingLLM的检查:
直接调用接口,发现问题复现,然后猜测是否Nginx的问题,不使用代理访问,一切正常。
解决
最终的问题就是Nginx的问题,Nginx默认会将数据进行缓存,要进行相应配置,并且SSE接口就那么几个,所以可以使用rewrite
对单独的几个接口关闭
location /llm/ {
rewrite ^/llm/(.*)$ /api/$1 break;
# 原有host 与 ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# 支持websocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:3001;
}
location ~ ^/llm/v1/workspace/[^/]+/thread/[^/]+/stream-chat$ {
rewrite ^/llm/(.*)$ /api/$1 break;
proxy_pass http://127.0.0.1:3001;
# 关闭缓冲,确保实时转发
proxy_buffering off;
proxy_cache off;
# 设置 HTTP/1.1 以保持长连接
proxy_http_version 1.1;
proxy_set_header Connection '';
# 防止 gzip 压缩,避免 chunked 被打包
gzip off;
# 增加超时时间,避免 SSE 长连接被断开
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# 如果后端是基于 Host 做路由的,保留原 Host
proxy_set_header Host $host;
# 转发其他头信息
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
END