当前位置: 首页 > 知识库问答 >
问题:

如何在Quarkus中通过REST从数据库流式传输大型数据

汤飞
2023-03-14

我正在Quarkus中实现一个GET方法,它应该向客户端发送大量数据。使用JPA/Hibernate从数据库中读取数据,序列化为JSON,然后发送到客户端。如果没有整个数据在内存中,如何有效地完成此操作?我尝试了以下三种可能性,但都没有成功:

  1. 使用JPA中的getResultList,返回一个以列表为主体的响应。MessageBodyWriter将负责将列表序列化为JSON。然而,这会将所有数据拉入内存,这对于大量记录来说是不可行的
  2. 使用JPA中的getResultStream,返回以流为主体的响应。MessageBodyWriter将负责将流序列化为JSON。不幸的是,这不起作用,因为在执行JAX-RS方法之后,在调用MessageBodyWriter之前,似乎关闭了EntityManager。这意味着基础结果集也被关闭,写入程序无法再从流中读取
  3. 使用流输出作为响应体。与2中的问题相同。发生

所以我的问题是:用Quarkus通过JPA发送读取的大数据的诀窍是什么?

共有3个答案

王子明
2023-03-14

这里有一些资源可以帮助您做到这一点:

  • 使用反应式Hibernate:https://quarkusio.zulipchat.com/#narrow/stream/187030-用户/主题/大型。20个数据集。20使用。20无功。20SQL。20个客户
  • 分页与仅转发结果集:https://knes1.github.io/blog/2015/2015-10-19-streaming-mysql-results-using-java8-streams-and-spring-data.html

最后一篇文章是针对SpringBoot的,但这个想法也可以用Quakus实现。

------------编辑:

好的,我已经制定了一个示例,在其中我进行批量选择。我做得很华丽,但没有它你也可以很容易地做到。

我将返回一个ScrollableResult,然后在Rest资源中使用它,通过SSE(服务器发送事件)将其流式传输到客户端

------------编辑2:

我已将setFetchSize添加到查询中。你应该使用这个数字,并将其设置在1-50之间。如果value=1,则db行将以1乘1的方式提取,这最像流。它将使用最少的内存,但db之间的I/O

在进行这样的批量操作时,强烈建议使用无状态会话。

@Entity
public class Fruit extends PanacheEntity {

    public String name;
    
    // I've removed the logic from here to the Rest resource, 
     // otherwise you cannot close the session
    
}
@Path("/fruits")
public class FruitResource {

