java - 播放框架:JsonNode 流/分块

我有一个大的 map 想要返回到前端。最初我将 map 转换为 jackson json 节点并使用 play 提供的 return ok() 方法将 map 返回给用户。

原始代码:

public Result returnResponse() {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> returnMap = populateMapWithData();
    JsonNode response = mapper.valueToTree(returnMap);
    return ok(response);
}

由于 map 可能非常大,我遇到了内存问题。

在查看 play framework 文档时,有两种方法可以将大数据返回到前端。如果大小已知,我可以将数据流回给用户。如果大小未知,我可以分块提供数据。

播放框架文档:https://www.playframework.com/documentation/2.8.x/JavaStream

对于流式传输:

public Result index() {
  java.io.File file = new java.io.File("/tmp/fileToServe.pdf");
  java.nio.file.Path path = file.toPath();
  Source<ByteString, ?> source = FileIO.fromPath(path);

  Optional<Long> contentLength = null;
  try {
    contentLength = Optional.of(Files.size(path));
  } catch (IOException ioe) {
    throw new RuntimeException(ioe);
  }

  return new Result(
      new ResponseHeader(200, Collections.emptyMap()),
      new HttpEntity.Streamed(source, contentLength, Optional.of("text/plain")));
}

对于分块:

public Result index() {
  // Prepare a chunked text stream
  Source<ByteString, ?> source =
      Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
          .mapMaterializedValue(
              sourceActor -> {
                sourceActor.tell(ByteString.fromString("kiki"), null);
                sourceActor.tell(ByteString.fromString("foo"), null);
                sourceActor.tell(ByteString.fromString("bar"), null);
                sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
                return NotUsed.getInstance();
              });
  // Serves this stream with 200 OK
  return ok().chunked(source);
}

我的问题是:

  1. 如何为 Jackson json 节点获得相同的结果?
  2. 是否有另一种方法可以使用 play 框架解决大型数据集问题?
  3. 我们还有其他用于 JSON 流的播放框架文档吗?

回答1

我认为你必须实现流代码设计 f.e.:

  • 函数 populateMapWithData() 必须返回数据流,一个返回元素 - 一个 json 节点(或节点数组,我认为 werry 小节点会使您的响应非常慢)
  • 函数 populateMapWithData() 必须逐段从数据库中获取数据(当流将尝试获取下一部分数据时),以节省内存
  • 对于结果,您必须使用 Chunked responses 因为您不知道内容的实际大小,https://www.playframework.com/documentation/2.8.x/ScalaStream#Chunked-responses 中的更多详细信息与您提出的问题相同的链接
  • 在客户端,您可以读取每个块或块数组,因为这批准了 json 规范

我让我们尝试制作不同的设计样本:

Results.ok().chunked( //chunked result
   Source.fromPublisher(s -> // publisher read all data by chunks
      mapper.valueToTree(populateEntryWithData(s))));

Object populateEntryWithData(Subscriber<? super T> s) {
   //todo implement page reading for data
   //todo or you can try use reactive driver for database
}

注意

使用 json 的游戏框架通常会更好地使用 play.libs.Json 或注入 ObjectMapper 或注入 ObjectMapperBuilder

如果您需要添加更多配置您可以调用 mapper.copy() 的对象映射器,否则您可能会破坏服务中的另一个内部映射函数

相似文章

最新文章