使用 Java 通过 HTTP 流式传输数据
Streaming data over HTTP with Java
我有两个 Java Spring 启动应用程序,我们将它们命名为 Source 和 Consumer。源有一个 GET 端点 /api/data
,returns 无限数据流。这个想法是从 Consumer 调用它并每隔几秒侦听一次数据块,然后 "never" 关闭此连接。我已经制作了一个看起来现在可以使用的简单源代码:
@RestController
@RequestMapping ("/api")
public class SourceController {
private final Logger logger = LoggerFactory.getLogger(SourceController.class);
@GetMapping (value = "/data", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<StreamingResponseBody> data(final HttpServletResponse response) {
response.setContentType("application/json");
StreamingResponseBody stream = out -> {
try {
for (int i = 0; i < 10; i++) {
String content = "{\"counter\":" + i + "}\n";
out.write(content.getBytes());
logger.info("size: " + content.getBytes().length);
Thread.sleep(1000);
}
out.close();
} catch (final Exception e) {
logger.error("Exception", e);
}
};
logger.info("steaming response {} ", stream);
return new ResponseEntity(stream, HttpStatus.OK);
}
}
我不确定它是否正是我想要的,因为当我用 Postman 调用它时,响应在 return
执行后 10 秒后出现。
消费者阅读但阅读了整个响应,而不是一段一段地阅读。
@RestController
@RequestMapping("/api")
public class ConsumerController {
private final Logger logger = LoggerFactory.getLogger(ConsumerController.class);
@GetMapping(value = "/consume", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> consume() throws IOException {
URL url = new URL("http://localhost:8080/api/data");
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream())
);
String decodedString;
while ((decodedString = in.readLine()) != null) {
logger.info(decodedString);
}
in.close();
return new ResponseEntity("ok", HttpStatus.OK);
}
}
源看起来是正确的,但你只是忘记在向它写入一大块数据后刷新 OutputStream
,所以消费者无法立即接收到这块数据。
所以在向 OutputStream
写入一些内容后调用 flush()
应该可以解决问题:
for (int i = 0; i < 10; i++) {
String content = "{\"counter\":" + i + "}\n";
out.write(content.getBytes());
out.flush();
logger.info("size: " + content.getBytes().length);
Thread.sleep(1000);
}
我有两个 Java Spring 启动应用程序,我们将它们命名为 Source 和 Consumer。源有一个 GET 端点 /api/data
,returns 无限数据流。这个想法是从 Consumer 调用它并每隔几秒侦听一次数据块,然后 "never" 关闭此连接。我已经制作了一个看起来现在可以使用的简单源代码:
@RestController
@RequestMapping ("/api")
public class SourceController {
private final Logger logger = LoggerFactory.getLogger(SourceController.class);
@GetMapping (value = "/data", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<StreamingResponseBody> data(final HttpServletResponse response) {
response.setContentType("application/json");
StreamingResponseBody stream = out -> {
try {
for (int i = 0; i < 10; i++) {
String content = "{\"counter\":" + i + "}\n";
out.write(content.getBytes());
logger.info("size: " + content.getBytes().length);
Thread.sleep(1000);
}
out.close();
} catch (final Exception e) {
logger.error("Exception", e);
}
};
logger.info("steaming response {} ", stream);
return new ResponseEntity(stream, HttpStatus.OK);
}
}
我不确定它是否正是我想要的,因为当我用 Postman 调用它时,响应在 return
执行后 10 秒后出现。
消费者阅读但阅读了整个响应,而不是一段一段地阅读。
@RestController
@RequestMapping("/api")
public class ConsumerController {
private final Logger logger = LoggerFactory.getLogger(ConsumerController.class);
@GetMapping(value = "/consume", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> consume() throws IOException {
URL url = new URL("http://localhost:8080/api/data");
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream())
);
String decodedString;
while ((decodedString = in.readLine()) != null) {
logger.info(decodedString);
}
in.close();
return new ResponseEntity("ok", HttpStatus.OK);
}
}
源看起来是正确的,但你只是忘记在向它写入一大块数据后刷新 OutputStream
,所以消费者无法立即接收到这块数据。
所以在向 OutputStream
写入一些内容后调用 flush()
应该可以解决问题:
for (int i = 0; i < 10; i++) {
String content = "{\"counter\":" + i + "}\n";
out.write(content.getBytes());
out.flush();
logger.info("size: " + content.getBytes().length);
Thread.sleep(1000);
}