Bootstrap

java返回事件流给前端

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>
;