2

我们在 inet 和 stackoverflow 上花费了很多时间,但是没有一个发现让我们在计划在 Spring 上下文中上传文件的方式上感到满意。

对我们的架构说几句话。我们有一个 node.js 客户端,它将文件上传到 Spring Boot 应用程序中。让我们将此 REST 端点称为“客户端端点”。我们的 Spring Boot 应用程序充当中间件并调用“外部系统”的端点,因此我们将此端点称为“外部”端点,因为区别。主要目的是这两个端点之间的文件处理以及它们之间的一些业务逻辑。

实际上,我们客户端的界面是这样的:

public class FileDO {

    private String id;
    private byte[] file;
    ...
}

在这里我们非常灵活,因为它是我们的客户端和我们的接口定义。

由于在负载下我们的系统有时会耗尽内存的问题,我们计划将我们的代码重新组织为一种更加基于流的反应式方法。当我写“负载不足”时,我的意思是负载严重不足,例如同时上传数百个文件,其中大文件从至少几 MB 到最多 1GB。我们知道,这些测试并不代表真实的应用程序用例,但我们希望做好准备。

我们对我们的挑战进行了一些研究,最终我们使用分析器工具向我们展示了根据我们的 REST 端点,我们将文件作为字节数组完全存储在我们的内存中。仅此而已,但效率不高。

目前我们正面临这样的要求,即为文件上传提供一个 REST 端点并将这些文件推送到某个外部系统的另一个 REST 端点。这样做,我们的主要应用程序意图是成为文件上传的一些中间层。根据这种初始情况,我们希望不会将这些文件作为一个整体留在我们的记忆中。最好是一个流,也许是反应性的。我们已经对一些业务功能有部分反应,但在熟悉所有这些东西的一开始。

那么,到目前为止,我们的步骤是什么?我们引入了一个新的 Client (node.js --> Spring Boot) 接口,如下所示。到目前为止,这有效。但它真的是一种基于流的方法吗?第一个指标表明,这不会降低内存利用率。

@PostMapping(value="/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Mono<Void> upload(@RequestPart(name = "id") String id, @RequestPart(name = "file") Mono<FilePart> file) {
    fileService.save(id, file);
    ...
}

第一个问题:这种类型 Mono<> 就在这里吗?还是我们应该更好地使用 DataBuffer 的 Flux 之类的?而且,如果是这样,客户端应该如何表现并以这种格式传递数据,它实际上是一种流式方法?

然后 FileService 类应该将此文件发布到外部系统中,也许对给定数据执行其他操作,至少记录 id 和文件名。:-) 我们在 FileService.save(..) 中的代码实际上如下所示:

...
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
    bodyBuilder.asyncPart(...take mono somehow...);
    bodyBuilder.part("id", id);
    return webClient.create("url-of-foreign-system")
            .uri("/uploadFile")
            .syncBody(bodyBuilder.build())
            .retrieve()
            .bodyToMono(Result.class);
...

不幸的是,第二个 REST 端点,我们的外部系统之一,看起来与我们的第一个没有什么不同。它将被来自另一个系统的数据丰富。它需要一些带有 id 和字节数组的 FileDO2 以及特定于第二个外部系统的其他一些元数据。

如前所述,我们的方法应该是最小化客户端和外部系统之间的操作的内存占用。有时我们不仅要向该系统提供数据,还要执行一些可能会减慢整个流式处理过程的业务逻辑。

有什么想法可以整体做到这一点吗?目前我们还没有线索来做这一切......我们感谢任何帮助或想法。

4

0 回答 0