programing

DataBufferLimitException:버퍼 webflux 오류에 대한 최대 바이트 제한을 초과했습니다.

golfzon 2023. 4. 4. 22:37
반응형

DataBufferLimitException:버퍼 webflux 오류에 대한 최대 바이트 제한을 초과했습니다.

파일을 보내는 동안 바이트 배열을 받습니다.어레이를 받기 위해 항상 webflux에 문제가 있습니다.에러는 다음과 같습니다.

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
    at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:101)
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException

이제 그것을 webflux로 해결하는 방법을 알 수 있습니까?

이 방법은 효과가 있었습니다.

  1. 작성하다@Bean 메인 「」의 어느 로,SpringBootApplication 링크:

    @Bean
    public WebClient webClient() {
        final int size = 16 * 1024 * 1024;
        final ExchangeStrategies strategies = ExchangeStrategies.builder()
            .codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size))
            .build();
        return WebClient.builder()
            .exchangeStrategies(strategies)
            .build();
    }
    
  2. 다음, 여러분이 .WebClient:

    @Service
    public class TestService {
    
        @Autowired
        private WebClient webClient;
    
        public void test() {
            String out = webClient
                .get()
                .uri("/my/api/endpoint")
                .retrieve()
                .bodyToMono(String.class)
                .block();
    
            System.out.println(out);
        }
    }
    

는 새로운 합니다.spring.codec.max-in-memory-sizeSpring Boot (스프링 부트)에다가 더해요.application.yml다음과 같이 합니다.

spring:
  codec:
    max-in-memory-size: 10MB

스프링 부트의 최대 바이트(MB)를 설정합니다. application.properties다음과 같이 합니다.

spring.codec.max-in-memory-size=20MB

단순한 Rest Controller에서 이 오류가 발생하고 있습니다(큰 json 문자열을 올립니다).

제가 방법이 있습니다.maxInMemorySize

import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;

@Configuration
public class WebfluxConfig implements WebFluxConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/swagger-ui.html**")
            .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024);
    }
}

이것은 의외로 찾기 어려웠다

나를 위해 일했다

webTestClient.mutate()
  .codecs(configurer -> configurer
          .defaultCodecs()
          .maxInMemorySize(16 * 1024 * 1024))
  .build().get()
  .uri("/u/r/l")
  .exchange()
  .expectStatus()
  .isOk()

데이터를 한 번에 검색하는 대신 다음과 같이 스트리밍할 수 있습니다.

Mono<String> string = webClient.get()
    .uri("end point of an API")
    .retrieve()
    .bodyToFlux(DataBuffer.class)
    .map(buffer -> {
        String string = buffer.toString(Charset.forName("UTF-8"));
        DataBufferUtils.release(buffer);
        return string;
    });

또는 스트림으로 변환:

    .map(b -> b.asInputStream(true))
    .reduce(SequenceInputStream::new)
    .map(stream -> {
        // consume stream
        stream.close();
        return string;
    });

대부분의 경우 스트림을 직접 처리하지 않고 실제로 집약하고 싶지 않습니다.대량의 데이터를 메모리에 로드해야 하는 것은 대부분 접근 방식을 보다 반응적인 접근 방식으로 바꾸려는 신호입니다.JSON 및 XML-Pars에는 스트리밍 인터페이스가 있습니다.

이건 내게 효과가 있었다.

val exchangeStrategies = ExchangeStrategies.builder()
                .codecs { configurer: ClientCodecConfigurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024) }.build()
        return WebClient.builder().exchangeStrategies(exchangeStrategies).build()

하나의 은 관습이다를 만드는 입니다.CodecCustomizer 가지 할 수 있습니다.WebFlux ★★★★★★★★★★★★★★★★★」WebClient★★★★★★★★★★★★★★★★★★:

@Configuration
class MyAppConfiguration {

    companion object {
        private const val MAX_MEMORY_SIZE = 50 * 1024 * 1024 // 50 MB
    }

    @Bean
    fun codecCustomizer(): CodecCustomizer {
        return CodecCustomizer {
            it.defaultCodecs()
                .maxInMemorySize(MAX_MEMORY_SIZE)
        }
    }
}

