说明:本文仅展示架构思路与安全片段,所有敏感字段已用占位符;不含可直接复刻的生产细节。数据与接口均为演示/虚拟

0. 背景与目标

长耗时/不确定接口(如对接第三方机器人平台)的同步阻塞,容易造成请求堆积与峰值雪崩。本次改造目标:

  • 接口 202 Accepted + 任务轮询,释放前端/网关压力

  • 消息队列削峰:生产入队 → 手动 ack 消费 → 失败入 DLQ

  • 幂等键X-Request-Id)与任务态存储,防重下发、可追踪

  • 动态配置(示例以配置中心为主),便于多环境切换

结果:写请求在峰值时平稳可控,消费端有序处理;失败统一入 DLQ 供排障复盘。

1. 轻量数据流

Client --POST--> Gateway --→ API|                         ||<--202 + taskId----------||--GET /tasks/{id} 轮询-->|API --(Produce)--> RabbitMQ(Topic) --→ Consumer(手动 ack) --→ 第三方平台|成功/失败|状态落库/缓存 <--+失败 → DLQ

注意:仅示意关键节点,隐藏了内部服务名、完整路由与业务字段。

2. 动态配置

# DataId: robot-mq-public.yml
spring:rabbitmq:host: ${ENV_MQ_HOST}port: ${ENV_MQ_PORT:5672}username: ${ENV_MQ_USER}password: ${ENV_MQ_PASS}virtual-host: /publisher-confirm-type: correlatedpublisher-returns: truemq:exchange: robot.task.topic.public       # 非生产命名queue:    robot.task.q.public           # 非生产命名rk:       robot.task.dispatch.public    # 非生产命名dlx:      robot.task.dlx.publicdlq:      robot.task.dlq.publicdlrk:     "#"async:idem-ttl-seconds: 3600

说明:占位符 ${ENV_*} 仅示意变量化做法;真实值不要写进文章或仓库。

3. 关键代码片段

以下为骨架式片段,只展示接口与关键点,隐藏了内部实现、异常策略、业务字段映射等细节。

3.1 交换机/队列与手动 ack

@Configuration
public class MqBasicsConfig {@Value("${mq.exchange}") private String exchange;@Value("${mq.queue}")    private String queue;@Value("${mq.rk}")       private String rk;@Value("${mq.dlx}")      private String dlx;@Value("${mq.dlq}")      private String dlq;@Value("${mq.dlrk}")     private String dlrk;@Beanpublic Declarables mqDeclarables() {Queue q = QueueBuilder.durable(queue).withArgument("x-dead-letter-exchange", dlx).withArgument("x-dead-letter-routing-key", dlrk).build();TopicExchange biz = ExchangeBuilder.topicExchange(exchange).durable(true).build();Binding b = BindingBuilder.bind(q).to(biz).with(rk);TopicExchange dead = ExchangeBuilder.topicExchange(dlx).durable(true).build();Queue deadQ = QueueBuilder.durable(dlq).build();Binding db = BindingBuilder.bind(deadQ).to(dead).with(dlrk);return new Declarables(biz, q, b, dead, deadQ, db);}@Beanpublic SimpleRabbitListenerContainerFactory manualAckFactory(ConnectionFactory cf,MessageConverter converter) {var f = new SimpleRabbitListenerContainerFactory();f.setConnectionFactory(cf);f.setMessageConverter(converter);f.setAcknowledgeMode(AcknowledgeMode.MANUAL);return f;}
}

3.2 生产者(骨架)

@Component
public class TaskProducer {private final RabbitTemplate tpl;@Value("${mq.exchange}") private String exchange;@Value("${mq.rk}")       private String rk;public TaskProducer(RabbitTemplate tpl) { this.tpl = tpl; }/** 仅演示:设置 CorrelationData 绑定 taskId,方便确认回调 */public void send(String taskId, Object payload, @Nullable String requestId) {MessagePostProcessor mpp = msg -> {msg.getMessageProperties().setMessageId(taskId);if (requestId != null) msg.getMessageProperties().setHeader("X-Request-Id", requestId);return msg;};tpl.convertAndSend(exchange, rk, payload, mpp, new CorrelationData(taskId));}
}

3.3 消费者(骨架,手动 ack + 幂等占位)

