当前位置:首页 > 文章列表 > 文章 > java教程 > Spring WebFlux 怎样限制并发请求的背压范围

Spring WebFlux 怎样限制并发请求的背压范围

来源:17golang原创 2026-11-02 11:15:14 0浏览 收藏

Spring WebFlux 要限制并发请求,必须先明确范围:flatMap(mapper, concurrency, prefetch)约束一条订阅链中同时活跃的内部序列;多个 HTTP 请求共用的下游容量,需要另设共享准入。背压传递的是元素需求,不能自动变成全站并发上限、每秒请求配额或总内存预算。

突增请求为什么会穿透单条链的并发设置

假设一个聚合接口接收一批商品编号,再通过 WebClient 查询详情。排查时看到的时间线可能是:单请求访问正常,入口并发增加后,下游等待时间变长,随后出现超时。这里是用于推理的场景,并非一份已执行的生产事故报告。

根因线索在于“每个请求各自订阅”。一条链设置并发 6,十条同时活跃的链在资源允许时就可能合计订阅 60 个内部调用。这个算式描述范围叠加,不是实测吞吐,也没有计入连接限制和请求完成速度。把参数 6 当成整个服务最多六个下游请求,修复就会落错位置。

排查第一步是区分入口活跃请求数、每条链的内部订阅数、服务实例的下游活跃调用数。三者分别记数,才能判断压力增加发生在哪一层。

HTTP 请求、独立订阅与 flatMap 并发参数的静态关系图

先给每次订阅设置明确的并发上限

// 每次订阅最多维持 6 个活跃的内部序列
Flux values = ids.flatMap(this::fetch, 6, 1);

这里的 ids 是编号流,fetch 返回一个表示单次远程查询的 Mono。只有真正受订阅驱动的调用才能被这个组合约束;如果在组装链之前就启动任务,或者在 mapper 内手动调用 subscribe(),任务生命周期便脱离了这条链的控制。

concurrency控制同时活跃的内部序列数量;prefetch控制对每个内部序列预请求的元素规模。对于只返回一个值的 Mono,prefetch 不是主要容量旋钮。把它写为 1,既不表示总共只缓存一个元素,也不表示只允许一个 HTTP 请求。

limitRate拆分向上游传播的需求批次。它可以帮助调整某个位置的预取行为,但没有时间窗口,不能表达“每秒六次”,也不能替代跨订阅的共享并发准入。需求沿操作符链传播,途中还可能被其他操作符重新塑形。

本步应核对 mapper 是否只返回 Publisher、远程调用是否延迟到订阅时启动,以及是否有显式缓存、提前收集为列表或额外的手动订阅。flatMap 会交错返回结果,若业务需要保持编号顺序,还必须单独选择排序策略并考虑对应缓冲代价。

把共享下游容量放到单例准入层

如果约束目标是“这个应用实例同时最多持有 24 个下游调用许可”,可以把许可放进 Spring 单例服务。以下示例采用立即拒绝策略,没有增加等待队列。数字只是示例,需要按照下游承载能力与实例数量配置。

import java.time.Duration;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.Semaphore;
import java.util.function.Supplier;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class DetailService {
    private final WebClient client;
    // 此对象随单例服务共享,不能为每个请求重新创建
    private final Semaphore permits = new Semaphore(24);

    public DetailService(WebClient client) {
        this.client = client;
    }

    public Flux fetchAll(Flux ids) {
        // 同时保留每个订阅的局部限制
        return ids.flatMap(this::fetch, 6, 1);
    }

    private Mono fetch(String id) {
        return guarded(() -> client.get()
                .uri("/items/{id}", id)
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(2)));
    }

    private  Mono guarded(Supplier> operation) {
        return Mono.defer(() -> {
            // 不在事件循环上阻塞等待许可
            if (!permits.tryAcquire()) {
                return Mono.error(new RejectedExecutionException("下游容量已满"));
            }
            // 供应器抛错也转为错误信号,并经过统一释放路径
            return Mono.defer(operation)
                    .doFinally(signal -> permits.release());
        });
    }
}

