text/event-stream

简介

GPT火了之后,一种新的Http MediaType慢慢火了起来,它就是text/event-stream,如果你对这个陌生,一定对他的兄弟比较熟悉:application/octet-stream。这几种类型本质上都是客户端与服务端打开了一个长连接,服务端可以多次写入一部分数据给客户端,客户端可以多次读取,直到全部读取完成。
由于ChatGPT的特性,如果需要生成的token较多,等它全部生成完成将消耗较多的时间,但是如果你将它生成过程中的数据源源不断地展示给用户,那么用户端的体验也不会差(类似于在线播放视频,不需要把整个视频下载完成才能播放)
text/event-stream支持服务端分多次往客户端写内容。

使用方法

利用spring mvc的SseEmitter提供服务接口

@PostMapping(value = "/test-stream", produces = "text/event-stream")
	public SseEmitter conversation(@RequestBody ChatRequest request) {
		final SseEmitter emitter = new SseEmitter();
		new Thread(() -> {
			try {
				for (int i = 0; i < 10; i++) {
					// 模拟某些耗时操作
					Thread.sleep(200L);
					emitter.send("这是第" + i +"次往服务端发送内容");
				}
			} finally {
				emitter.complete();
			}
		}).start();
		return emitter;
	}

客户端如何读取text/event-stream的接口呢?对于客户端来说,无论什么协议,都是用InputStream去读取数据,需要注意的是,这种协议每次都会在send的数据前面加上data: ,每次send后都会发送一次空行数据。

URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);

// 写入请求参数
OutputStream os = conn.getOutputStream();
os.write("request body".getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();

//读取响应参数
try (InputStream is = conn.getInputStream();) {
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
while ((line = reader.readLine()) != null) {
    if (StringUtils.isBlank(line)) {
        continue;
    }
    if (line.startsWith("data:")) {
        line = line.substring("data:".length());
        // 处理数据
    }
}

超时时间设置

默认情况下,tomcat对于AsyncRequest会设置默认30秒的超时时间,如果你的异步请求耗时较大,会抛出AsyncRequestTimeoutException,可以通过以下方式解决:

@Component
public class MyWebMvcConfig implements WebMvcConfigurer {
	@Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(-1L);
    }
}

创建SseEmitter的时候,通过构造函数指定超时时间(可选)。

final SseEmitter emitter = new SseEmitter(120 * 1000L);

你可能感兴趣的:(#,Java基础,java)