和前端准备对接一个SSE(Server-Sent Events)服务器推送事件 接口,作为后端开发,之前也没对接过这类接口,在没有前端页面时不知道咋自测,后面发现直接在idea内开两个终端Terminal窗口curl就能完成自测了,不用手写一个前端页面。用postman自测也行,文末会讲。

一、后端代码(Java)

1. 接口拆分

分为三个接口:连接、发送信息、断开连接

过程如下:
客户端向服务端发送建立连接请求;
服务端向客户端推送内容;
服务端向客户端发送结束信号并结束

2.注意事项

请求头和响应体要设置一些属性值。

响应头:
设置content-type为text/event-stream,
设置transfer-encoding为chunked;

请求头:
设置accept为text/event-stream;

3.其他问题

服务端打算断开时,是否需要先得到客户端应答再断开?最近在浏览器控制台看了百度翻译的sse流式响应,感觉是每翻译完一个问题就结束了??
补充:sse是单项通道,只能服务器向浏览器推送

4.java代码参考

4.1 controller层

@RestController
public class ChatController {

    @Autowired
    private ChatService chatService;

    // TODO: 2023/10/16 执行顺序
    @CrossOrigin
    @PostMapping(value = "/ask")
    @SneakyThrows
    public void chatGPT(@RequestBody ChatRequest request) {
        chatService.chat(request);// TODO: 2023/10/16 执行顺序 2
    }

    @CrossOrigin
    @GetMapping(value = "/link", produces = "text/event-stream;charset=utf-8")
    @SneakyThrows
    public SseEmitter link() {
        return chatService.link();// TODO: 2023/10/16 执行顺序 1
    }

    @CrossOrigin
    @PostMapping(value = "/stop")
    @SneakyThrows
    public void chatGPT() {
        chatService.stop();// TODO: 2023/10/16 执行顺序 3
    }
}

4.2 请求体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChatRequest {
    private String question;
}

4.3 接口

public interface ChatService {
    void chat(ChatRequest request);

    SseEmitter link();

    void stop();

}

4.4 实现类
@Slf4j
@Service
public class ChatServiceImpl implements ChatService {

    static HashMap<String, SseEmitter> map = new HashMap<>();

    @SneakyThrows
    @Override
    public void chat(ChatRequest request) {
        SseEmitter sseEmitter = map.get("222");
        String question = request.getQuestion();
        char[] chars = question.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            sseEmitter.send(SseEmitter.event().id("111").data(chars[i]));
        }
    }

    @SneakyThrows
    @Override
    public SseEmitter link() {

        SseEmitter sseEmitter = new SseEmitter(0L);//设置超时时间,单位为毫秒
        map.put("222", sseEmitter);

        //  >> 回调1:长链接完成后回调接口(即关闭连接时调用)
        sseEmitter.onCompletion(() -> {
            map.remove("222");
            log.info("连接关闭, userId = {},  sessionId = {}, 时间戳 = {}", null, null, System.currentTimeMillis());// TODO: 2023/10/16 执行顺序 4
        });


        //  >> 回调2:出现异常会调用此方法
        sseEmitter.onError(new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) {
                log.info("连接出错, userId = {},  sessionId = {}, 时间戳 = {}", null, null, System.currentTimeMillis());
                sseEmitter.completeWithError(new RuntimeException("SSE 超时了"));
            }
        });


        //  >> 回调3:出现连接超时,会调用此方法
        sseEmitter.onTimeout(() -> {
            log.info("连接超时, userId = {},  sessionId = {}, 时间戳 = {}", null, null , System.currentTimeMillis());
            sseEmitter.completeWithError(new RuntimeException("SSE 超时了"));
        });

        sseEmitter.send(SseEmitter.event().data("操作成功"));
        return sseEmitter;
    }

    @SneakyThrows
    @Override
    public void stop() {
        SseEmitter sseEmitter = map.get("222");
        sseEmitter.send(SseEmitter.event().id("111").data("中断"));
        sseEmitter.complete();
    }
}

二、在终端通过CURL测试

1. 在idea打开终端Terminal

使用idea的终端窗口:
在这里插入图片描述

2. 示例接口的curl报文

2.1 请求连接的curl
curl -H "Accept: text/event-stream" http://localhost:9033/link

得到应答:
在这里插入图片描述

2.2 提问接口的curl(这个演示的是将输入的文字推送出来):
curl -X POST -H "Content-Type: application/json" -d "{\"question\": \"hhhhh\"}" http://localhost:9033/ask

提问后得到的响应:
在这里插入图片描述

2.3 mock服务主动断开的curl:
curl -X POST http://localhost:9033/stop

客户端收到的:
在这里插入图片描述

三、通过Postman测试

两个注意事项:
1.笔者一开始以为postman不支持测试SSE,后来发现是自己发送SSE请求时没有设置Accept: text/event-stream的请求头,导致发送不成功。
2.SSE的流式响应只会在link接口的postman窗口出现。用本文示例接口举例(link/ask/stop接口),postman先调link接口,再调ask接口,调完ask接口后,需要回到link接口的窗口下才能看到流式响应。因为端点在link接口那,在ask接口的窗口下是看不到的。

Postman 11+ 版本支持 SSE:

在 Postman 11 及以上版本中,可以直接测试 SSE 连接

使用方法:

创建新请求

选择请求方法为 GET

在地址栏输入 SSE 端点 URL

在 Headers 中添加 Accept: text/event-stream

发送请求后会保持连接并实时显示接收到的消息
在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