项目需要提供已配置 baseUrl 的 WebClient Bean,并使用与当前 Spring 项目兼容的 Reactor 版本。示例没有创建 Controller;在现有响应式入口中返回 fetchAll的结果即可,不要为了取结果调用 block()。

外层 Mono.defer保证每次订阅重新尝试取得许可。内层 defer 将供应器调用纳入信号生命周期;doFinally处理完成、错误与取消后的许可释放。不要使用阻塞的 acquire(),否则事件循环可能被等待许可占住。

这一实现的边界是单个服务对象,通常对应单个应用实例。多实例部署时,各实例各有 24 个许可;如果需要所有实例共享的下游总配额,还要在部署配额、网关或共享协调层分配容量。

立即拒绝会改变错误行为。普通 flatMap 中一个内部错误通常会使整体链失败,并取消其他内部订阅。应在接口错误映射层将容量不足转换成明确的服务繁忙响应,例如按接口契约返回 503;不要把内部异常文本直接展示给调用方。如果接口允许逐项成功或失败,应另行设计逐项结果对象,不能用空值偷偷丢掉失败项。不要对容量拒绝立即无限重试。

单例服务共享许可、WebClient 与终止信号的静态关系图

复查队列、响应体与取消后的资源占用

并发下降不等于所有积压都消失。连接池等待队列、上游已经收集的编号列表、编码器缓冲以及下游实际执行的工作,都有自己的生命周期。对字符串响应,bodyToMono(String.class)会聚合响应内容,需同时配置合理的编解码缓冲上限:

// 构建项目所用 WebClient 时设置,baseUrl 仍由项目配置
WebClient client = WebClient.builder()
        .baseUrl(remoteBaseUrl)
        .codecs(c -> c.defaultCodecs().maxInMemorySize(64 * 1024))
        .build();

这里的 64 KiB 是示例编解码缓冲配置,不是整个进程的内存上限,也不适用于无条件接收任意大文档。超过限制可能产生 DataBufferLimitException,应按实际响应协议处理。若响应需要真正流式消费,应采用适合协议的流式解码方式,并避免末尾再次聚合全部内容。

要约束的对象适用位置不能据此推断
单订阅活跃内部序列flatMap concurrency全实例或全站并发
实例共享调用许可单例非阻塞准入远程服务器物理执行数绝对相等
需求批次limitRate、prefetch每秒请求数
连接与等待申请HTTP 客户端连接池业务任务总量已受控
聚合解码缓冲maxInMemorySize所有内存分配之和

超时后远程任务一定停止吗?不一定。本地 timeout 会终止当前链路等待并触发取消,许可会释放,但远程服务可能继续处理已经收到的请求。这里限制的是本地逻辑调用许可,不能据此证明远程物理任务也立即减少。需要结合服务端取消支持、请求幂等性和下游超时策略控制残留工作。

修复后应观察什么?在隔离环境分别增加单次请求的编号数量和同时请求数量,观察每订阅活跃内部序列、共享许可占用、准入拒绝数、连接等待数、超时数和响应体大小分布。预期局部并发不超过设定值,共享许可不会超过配置容量;拒绝或超时可能增加,不能把错误被快速返回解释为吞吐已经提升。本文未执行压测,以上是读者可采用的复查动作。

还应加入取消与供应器抛错场景,确认许可最终归还;避免在服务外继续持有已失效的订阅。局部并发、共享准入和队列预算共同明确后,背压的作用范围才有可解释的边界。

官方参数说明可复制查阅:https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html;Mono 生命周期:https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html;WebClient 缓冲配置:https://docs.spring.io/spring-framework/reference/web/webflux-webclient/client-builder.html;WebFlux 说明:https://docs.spring.io/spring-framework/reference/web/webflux.html。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java CDS 怎样缩短应用启动时间Java CDS 怎样缩短应用启动时间
上一篇
Java CDS 怎样缩短应用启动时间
Python asyncio.Queue.shutdown 怎样让生产者和消费者安全收尾
下一篇
Python asyncio.Queue.shutdown 怎样让生产者和消费者安全收尾
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    842次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    842次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    923次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    898次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    728次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码