java自定义事件流返回给前端
Controller
@GetMapping(value = "/deleteAllFilesSse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@ApiOperation("一键删除流式返回")
public void deleteAllFilesSse(@RequestParam(value = "userId", required = false) String userId,
HttpServletResponse response) throws Exception {
auditInspectionQAModelService.deleteAllFilesSse(userId, response);
}
Service
@Override
public void deleteAllFilesSse(String userId, HttpServletResponse response) throws Exception {
// 设置ContentType为"text/event-stream"
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
// 使用PrintWriter来发送事件
PrintWriter writer = response.getWriter();
// 每个事件由注释分隔
writer.write("event:message\n");
// 事件的数据字段,可为json字符串,必须以\n\n结尾
writer.write("data:" + {\"message\":\"Hello, world!\"} + "\n\n");
// 刷新输出缓冲区
writer.flush();
// 模拟休眠
try {
Thread.sleep(1000); // 每秒发送一次事件
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
java接收事件流转发给前端
@ApiOperation("聊天")
@GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public void chat(@RequestParam("query") String query,
@RequestParam(value = "userId", required = false) String userId,
HttpServletResponse rp) throws Exception {
ChatDto chatDto = new ChatDto();
chatDto.setQuery(query);
chatDto.setUserId(userId);
try {
auditInspectionQAModelService.chat(chatDto, rp);
} catch (Exception exception) {
log.error("发起会话请求失败", exception);
throw new AdmsException("发起会话请求失败");
}
}
通过OkHttp实现事件流
private void sendPostSseRequest(String chatId, Boolean isAnswer, Boolean isSaveMsgFiles,
String url,
Map<String, Object> paramMap,
HttpServletResponse rp) throws Exception {
log.info("chat url: {}", url);
String json = JSON.toJSONString(paramMap);
log.info("chat paramMap: {}", json);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), json);
// 请求对象
Request request = new Request.Builder()
.url(url)
.post(body)
.addHeader(key_authorization, apiKey)
.build();
// 自定义监听器
SSEListener sseListener= new SSEListener(chatId, rp, isAnswer, isSaveMsgFiles);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.MINUTES);
//跳过SSL验证
if (isSkipSSLValid) {
builder.sslSocketFactory(getSSLSocketFactory(), getX509TrustManager())
.hostnameVerifier(getHostnameVerifier());
}
OkHttpClient client = builder.build();
EventSource.Factory factory = EventSources.createFactory(client);
// 创建事件
EventSource eventSource = factory.newEventSource(request, sseListener);
sseListener.getCountDownLatch().await();
}
自定义SSEListener
package com.cmft.service.bigModel;
import com.alibaba.fastjson.JSONObject;
import com.cmft.entity.bigModel.AdmBigModeChatFileRel;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
@Component
@Slf4j
public class SSEListener extends EventSourceListener {
private CountDownLatch countDownLatch = new CountDownLatch(1);
private HttpServletResponse rp;
private StringBuffer output = new StringBuffer();
public SSEListener() {}
public SSEListener(HttpServletResponse response) {
this.rp = response;
}
@Override
public void onOpen(EventSource eventSource, Response response) {
if (rp != null) {
rp.setContentType("text/event-stream");
rp.setCharacterEncoding("UTF-8");
rp.setStatus(200);
log.info("建立sse连接...");
} else {
log.info("客户端非sse推送");
}
}
@Override
public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
try {
output.append(data);
if (rp != null) {
if ("\n".equals(data)) {
rp.getWriter().write("event:" + type + "\n");
//rp.getWriter().write("id:" + chatGlmDto.getMessageId() + "\n");
rp.getWriter().write("data:\n\n");
rp.getWriter().flush();
} else {
String[] dataArr = data.split("\\n");
for (int i = 0; i < dataArr.length; i++) {
if (i == 0) {
rp.getWriter().write("event:" + type + "\n");
//rp.getWriter().write("id:" + chatGlmDto.getMessageId() + "\n");
}
if (i == dataArr.length - 1) {
rp.getWriter().write("data:" + dataArr[i] + "\n\n");
rp.getWriter().flush();
} else {
rp.getWriter().write("data:" + dataArr[i] + "\n");
rp.getWriter().flush();
}
}
}
}
} catch (Exception e) {
log.error("消息错误:", e);
countDownLatch.countDown();
throw new RuntimeException(e);
}
}
@Override
public void onClosed(EventSource eventSource) {
log.info("sse连接关闭");
log.info("结果输出:{}", output.toString());
countDownLatch.countDown();
}
@Override
public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
log.error("使用事件源时出现异常... {}", t.getMessage());
countDownLatch.countDown();
}
public CountDownLatch getCountDownLatch() {
return this.countDownLatch;
}
}
pom.xml需要加入的依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>3.14.9</version>
</dependency>