不写前端代码,curl直接开两个终端调试sse
过程:
客户端向服务端发送建立连接请求;
服务端向客户端推送内容;
服务端向客户端发送结束信号并结束
注意事项:
只有连接时要求content-type是xxx
其他问题:
服务端打算断开时,是否需要先得到客户端应答再断开?
java代码参考:
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
}
}
请求体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChatRequest {
private String question;
}
接口
public interface ChatService {
void chat(ChatRequest request);
SseEmitter link();
void stop();
}
实现类
@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验证效果
- 请求连接的curl
curl -H "Accept: text/event-stream" http://localhost:9033/link
得到应答:
- 提问的curl(这个演示的是将输入的文字推送出来):
curl -X POST -H "Content-Type: application/json" -d "{\"question\": \"hhhhh\"}" http://localhost:9033/ask
提问后得到的响应:
- mock服务主动断开的curl:
curl -X POST http://localhost:9033/stop
客户端收到的: