Ограничить размер HTTP-ответа на Spring WebFlux

Я пишу http crawler, используя Spring WebFlux, и его легко выполнять параллельно и иметь таймауты HTTP:

val sitesToCrawl: Flux<String> = streamOfUrl()
val concurrencyLimit = 100
sitesToCrawl.flatMap(
    { WebClient.create().get().uri(it).exchange().timeout(Duration.ofSeconds(10)) },
    concurrencyLimit
)

Но как я могу ограничить размер ответа страницы, как будто я не хочу загружать более 500 КБ данных для каждого URL. Чтение HTTP-заголовка Content-Length не является надежным. Я думаю, мне нужно перейти на один уровень вниз и напрямую использовать байтовые буферы и события Netty, но было бы неплохо обернуть это, используя Flux/Mono, чтобы продолжать использовать эти примитивы.

1 ответ

Решение

Если вы используете Spring Boot, вы должны создать свой WebClient используя автоматически настроенный WebClient.Builder; это будет отражать мнение Spring Boot и, например, будет настраивать декодирование Джексона в соответствии с выбранными вами параметрами конфигурации.

Вы также должны создать один и использовать его для многих запросов, вместо того, чтобы создавать новый экземпляр клиента для каждого запроса - это неэффективно.

Теперь, начиная с Spring Framework 5.1 (Spring Boot 2.1), Spring WebFlux поставляется с функцией фильтра, которая делает именно это: она читает N байтов, а затем отменяет ответ (прекращает чтение и закрывает соединение). Обратите внимание, что это поведение интересно, если ответ довольно большой, но это также сделает соединение непригодным для повторного использования и не будет возвращено в пул соединений. Если вы сканируете много страниц на одном хосте, пул соединений против создания новых соединений является интересным компромиссом.

Теперь это должно выглядеть так:

@Component
public class CrawlingService {

  private WebClient webClient;

  public CrawlingService(WebClient.Builder builder) {
      this.webClient = builder.filter(ExchangeFilterFunctions.limitResponseSize(maxSize))
                    .build();
  }

  public Mono<Void> crawlPage(URI page) {
      return // use webClient here
  }  

}

Если вы не готовы использовать Spring Boot 2.1 (он еще не выпущен), вы всегда можете посмотреть на реализацию функции фильтра и скопировать / вставить этот код в ваш проект, это очень мало.

Другие вопросы по тегам