Spring Boot 2.3.0에서는 Reactive Elasticsearch REST 클라이언트 전용 설정 속성이 있습니다.

다음 구성 속성을 사용하여 클라이언트의 특정 메모리 제한을 설정할 수 있습니다.

spring.data.data.search.client.spring.max-in-memory-size=기존 spring.spring.disc.max-in-memory-size 속성은 분리되어 있으며 응용 프로그램의 다른 WebClient 인스턴스에만 영향을 줍니다.

하기 위해 할 수 는 콩 하십시오.WebFluxConfigurationSupport를 사용하고 있는 는, 내, Boot 2.7.2에서는 스프링이 되는 「스프링을 로드합니다.spring.codec.max-in-memory-size이 솔루션이 동작하려면 , 이 속성도 올바르게 설정되어 있을 필요가 있습니다.

이것이 문제의 원인인지 여부를 테스트하려면WebFluxConfigurationSupport임시로 구현합니다.장기적으로는 Configuration bean을 사용하여 auto configured bean의 Atribute를 덮어쓰는 것이 효과가 있었습니다.저 같은 경우에는WebFluxConfigurer같은 방법을 모두 사용할 수 있어 의 대체 수단이었다.WebFluxConfigurationSupport큰 JSON 메시지가 설정대로 디코딩됩니다.

webClient 기본 설정을 글로벌하게 변경하지 않으려면 다음 방법을 사용하여 여러 DataBuffer를 수동으로 병합할 수 있습니다.

webClient
        .method(GET)
        .uri("<uri>")
        .exchangeToMono(response -> {
          return response.bodyToFlux(DataBuffer.class)
              .switchOnFirst((firstBufferSignal, responseBody$) -> {
                assert firstBufferSignal.isOnNext();
                return responseBody$
                    .collect(() -> requireNonNull(firstBufferSignal.get()).factory().allocateBuffer(), (accumulator, curr) -> {
                      accumulator.ensureCapacity(curr.readableByteCount());
                      accumulator.write(curr);
                      DataBufferUtils.release(curr);
                    })
                    .map(accumulator -> {
                      final var responseBodyAsStr = accumulator.toString(UTF_8);
                      DataBufferUtils.release(accumulator);
                      return responseBodyAsStr;
                    });
              })
              .single();
        });

위의 코드는 모든 데이터를 집약합니다.DataBuffers는 싱글로DataBuffer그 후, 최종 결과를 변환DataBuffer끈으로 묶다.수신된 DataBuffer가 문자를 구성하는 데 모든 바이트를 가지고 있지 않을 수 있으므로 이 답변은 작동하지 않습니다(UTF-8 문자를 포함하면 각 문자가 최대 4바이트를 차지할 수 있습니다).따라서 중간값으로 변환할 수 없습니다.DataBuffer를 String으로 변환합니다.버퍼의 끝에 오는 바이트는 유효한 문자를 작성하는데 필요한 바이트의 일부만 포함할 수 있습니다.

그러면 모든 응답이 로드됩니다.DataBuffer를 메모리에 저장하지만 글로벌 설정을 변경하는 것과는 달리webClient전체 어플리케이션에서 사용할 수 있습니다.이 옵션을 사용하면 원하는 부분만 전체 응답을 읽을 수 있습니다. 즉, 큰 응답이 예상되는 부분만 좁혀서 이 옵션을 선택할 수 있습니다.

Spring boot 2.7.x에서 메모리 크기를 webclient로 설정하려면 다음 속성을 사용해야 합니다.webclient는 반응형 Elastic Search에서 내부적으로 사용됩니다.

spring.elasticsearch.webclient.max-in-memory-size=512MB

springboot 메인클래스에 아래 코드를 추가합니다.

@Bean
public WebClient getWebClient() {
    return WebClient.builder()
            .baseUrl("Your_SERVICE_URL")
            .codecs(configurer -> configurer
                      .defaultCodecs()
                      .maxInMemorySize(16 * 1024 * 1024))
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
}

난 이게 좋아.

언급URL : https://stackoverflow.com/questions/59735951/databufferlimitexception-exceeded-limit-on-max-bytes-to-buffer-webflux-error

반응형