How to Resolve Spring Webflux DataBufferLimitException – 如何解决Spring Webflux DataBufferLimitException?

最后修改: 2022年 10月 18日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll explore why we may see DataBufferLimitException in a Spring Webflux application. We’ll then take a look at the various ways we can resolve the same.

在本教程中,我们将探讨为什么我们会在Spring Webflux应用程序中看到DataBufferLimitException。然后,我们将看看有哪些方法可以解决这个问题。

2. Understanding the Problem

2.了解问题

Let’s understand the problem first before jumping to the solution.

在跳到解决方案之前,让我们首先了解问题。

2.1. What’s DataBufferLimitException?

2.1.什么是DataBufferLimitException?

Spring WebFlux limits buffering of data in-memory in codec to avoid application memory issues. By default, this is configured to 262,144 bytes. When this isn’t enough for our use case, we’ll end up with the DataBufferLimitException.

Spring WebFlux 限制在编解码器中对内存中的数据进行缓冲,以避免应用程序内存问题。默认情况下,这被配置为262,144字节。当这不足以满足我们的用例时,我们最终会遇到DataBufferLimitException

2.2. What’s a Codec?

2.2.什么是Codec

The spring-web and spring-core modules provide support for serializing and deserializing byte content to and from higher-level objects through non-blocking I/O with reactive stream back pressure. Codecs offer an alternative to Java serialization. One advantage is that, typically, objects need not implement Serializable.  

spring-webspring-core模块通过具有反应式流背压的非阻塞I/O,为向高层对象序列化和反序列化字节内容提供支持。Codecs提供了Java序列化的替代方案。一个优点是,通常,对象不需要实现Serializable。

3. Server Side

3.服务器端

Let’s first look at how DataBufferLimitException plays out from a server perspective.

让我们首先看看DataBufferLimitException是如何从服务器的角度进行的。

3.1. Reproducing the Issue

3.1.重现问题

Let’s try to send a JSON payload of size 390 KB to our Spring Webflux server application to create the exception. We’ll use the curl command to send a POST request to our server:

让我们尝试发送一个大小为390KB的JSON有效载荷到我们的Spring Webflux服务器应用程序,以创建异常。我们将使用curl命令向我们的服务器发送POSTrequest。

curl --location --request POST 'http://localhost:8080/1.0/process' \
  --header 'Content-Type: application/json' \
  --data-binary '@/tmp/390KB.json'

As we can see, the DataBufferLimitException is thrown:

我们可以看到,DataBufferLimitException被抛出。

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
  at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:99) ~[spring-core-5.3.23.jar:5.3.23]
  Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
  *__checkpoint ⇢ HTTP POST "/1.0/process" [ExceptionHandlingWebHandler]

3.2. Solution via Properties

3.2.通过属性求解

The easiest solution would be to configure the application property spring.codec.max-in-memory-size. Let’s add the following to our application.yaml file:

最简单的解决方案是配置应用程序属性spring.codec.max-in-memory-size。让我们在我们的application.yaml文件中添加以下内容。

spring:
    codec:
        max-in-memory-size: 500KB

With that, we should now be able to buffer payloads larger than 500 KB in our application.

有了这些,我们现在应该能够在我们的应用程序中缓冲大于500KB的有效载荷。

3.3. Solution via Code

3.3.通过代码解决

Alternatively, we can use the WebFluxConfigurer interface to configure the same thresholds. To do this, we’ll add a new configuration class, WebFluxConfiguration:

另外,我们可以使用WebFluxConfigurer接口来配置相同的阈值。为此,我们将添加一个新的配置类,WebFluxConfiguration:

@Configuration
public class WebFluxConfiguration implements WebFluxConfigurer {
    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.defaultCodecs().maxInMemorySize(500 * 1024);
    }
}

This approach will also provide us with the same results.

这种方法也将为我们提供相同的结果。

4. Client Side

4.客户端

Let’s now switch gears to look at the client-side behavior.

现在让我们换个角度来看看客户端的行为。