    @GET
    @Produces(SERVER_SENT_EVENTS)
    public void fruitsStream(@Context Sse sse, @Context SseEventSink sink) {
        var sf = Fruit.getEntityManager().getEntityManagerFactory().unwrap(SessionFactory.class);

        try (var session = sf.openStatelessSession();
             var scrollableResults = session.createQuery("select f from Fruit f")
                                    .scroll(ScrollMode.FORWARD_ONLY) 
                                    .setFetchSize(1) {
            while (scrollableResults.next()) {
                sink.send(sse.newEventBuilder().data(scrollableResults.get(0)).mediaType(APPLICATION_JSON_TYPE).build());
            }
            sink.close();
        }
    }
}

然后我这样调用这个Restendpoint(通过httpie):

> http :8080/fruits --stream

data: {"id":9996,"name":"applecfcdd592-1934-4f0e-a6a8-2f88fae5d14c"}

data: {"id":9997,"name":"apple7f5045a8-03bd-4bf5-9809-03b22069d9f3"}

data: {"id":9998,"name":"apple0982b65a-bc74-408f-a6e7-a165ec3250a1"}

data: {"id":9999,"name":"apple2f347c25-d0a1-46b7-bcb6-1f1fd5098402"}

data: {"id":10000,"name":"apple65d456b8-fb04-41da-bf07-73c962930629"}

希望这对你有帮助。

唐彬炳
2023-03-14

根据您的要求,您有两种选择:

选项1:采用HATEOAS方法(https://restfulapi.net/hateoas/)。通过REST标准交换大数据集的标准模式之一。因此,在这种方法中,服务器将在第一次响应中快速响应一组HATEOAS URI。其中每个HATEOAS URI代表一组元素。因此,您需要根据数据大小生成这些URI,并让客户端代码负责将这些URI作为REST API单独调用以获取实际数据。但在这个选项中,您也可以考虑Reactive风格,以获得具有小内存足迹的流处理的更多优势。

选项2:正如上面@Serkan所建议的,将结果集作为REST响应从数据库连续流式传输到客户端。在这里,您需要确保客户端和服务之间的网关用于超时设置。如果没有门户,你就是好的。因此,您可以利用所有层的反应式编程来实现连续流。“DAO/数据访问层”--

唐啸
2023-03-14

您的结果是否必须全部包含在一个响应中?让客户端请求下一个结果页,直到没有下一个结果页为止,这是一个典型的REST API分页练习,怎么样?此外,JPA后端将只从数据库中获取该页面,因此没有任何时候所有内容都会存储在内存中。

 类似资料:
  • 在我的设置中,我希望通过SSE通道(服务器发送的事件)转发某些状态更改。状态更改是通过调用RESTendpoint来启动的。因此,我需要将传入的状态更改转发到SSE流。 在Quarkus中实现这一点的最佳/最简单方法是什么。 我能想到的一个解决方案是使用EventBus(https://quarkus.io/guides/reactive-messaging)。SSEendpoint将订阅状态更改

  • 问题内容: 我想使用elasticsearch-river-mysql以便将数据从MySQL数据库连续传输到ElasticSearch。我是ES和Rivers的初学者,所以希望您能为我的问题提供帮助。 据我所知,数据将从MySQL数据库流式传输到ES集群,后者将自动对其进行索引。那是对的吗?我需要了解任何超时或限制吗? 关系数据库表之间的外键关系将如何转换为ES?包含外键的表行是否将成为ES文档的

  • 问题内容: 我正在尝试设置一个非常基本的html5页面,该页面会加载20MB的.mp4视频。看来,浏览器需要下载整个内容,而不仅仅是播放视频的第一部分并在其余部分进行流传输。 我在搜索时发现的最接近的内容…我尝试了“手刹”和“数据回合”,两者均未发挥作用: 关于如何执行此操作或是否可行的任何想法? 这是我正在使用的代码: 问题答案: 确保moov(元数据)在mdat(音频/视频数据)之前。这也称为

  • 我试图从SQL数据库中获取一些数据,这时对我的Camel服务进行了REST调用。我可以以字节数组的形式获取数据,并将其设置为Camel的主体,然后将其作为原始字节返回给调用者(web浏览器、应用程序等)。 我现在想用流来做这件事,而不是保存内存中的所有字节。如果我使用,当方法完成时,我可以从结果集获得的连接和流都将关闭,即使我试图返回一个并将其设置为主体。这阻止了我从DB直接流式传输数据,通过Ca

  • 问题内容: 我有10,000个地址和5,000人的大型数据库。 我想让用户在数据库中搜索地址或用户。在输入文本时,我想使用Twitter的提前提示功能来建议结果。 在此处查看NBA示例:http : //twitter.github.io/typeahead.js/examples。 我了解从速度和负载的角度来看,预取15,000个项目并不是最佳选择。尝试实现此目标的更好方法是什么? 问题答案:

  • 问题内容: 使用MVC模型,我想编写一个JsonResult,它将Json字符串流式传输到客户端,而不是一次将所有数据转换成Json字符串,然后将其流回客户端。我有一些动作需要在Json传输时发送非常大的记录(超过300,000条记录),我认为基本的JsonResult实现是不可伸缩的。 我正在使用Json.net,我想知道是否有一种方法可以在转换Json字符串时流化它的块。 但是我不确定如何将这