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(
) { }
- 第二种方式,从
DelegatingWebFluxConfiguration
延伸到你的路由器或控制器
参考:https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-config-advanced-java
@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
(如果内存已满,存储在磁盘中)
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
如果内存已满,它会将这些文件临时保存到磁盘。
您还可以自定义存储大小文件的内存
可能没有多少开发者遇到和我一样的问题。
但我想分享我已经解决了将近 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(
) { }
- 第二种方式,从
DelegatingWebFluxConfiguration
延伸到你的路由器或控制器
参考:https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-config-advanced-java
@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
(如果内存已满,存储在磁盘中)
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
如果内存已满,它会将这些文件临时保存到磁盘。
您还可以自定义存储大小文件的内存