限制 Spring WebFlux 上的 HTTP 响应大小

Limit HTTP response size on Spring WebFlux

我正在使用 Spring WebFlux 编写 http 爬虫程序,它很容易并行执行并具有 HTTP 超时:

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

但是我如何限制页面响应大小,比如我不想为每个 URL 下载超过 500KB 的数据。读取 HTTP header Content-Length 不可靠。我想我需要降低一级并直接使用字节缓冲区和 Netty 事件,但是最好使用 Flux/Mono 包装它以继续使用这些原语

如果您正在使用 Spring 启动,您应该使用自动配置的 WebClient.Builder 创建您的 WebClient;这将反映 Spring Boot 的意见,例如,将根据您选择的配置选项配置 Jackson 解码。

您还应该构建一个并将其用于许多请求,而不是为每个请求创建一个新的客户端实例 - 这效率不高。

从 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(尚未发布),您可以随时查看过滤器功能的实现,并将 copy/paste 代码添加到您的项目中,这非常棒短的。