【人工智能】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;
		}
    }  

通过浏览器访问接口 。发送数据到浏览器

这段Java代码实现了一个调用问答AI接口并进行流式输出的功能。以下是代码的详细描述: 主要功能 通过HTTP POST请求调用一个问答AI接口 使用流式传输模式(stream=true)获取响应 将响应内容实时输出到控制台 代码结构 内部类定义 Message类:表示对话中的一条消息,包含role(角色,如"system"或"user")和content(内容)字段 RequestBody类:表示请求体,包含: model:使用的模型名称("qwen-plus") messages:消息数组 stream:是否使用流式传输(设置为true) 主流程 准备请求数据: 创建包含系统提示和用户问题的消息数组 设置使用"qwen-plus"模型 启用流式传输(stream=true) 发送请求: 将请求体转换为JSON格式 创建HTTP连接并设置请求头: Content-Type: application/json Accept: application/json Authorization: Bearer token 发送POST请求 处理响应: 读取响应码并打印 以流式方式读取响应内容,逐行打印到控制台 同时将响应内容收集到StringBuilder中 特点 流式传输:由于设置了stream=true,服务器会以流式方式返回响应,允许客户端逐步接收和处理数据,而不是等待完整响应。 实时输出:代码在接收到每一行响应后立即打印到控制台(System.out.println),实现了实时显示的效果。 错误处理:使用try-catch捕获并打印异常。 认证:使用Bearer Token进行API认证(代码中隐藏了实际密钥)。 使用场景 这段代码适用于需要: 与AI模型进行对话交互 实时获取模型生成的响应
### Java 中使用 `SseEmitter` 实现 SSE 流式响应 #### 创建 Spring Boot 应用程序并配置依赖项 为了实现 Server-Sent Events (SSE),首先需要确保项目中包含了必要的依赖项。对于 Maven 项目,在 `pom.xml` 文件中添加如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` #### 定义控制器方法来处理 SSE 请求 下面是一个完整的例子,展示了如何定义一个 REST 控制器来处理来自客户端的 SSE 连接请求,并通过 `SseEmitter` 向其发送消息。 ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.Map; @RestController public class SseController { private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>(); @GetMapping("/connect/{userId}") public SseEmitter connect(@PathVariable String userId) { SseEmitter emitter = new SseEmitter(); emitters.put(userId, emitter); // 设置超时时间(可选) emitter.onCompletion(() -> emitters.remove(userId)); emitter.onError((e) -> emitters.remove(userId)); return emitter; } @GetMapping("/sendMessage/{userId}/{message}") public void sendMessage(@PathVariable String userId, @PathVariable String message) throws IOException { SseEmitter sseEmitter = emitters.get(userId); if (sseEmitter != null && !emitters.containsKey(userId)) { sseEmitter.send(SseEmitter.event() .name("newMessage") // 自定义事件名称 .data(message)); // 发送的数据内容 } } } ``` 此代码片段实现了两个主要功能:一是建立连接;二是向特定用户发送消息[^1]。 #### 前端 JavaScript 部分 在前端页面上可以通过原生 JavaScript 或者 jQuery 来监听服务端发出的消息。这里给出一段简单的 HTML 和 JS 代码作为示例: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SSE Example</title> <script type="text/javascript"> if (!!window.EventSource) { var source = new EventSource('/connect/userId'); source.addEventListener('open', function(e) { console.log("Connection was opened."); }, false); source.addEventListener('error', function(e) { if (e.target.readyState == EventSource.CLOSED) { console.error("EventSource failed."); } }, false); source.addEventListener('newMessage', function(e) { console.info("New message:", e.data); }, false); } else { console.warn("Your browser doesn't support SSE"); } </script> </head> <body> </body> </html> ``` 这段脚本会尝试订阅 `/connect/userId` 路径下的 SSE 数据流,并针对不同类型的事件做出相应的反应[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋力向前123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值