@Slf4j
@Component
public class TaskConsumer {@RabbitListener(queues = "${mq.queue}", containerFactory = "manualAckFactory")public void onMessage(Message message, Channel channel) throws Exception {String taskId = message.getMessageProperties().getMessageId();String reqId  = (String) message.getMessageProperties().getHeaders().get("X-Request-Id");try {// TODO 幂等占位:检查 reqId / taskId 是否已处理// TODO 业务占位:调用第三方平台(已脱敏),记录状态channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);} catch (Exception ex) {log.error("consume failed, taskId={}", taskId, ex);// 直接拒绝入 DLQ:公开文章不贴重试/回退策略细节channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);}}
}

3.4 接口契约(202 + 轮询)(骨架)

@RestController
@RequestMapping("/external/async")
public class AsyncController {// POST 接口:只返回 202 + taskId(隐藏业务入参/第三方字段)@PostMapping("/command")public ResponseEntity<Map<String, Object>> postCommand(@RequestHeader(value="X-Request-Id", required=false) String reqId) {String taskId = UUID.randomUUID().toString();// TODO 写入 PENDING 状态(缓存/DB),并生产 MQ 消息return ResponseEntity.accepted().body(Map.of("taskId", taskId));}// 轮询接口:返回 PENDING / DONE / FAILED(隐藏具体结果结构)@GetMapping("/tasks/{taskId}")public Map<String, Object> query(@PathVariable String taskId) {// TODO 读取存储的任务态与简要结果return Map.of("taskId", taskId, "status", "PENDING");}
}

骨架片段仅演示“怎么做”,刻意省略:完整领域模型、重试/退避参数、幂等键落盘、第三方错误分型、限流灰度策略等生产细节。

4. 测试与验收要点

  • 压测入口:仅关注POST→202 延迟与消费者侧消费速率,而非业务同步 RT

  • 人为制造失败,确认DLQ 入列与排障链路可达

  • 幂等:同 X-Request-Id 重复请求不导致重复外呼

  • 限流:网关/服务内双层策略命中时,优先保证系统稳定

  • 监控:最少监控入队速率、堆积深度、消费速率、DLQ 数量


5. 常见坑位

  • 回调未开启:生产者没开 confirm/return,消息“丢哪儿了”难定位

  • 死信配置缺失:Nack(false,false) 却没有 DLX/DLQ

