Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Micronaut控制器中的流式大响应,无需耗尽内存_Java_Json_Mongodb_Reactive Programming_Micronaut - Fatal编程技术网

Java Micronaut控制器中的流式大响应,无需耗尽内存

Java Micronaut控制器中的流式大响应,无需耗尽内存,java,json,mongodb,reactive-programming,micronaut,Java,Json,Mongodb,Reactive Programming,Micronaut,我们正在使用Micronaut和Mongo通过一些控制器公开数据。由于响应实体的大小在增长,我们的应用程序有时会出现内存不足的情况。因此,我们正在研究切换到异步mongo驱动程序,并使用反应式响应将数据流传输到客户端。不幸的是,我们无法更改API响应结构或内容类型(所有application/json) 我们的一个API返回了如下结构的实体: [ { "field": "value" }, { "field": "va

我们正在使用Micronaut和Mongo通过一些控制器公开数据。由于响应实体的大小在增长,我们的应用程序有时会出现内存不足的情况。因此,我们正在研究切换到异步mongo驱动程序,并使用反应式响应将数据流传输到客户端。不幸的是,我们无法更改API响应结构或内容类型(所有
application/json

我们的一个API返回了如下结构的实体:

[
  { "field": "value" },
  { "field": "value" },
  ...
  { "field": "value" }
]
@Get("all")
public Flowable<String> getAllExamples() {
    ObjectMapper objectMapper = new ObjectMapper();
    Publisher<Example> dev = dataStore.find();
    return Flowable.fromPublisher(dev)
            .map(mapper::map)
            .concatMap(item -> Flowable.just(objectMapper.writeValueAsString(item), ","))
            .startWith("{\"list\": [")
            .concatWith(Flowable.just("],\"meta\":\"whatever\"}"));
}
这是我们使用该控制器实现的,其中
数据存储
返回一个
发布者

我们是否可以为这样的实体应用类似的发布者/可流动模式,或者在发送响应之前将这些响应的数据加载到内存中

我们尝试了以下签名:

    @Get("all/dev")
    Single<ExamplesWrapper> getAllDev() {
        Publisher<Example> dev = dataStore.find();
        return Flowable.fromPublisher(dev)
                .map(mapper::map)
                .collect((Callable<ArrayList<Example>>) ArrayList::new, ArrayList::add)
                .map(ExampleWrapper::new);
    }
@Get(“全部/dev”)
单getAllDev(){
Publisher-dev=dataStore.find();
返回Flowable.fromPublisher(开发人员)
.map(映射器::map)
.collect((可调用)ArrayList::new,ArrayList::add)
.map(ExampleWrapper::new);
}
包装器将在其中添加一些元数据。但这会在发送之前再次将其全部加载到内存中,导致应用程序崩溃

将Flowable添加到响应包装器中:


public class ExamplesWrapper {

    private final Flowable<Example> examples;

    @ConstructorProperties({"examples"})
    public ExamplesWrapper(Flowable<Example> examples) {
        this.examples = examples;
    }

    public Flowable<Example> getExamples() {
        return examples;
    }
}

公共类示例说唱歌手{
私人最终流动示例;
@构造函数属性({“示例”})
公共示例振打器(可流动示例){
这个例子=例子;
}
公共流getExamples(){
返回示例;
}
}
也会因一些不错的Jackson映射异常而失败

元数据不依赖于实际的示例数据(它添加了一些静态的公司信息)。我们能否在不必将所有数据加载到内存的情况下实现这样一个端点?

来自:

6.20写入响应数据

反应式写入响应数据

Micronaut的HTTP服务器支持通过 返回一个发布服务器,该发布服务器发出可编码到 HTTP响应

下表总结了示例返回类型签名和 服务器处理它们的行为:返回类型描述

  • 可流动的:可流动的,以字节[]形式发送内容的每个块,而不阻塞
  • 助焊剂:一种反应器助焊剂,它以网状的ByteBuf形式释放出每一块物质
  • Publisher:以字符串形式发布每个内容块的发布者
  • 当发出POJO时,每个发出的对象默认编码为JSON而不阻塞
返回反应类型时,服务器使用的传输编码为 分块并继续写入数据,直到发布者onComplete方法 被称为

我理解这一点,因此如果您希望Micronaut机制流式处理您的内容,您需要有签名,如
可流动
通量
发布者
,其中项目是您的响应的一部分,而不是完整的项目。Micronaut随后将使用来自Flowable或等效产品的块进行响应

在这种情况下,我想到的一件事是,你可以自己做分割成合适的块。这样,在不将大响应缓冲到内存中的情况下,流式传输大响应应该是可行的

比如说:

[
  { "field": "value" },
  { "field": "value" },
  ...
  { "field": "value" }
]
@Get("all")
public Flowable<String> getAllExamples() {
    ObjectMapper objectMapper = new ObjectMapper();
    Publisher<Example> dev = dataStore.find();
    return Flowable.fromPublisher(dev)
            .map(mapper::map)
            .concatMap(item -> Flowable.just(objectMapper.writeValueAsString(item), ","))
            .startWith("{\"list\": [")
            .concatWith(Flowable.just("],\"meta\":\"whatever\"}"));
}
@Get(“全部”)
公共可流动getAllExamples(){
ObjectMapper ObjectMapper=新的ObjectMapper();
Publisher-dev=dataStore.find();
返回Flowable.fromPublisher(开发人员)
.map(映射器::map)
.concatMap(item->Flowable.just(objectMapper.writeValueAsString(item),“,”))
.startWith(“{”列表\“:[”)
.concatWith(Flowable.just(“],\“meta\”:\“whatever\”);
}
这是一个骇人听闻的问题,但似乎适合这样的情况


有些方法不起作用:

我在一个定制的Jackson映射器中测试了直接写入JsonGenerator,在对象运行时按中所述刷新对象,但micronaut似乎不会将响应刷新回最终用户,而是将其缓冲,导致内存不足。该方法与弹簧靴配合使用,因此可能是Micronaut中缺少的功能


当使用Micronaut(阻塞)响应并试图在写入数据时刷新数据时,同样的缓冲也发生在我身上

感谢这一点,尽管它确实很粗糙并且需要一些调整(列表包含一个尾随的
),但它确实解决了我们在本例中的问题:)