Webflux 上传大文件导致 Java 堆 space

Webflux upload large files cause Java heap space

可能没有多少开发者遇到和我一样的问题。
但我想分享我已经解决了将近 1 个月的解决方案。
我使用 Kubernetes 和 docker-compose,这个 Webflux 服务(容器)设置了内存限制 1g mem_limit: 1g 我不允许增加内存限制。

coRouter 已被用作容器中的控制器。

@Configuration
class WebController(
) {

    @Bean
    fun endpoints() = coRouter {
        contentType(MediaType.MULTIPART_FORM_DATA).nest {
            POST("/uploadFile") { requestParm ->
                requestParm.awaitMultipartData().multipartFormData["file"]?.first()
            }
        }
    }
}

如果我使用 API 上传文件 http://localhost:9004/uploadFile 100MB,10 个文件(使用 JS Ajax)
它会产生 java.lang.OutOfMemoryError: Java heap space 因为它没有足够的内存。
每当您调用 requestParm.awaitMultipartData() 时,它都会将这些字节流式传输到内存中。

我意识到,我当前的代码会将上传文件直接存储在内存中。 有两种方法可以解决这个问题。

  • 第一种方式,在您的路由器或控制器上添加@EnableWebFlux

@Configuration
@EnableWebFlux
class WebController(
) { }
@Configuration
class WebController(
): DelegatingWebFluxConfiguration() { }

为什么解决了上传大文件的问题?

以上这2种方式会自动使用晚餐class的方法
configureHttpMessageCodecs(configurer: ServerCodecConfigurer)

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-codecs-multipart

By default, the DefaultPartHttpMessageReader is used, but this can be changed through the ServerCodecConfigurer. For more information about the DefaultPartHttpMessageReader, refer to to the javadoc of DefaultPartHttpMessageReader.

如您所见,ServerCodecConfigurer 将默认使用 DefaultPartHttpMessageReader。

DefaultPartHttpMessageReader 具有这个重要属性 MaxInMemorySize(如果内存已满,存储在磁盘中)

https://docs.spring.io/spring-framework/docs/5.3.1/javadoc-api/org/springframework/http/codec/multipart/DefaultPartHttpMessageReader.html#setMaxInMemorySize-int-

Configure the maximum amount of memory allowed per part. When the limit is exceeded: file parts are written to a temporary file. non-file parts are rejected with DataBufferLimitException. By default this is set to 256K.

上传文件默认内存为256K
如果内存已满,它会将这些文件临时保存到磁盘。

您还可以自定义存储大小文件的内存