  • 幂等只在接口层:消费者未做幂等,仍可能二次下游外呼

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/98000.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/98000.shtml
英文地址,请注明出处:http://en.pswp.cn/web/98000.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

接口返回 2 万条数据,easy-trans导致多了20s耗时排查过程

内网访问排版核料详情功能&#xff0c;用户反馈要等十几秒排查 sql&#xff1a;sql 比较简单排查内存计算&#xff1a;arthus trace 类名 方法名 总耗时2s排查页面渲染是否缓慢&#xff1a;F12 查看接口 等待服务器响应 20s 下载时间 30s, 故不考虑渲染问题排查请求响应日志打…

AIGC入门,手搓大模型客户端与MCP交互

概述 在现代应用开发中&#xff0c;将大语言模型&#xff08;LLM&#xff09;与专用工具服务相结合&#xff0c;可以构建出既能理解自然语言&#xff0c;又能准确执行专业任务的智能代理。本文介绍一个基于 MCP&#xff08;Model Context Protocol&#xff09;协议和 Ollama 本…

深度学习:从预备知识到未来展望

在当今数字化时代&#xff0c;深度学习正以前所未有的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车&#xff0c;从精准医疗到个性化推荐系统&#xff0c;深度学习的应用无处不在。本文将从深度学习的预备知识入手&#xff0c;探讨其发展历程、关键技术和未来…

软考高级系统架构设计师之构件与中间件技术篇

一、构件的定义 定义1:软件构件是一种组装单元&#xff0c;它具有规范的接口规约和显式的语境依赖。软件构件可以被独立地部署并由第三方任意地组装。 定义2:构件是某系统中有价值的、几乎独立的并可替换的一个部分&#xff0c;它在良好定义的体系结构语境内满足某清晰的功能。…

Node.js 文件上传中文文件名乱码问题,为什么只有Node会有乱码问题,其他后端框架少见?

问题现象当用户上传包含中文字符的文件时&#xff0c;在服务器端获取到的文件名可能变成类似 ‹•–‡.txt 这样的乱码&#xff0c;而不是预期的中文文件名。为什么只有Node会乱码&#xff1f;很多后端框架&#xff08;如 Java Spring Boot、Python Django、PHP Laravel&#x…

学习英语音标 (从汉语角度看英语音标发音差异)

仅供参考, 跟着教学视频看不懂时再来看以下引导 以下只写容易出错的音标 发音视频: https://www.jiwake.com/yinbiaofayin/ 音标规则单词ɜː类似汉语e, 饿~urgeə类似汉语e, 饿goɔː类似汉语o, 哦~walkɒ类似汉语o, 哦washɪ/iː/的短语, 不止发声短,舌头不用隆起itʃ类似汉…

论文笔记(九十一)GWM: Towards Scalable Gaussian World Models for Robotic Manipulation

GWM: Towards Scalable Gaussian World Models for Robotic Manipulation文章概括摘要1. 引言2. 相关工作3. 高斯世界模型&#xff08;Gaussian World Model&#xff09;3.1. 世界状态编码&#xff08;World State Encoding&#xff09;3.2. 基于扩散的动态建模&#xff08;Dif…

apache phoenix sql 命令大全详解

这是一份非常详细的 Apache Phoenix SQL 命令大全和详解。Phoenix 作为 HBase 上的 SQL 层&#xff0c;其语法大部分与标准 SQL 兼容&#xff0c;但也有许多针对 HBase 的特性扩展。核心概念 在开始之前&#xff0c;请记住 Phoenix 的两个核心概念&#xff1a; 主键&#xff08…

【代码讲解】SO-ARM100 双场景演示:手柄驱动 Mujoco 仿真 + 实机控制

视频讲解&#xff1a; 【代码讲解】SO-ARM100 双场景演示&#xff1a;手柄驱动 Mujoco 仿真 实机控制今天介绍下使用使用北通手柄通过控制 Mujoco 中的 SO-ARM100 机械臂&#xff0c;然后将关节数据通过 zmq 通信转发控制实际机械臂。 本期中会涉及如下点&#xff0c;需要注意…

「数据获取」《中国教育经费统计年鉴》(1997-2024)

01、数据简介《中国教育经费统计年鉴》作为我国教育经费领域的核心统计典籍&#xff0c;全面系统地呈现了全国各级各类教育经费的来源构成、分配流向与使用成效。其统计范围覆盖学前教育、基础教育、中等职业教育、高等教育及特殊教育等全学段&#xff0c;数据维度涵盖财政性教…

使用 Logspout 收集所有容器的

1.将所有容器的输出路由到远程 rsyslog 服务器1.修改 rsyslog 配置文件/etc/rsyslog.conf, 从中找到 “# Provides UDP sysilog recepion"语句。并将该处的以下两行配置代码行首的“#”字符删除&#xff08;取消注释&#xff09;[roothost1 ~]# vi /etc/rsyslog.conf [roo…

【智能化解决方案】基于多目标优化检索增强生成的智能行程规划方案

&#x1f4dd; 基于多目标优化的智能行程规划方案 1 用户需求分析与矩阵构建 1.1 核心用户信息提取 根据用户提供的年龄、出发地、目的地、出行时间等基本信息&#xff0c;我们首先构建一个用户特征向量&#xff1a; U {Age, Origin, Destination, TravelDate, Duration, Budg…

软件研发的演变

软件研发从一门手工作坊式的艺术&#xff0c;逐步演进为一门系统化、工程化、智能化的现代学科。其发展历程不仅体现了技术的飞跃&#xff0c;更反映了方法论、协作模式和思维方式的深刻变革。一、发展演变历程软件研发的演变可以大致划分为以下几个阶段&#xff1a;1. 软件作坊…

「日拱一码」091 机器学习——集成学习

目录 集成学习介绍 1. 核心思想 2. 为什么有效&#xff1f; 3. 主要流派与方法 A. 并行方法&#xff1a;Bagging (Bootstrap Aggregating) B. 串行方法&#xff1a;Boosting C. 堆叠法&#xff1a;Stacking 代码示例 Bagging 的代表 —— 随机森林 (Random Forest) 集成…

vscode实现第三方包的使用,cmake结合vcpkg(跨平台)

要使用cmake和vcpkg组织一个完整的现代cpp项目&#xff0c;一般来说需要三个文件vcpkg.json描述第三方依赖项//vcpkg.json {"dependencies": ["fmt"] }//安装,在vcpkg.json目录执行 vcpkg installCMakePresets.json定义项目的本质属性&#xff08;What&…

DevExpress中Word Processing Document API学习记录

文章目录1 文档结构划分2 文档操作基础2.1 Positions and Ranges2.2 Secitions2.3 Paragraphs2.4 Tables2.5 Lists2.6 Hyperlinks and Bookmarks2.7 Comments2.8 Headers and Footers2.9 Shapes and Pictures2.10 Watermarks2.11 Charts2.12 OLE Objects2.13 ActiveX Controls2…

Roo Code 的差异_快速编辑功能

什么是差异编辑&#xff1f; 简单来说&#xff0c;差异编辑就像是一位细心的装修师傅&#xff1a;他不会把整个房子拆掉重盖&#xff0c;而是精准地只修补需要改动的部分。Roo Code 的这项功能默认开启&#xff0c;它通过比对代码差异&#xff08;diff&#xff09;来实施修改&a…

【Axure高保真原型】标签树分类查询案例

今天和大家分享标签树分类查询案例的原型模版&#xff0c;效果包括&#xff1a; 树形分类——点击左侧树形里的箭头&#xff0c;可以展开或收起子级选项&#xff1b; 查询表格——点击标签树里的选项&#xff0c;如果是末级选项&#xff0c;可以筛选右侧表格用户标签&#xf…

容器化部署项目05

一、工作原理 镜像&#xff1a;容器的模板&#xff0c;包括容器运行时所需的数据 容器&#xff1a;运行中的进程&#xff0c;依赖镜像运行&#xff0c;镜像的具现化 镜像你可以把它看成Python中的类&#xff0c;而容器可以看做是类的实例化对象。 一个类可以有多个对象&#xf…

微信小程序 工作日历 周计划日报 修改等提报和状态展示功能,支持h5,Android ,ios,基于uniapp,适配vue2和vue3

Work-calendar 介绍 &#xff08;底部附链接&#xff09; 基于uni-calendar做的定制化开发&#xff0c;主要功能为工作日历展示和提报组件 ​ 1.支持周计划日报状态展示且可配置 ​ 2.支持农历展示配置&#xff0c;回到当日&#xff0c;月份切换 ​ 3.日历&#xff0c;周报…