在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容 ,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate
缓存机制会导致数据必须等待全部接收完毕后再处理,违背了流式传输的初衷。
本文将介绍如何在 Spring Boot 2.7.x 中使用 RestTemplate
实现一个流式响应的中转接口。
一、关键点说明
-
禁用请求缓冲:
通过
SimpleClientHttpRequestFactory#setBufferRequestBody(false)
禁用缓冲,确保流式传输生效。 -
设置响应为 SSE 格式:
设置
HttpServletResponse
的响应头为text/event-stream
,支持前端基于 EventSource 的实时响应。 -
使用 RestTemplate.execute 方法:
通过
RestTemplate.execute()
方法自定义RequestCallback
和ResponseExtractor
实现对输入输出流的精细控制。
二、完整代码实现
/**
* 处理流式响应的HTTP请求方法(流式响应, Spring Boot 2.7.x 兼容)
*
* @param requestBody 请求体内容
* @param url 请求URL
* @param httpMethod 请求方法
* @param response HttpServletResponse,用于直接返回流数据
*/
public static void executeStreamingRequest(
Map<String, Object> requestBody,
String url,
HttpMethod httpMethod,
HttpServletResponse response) throws IOException {
// 1. 配置RestTemplate(启用流式)
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false); // 关键:禁用请求缓冲
requestFactory.setConnectTimeout(30000);
requestFactory.setReadTimeout(60000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// 2. 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 3. 执行流式请求
try {
log.info("【开始流式请求】URL: {}", url);
restTemplate.execute(
URI.create(url),
httpMethod,
new RequestCallback() {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getHeaders().putAll(headers);
if (requestBody != null) {
new ObjectMapper().writeValue(request.getBody(), requestBody);
}
}
},
new ResponseExtractor<Void>() {
@Override
public Void extractData(ClientHttpResponse clientResponse) throws IOException {
// 设置响应头
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
// 流式传输
try (InputStream is = clientResponse.getBody();
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
os.flush(); // 立即刷新
}
}
return null;
}
}
);
} catch (RestClientException e) {
log.error("流式请求失败", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("服务调用失败: " + e.getMessage());
}
}
三、使用场景举例
-
转发 OpenAI 的流式 API 响应
-
转发后端 AI 推理接口的逐步响应
-
转发日志、进度等后台任务推送数据
四、注意事项
-
保证上下游接口支持长连接和流式传输
-
防止浏览器缓冲影响效果,前端建议使用
EventSource
或fetch + reader
模式消费 -
若上游响应为分块传输(chunked),务必确保 header 中包含
Transfer-Encoding: chunked