【人工智能】java流式返回数据接口
前言
最近由于需要接入deepseek,而且使用的java的后端框架,平时的接口都是返回实体数据,使用deepseek由于模型在思考的过程中比较耗时,有使用流输出和非流输出两种方式。当然本节主要讲通过流式输出。
使用SseEmitter实体框架
SseEmitter是Spring Framework提供的一个类,用于支持Server-Sent Events(SSE)。SSE是一种基于HTTP的协议,允许服务器向客户端推送实时数据,而不需要客户端不断地轮询服务器。SSE特别适用于需要实时更新数据的场景,例如实时通知、实时数据流等
SseEmitter的主要特点
单向通信:SSE是单向的,服务器可以向客户端推送数据,但客户端不能向服务器发送数据。
基于HTTP:使用标准的HTTP协议,不需要额外的协议支持。
自动重连:如果连接中断,客户端会自动尝试重新连接服务器。
事件流格式:使用简单的文本格式传输数据,每条消息以"data:"开头,并以两个换行符"\n\n"结束
SseEmitter的使用方法
创建SseEmitter实例:在控制器中创建一个SseEmitter实例,并将其返回给客户端。
发送事件:通过SseEmitter实例的send方法向客户端发送事件。
处理连接关闭:通过SseEmitter的onCompletion和onTimeout方法处理连接关闭或超时的情况
示例代码
以下是一个简单的Spring Boot示例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RestController
public class SseController {
private final ExecutorService nonBlockingService = Executors.newCachedThreadPool();
@GetMapping("/sse")
public SseEmitter handleRequest() {
SseEmitter emitter = new SseEmitter();
nonBlockingService.submit(() -> {
try {
// 模拟数据发送逻辑
while (true) {
// 发送数据到客户端
emitter.send(SseEmitter.event().name("message").data("Hello, World!"));
Thread.sleep(1000); // 每秒发送一次数据
}
} catch (Exception e) {
emitter.completeWithError(e);
} finally {
emitter.complete(); // 完成发送后关闭连接
}
});
return emitter;
}
}
这里强调的是 通过这种方式只能用get的请求方式,我在实验中测试用post方法测试好像不行。
如果web服务器使用的是nginx,还要配合nginx配置,如果不配置,就无法通过流的方式输出到客户端
#反向代理
location /deepseek/ {
# 设置 Nginx 不对 SSE 响应进行缓冲,直接透传给客户端
proxy_buffering off;
# 设置代理读取服务器响应的超时时间
proxy_read_timeout 24h;
# 设置客户端连接的超时时间
proxy_connect_timeout 1h;
# 设置 HTTP 版本,SSE 需要 HTTP/1.1
proxy_http_version 1.1;
# 保持连接活性,不发送连接关闭的信号
proxy_set_header Connection '';
# 配置代理传递的头部,确保 Host 头部正确传递
proxy_set_header Host $host;
# 配置代理的后端服务器地址
proxy_pass http:服务地址/streamChat;
# 设置代理的响应头部,保持传输编码为 chunked
proxy_set_header X-Accel-Buffering no;
# 设置跨域资源共享 (CORS),如果你的客户端和服务器不在同一个域上
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin,Authorization,Accept,X-Requested-With' always;
if ($request_method = 'OPTIONS') {
# 如果请求方法为 OPTIONS,则返回 204 (无内容)
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin,Authorization,Accept,X-Requested-With';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
通过浏览器访问接口 。发送数据到浏览器