4.1. Reproducing the Issue

4.1.重现问题

We’ll try to reproduce the same behavior with Webflux’s WebClient. Let’s create a handler that calls the server with a payload of 390 KB:

我们将尝试用Webflux的WebClient再现同样的行为。让我们创建一个处理程序,以390KB的有效载荷调用服务器。

public Mono<Users> fetch() {
    return webClient
      .post()
      .uri("/1.0/process")
      .body(BodyInserters.fromPublisher(readRequestBody(), Users.class))
      .exchangeToMono(clientResponse -> clientResponse.bodyToMono(Users.class));
}

We see again that the same exception is thrown but this time due to the webClient trying to send a larger payload than allowed:

我们再次看到,同样的异常被抛出,但这次是由于webClient试图发送大于允许的有效载荷。

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
  at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:99) ~[spring-core-5.3.23.jar:5.3.23]
  Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    *__checkpoint ⇢ Body from POST http://localhost:8080/1.0/process [DefaultClientResponse]
    *__checkpoint ⇢ Handler com.baeldung.spring.reactive.springreactiveexceptions.handler.TriggerHandler@428eedd9 [DispatcherHandler]
    *__checkpoint ⇢ HTTP POST "/1.0/trigger" [ExceptionHandlingWebHandler]

4.2. Solution via Properties

4.2.通过属性求解

Again, the easiest solution is to configure the application property spring.codec.max-in-memory-size. Let’s add the following to our application.yaml file:

同样,最简单的解决方案是配置应用程序属性spring.codec.max-in-memory-size。让我们在我们的application.yaml文件中添加以下内容。

spring:
    codec:
        max-in-memory-size: 500KB

With that, we should now be able to send payloads larger than 500 KB from our application. It’s worth noting that this configuration gets applied to the entire application, which means to all web clients and the server itself.

有了这些,我们现在应该能够从我们的应用程序发送大于500KB的有效载荷。值得注意的是,这个配置被应用于整个应用程序,这意味着所有的网络客户端 和服务器本身。

Hence, if we want to configure this limit only for specific web clients, then this won’t be an ideal solution. Additionally, there is a caveat with this approach. The builder used to create the WebClients must be auto-wired by Spring like the below:

因此,如果我们想只为特定的网络客户端配置这个限制,那么这将不是一个理想的解决方案。此外,这种方法还有一个注意事项。用于创建WebClients的构建器必须是由Spring自动连接的,如下所示。

@Bean("webClient")
public WebClient getSelfWebClient(WebClient.Builder builder) {
    return builder.baseUrl(host).build();
}

4.3. Solution via Code

4.3.通过代码解决

We’ve also got a programmatic way to configure the web clients to achieve this goal. Let’s create a WebClient with the following configuration:

我们也有一个程序化的方法来配置网络客户端以实现这一目标。让我们创建一个WebClient,配置如下。

@Bean("progWebClient")
    public WebClient getProgSelfWebClient() {
        return WebClient
          .builder()
          .baseUrl(host)
          .exchangeStrategies(ExchangeStrategies
	  .builder()
	  .codecs(codecs -> codecs
            .defaultCodecs()
            .maxInMemorySize(500 * 1024))
	    .build())
          .build();
}

And with that, we should now be able to successfully send payloads higher than 500 KB using our web client.

就这样,我们现在应该能够使用我们的网络客户端成功地发送高于500KB的有效载荷。

5. Conclusion

5.总结

In this article, we understood what DataBufferLimitException is and looked at how to fix them on both the server and client sides. We looked at two approaches for both, firstly based on properties configuration and secondly programmatically. We hope this exception won’t be a trouble for you anymore.

在这篇文章中,我们了解了什么是DataBufferLimitException,并研究了如何在服务器和客户端修复它们。我们研究了两种方法,首先是基于属性配置的方法,其次是编程方法。我们希望这个异常不会再给你带来麻烦。

As always, the complete code is available over on GitHub.

一如既往,完整的代码可在GitHub上获得