当前位置: 首页 > news >正文

Day5-微服务-RocketMQ具体项目的应用场景

场景:用户购票,在服务端,校验验证码,拿到锁,选座购票,那么现在,拿锁和选座购票中插入一个异步线程,告诉用户你有资格购票或者已经下单成功,不然一直在等待,给一个快速响应,前端轮询购票结果,用户不用担心中间的选座购票,轮询带来的压力对于后端相对较小。下单和查询分为两个模块。

现在把异步操作分为出票模块,从服务端发送消息给MQ,查看是否有出票的请求,MQ有一个生产者和服务者。现在我们使用MQ的异步,存储消息,即使中间出错也会保存出错前的消息。

生产者和消费者和主题,消息就是跟主题做关联,购票也可以是一个主题。

普通消息发送


顺序消息发送

延迟消息发送

批量消息发送

事务消息发送:解决分布式事务。

新版本默认自动开启接收消息,

消费者

消费概念
Push消费
Pull消费

消息可以被多个消费者处理也可以被单个处理。

启动NameServer和Broker
修改runbroker.cmd的配置

修改为一个512m,另一个1g,-Xms:启动时就占用的内存,-Xmx:最大可用内存。

另外把堆外内存改为1g,本地生产15g太大了。

在conf目录下的broker.conf文件下添加存放rocketmq数据的地方。


修改完之后先启动NameServerv:

看到这个显示这个就成功了:

然后再启动runbroker使用broker.conf启动

启动成功:

发送消息测试:

使用RocketMQ Assistant的GUI客户端并连接:

可以看到主题中有了1000条消息:

有一部分在发送MQ之前,一部分在发送之后。


首先导入依赖:

<dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.3</version> </dependency>

SpringBoot3并不认识这个文件

一般消费者个生产者不在同一台机器。

实例,在火车购票系统中,为了防止一个时间段的大量请求压跨系统,就要引入RocketMQ的消息队列,先拿号在买票,而不是同时一下子处理所有人的购票请求。

购买车票成功后,就会显示主题的状态。

定义处理主题为确认订单的消费者:

@Service @RocketMQMessageListener(consumerGroup = "default", topic = "CONFIRM_ORDER") //消息主题 public class ConfirmOrderConsumer implements RocketMQListener<MessageExt> { private static final Logger LOG = LoggerFactory.getLogger(ConfirmOrderConsumer.class); @Resource private ConfirmOrderService confirmOrderService; @Override public void onMessage(MessageExt messageExt) { byte[] body = messageExt.getBody(); LOG.info("ROCKETMQ收到消息:{}", new String(body)); ConfirmOrderDoReq req = JSON.parseObject(new String(body), ConfirmOrderDoReq.class); MDC.put("LOG_ID", req.getLogId()); confirmOrderService.doConfirm(req); } }

在确认订单之前的controller中注入MQ的templete:

@Service public class BeforeConfirmOrderServiceImpl implements BeforeConfirmOrderService { private static final Logger LOG = LoggerFactory.getLogger(BeforeConfirmOrderServiceImpl.class); @Resource private ConfirmOrderMapper confirmOrderMapper; @Resource private SkTokenService skTokenService; @Resource ConfirmOrderService confirmOrderService; //SpringBoot3注入不成功 @Resource public RocketMQTemplate rocketMQTemplate; @SentinelResource(value = "beforeDoConfirm", blockHandler = "beforeDoConfirmBlock") @Override public Long beforeDoConfirm(ConfirmOrderDoReq req) { req.setMemberId(LoginMemberContext.getId()); Long id = null; // 根据前端传值,加入排队人数 for (int i = 0; i < req.getLineNumber() + 1; i++) { req.setMemberId(LoginMemberContext.getId()); // 校验令牌余量 boolean validSkToken = skTokenService.validSkToken(req.getDate(), req.getTrainCode(), LoginMemberContext.getId()); if (validSkToken) { LOG.info("令牌校验通过"); } else { LOG.info("令牌校验不通过"); throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_SK_TOKEN_FAIL); } //获取车次锁 //RedisKeyPreEnum.CONFIRM_ORDER + "-" + DateUtil.formatDate(req.getDate())+"-"+req. Date date = req.getDate(); String trainCode = req.getTrainCode(); String start = req.getStart(); String end = req.getEnd(); List<ConfirmOrderTicketReq> tickets = req.getTickets(); // 保存确认订单表,状态初始 DateTime now = DateTime.now(); ConfirmOrder confirmOrder = new ConfirmOrder(); confirmOrder.setId(SnowUtil.getSnowflakeNextId()); confirmOrder.setCreateTime(now); confirmOrder.setUpdateTime(now); confirmOrder.setMemberId(req.getMemberId()); confirmOrder.setDate(date); confirmOrder.setTrainCode(trainCode); confirmOrder.setStart(start); confirmOrder.setEnd(end); confirmOrder.setDailyTrainTicketId(req.getDailyTrainTicketId()); confirmOrder.setStatus(ConfirmOrderStatusEnum.INIT.getCode()); confirmOrder.setTickets(JSON.toJSONString(tickets)); confirmOrderMapper.insert(confirmOrder); ConfirmOrderDoReq confirmOrderDoReq = new ConfirmOrderDoReq(); confirmOrderDoReq.setDate(req.getDate()); confirmOrderDoReq.setTrainCode(req.getTrainCode()); confirmOrderDoReq.setLogId(MDC.get("LOG_ID")); // 发送MQ排队购票 String reqJson = JSON.toJSONString(confirmOrderDoReq); LOG.info("排队购票,发送mq开始,消息:{}", reqJson); //发送的主题,购票的请求转换为string rocketMQTemplate.convertAndSend(RocketMQTopicEnum.CONFIRM_ORDER.getCode(), reqJson); LOG.info("排队购票,发送mq结束"); confirmOrderService.doConfirm(confirmOrderDoReq); id = confirmOrder.getId(); } //返回最后一个id return id; } @Override public void beforeDoConfirmBlock(ConfirmOrderDoReq req, BlockException e) { LOG.info("购票请求被限流:{}", req); throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION); } }

关键:再发送MQ之前,订单表先保存下来。即使MQ丢失,订单表的数据任然存在。优先保证数据的准确性。

获取所得操作必须在正常的购票逻辑中。

打印日志:

那票但是拿不到锁,拿令牌就有资格购票,所以有了排队机制,分布式锁是为了防止超卖。

MQ就告诉出票模块有订单产生,出票模块只关心出票不用关心给谁,按照订单出票。

后端经常处理大批量数据,所以分页会减缓压力,分页分页的出票。

订单轮询查询:

用来给正在排队的订单提供一个结果,让用户知道前面还有多少个订单。

http://www.zskr.cn/news/1490605.html

相关文章:

  • 社区医院后台管理系统(SpringBoot+Java+MySQL,含完整可运行源码与数据库脚本)
  • OpenWrt-Rpi网络优化终极指南:5步实现游戏零延迟体验
  • 5分钟上手Villus:Vue.js项目集成GraphQL的极速入门教程
  • 手把手教你:华为USG6000防火墙BootROM菜单的7个隐藏功能详解(含密码重置与版本回退)
  • 2026年知名的耐高温pph球阀/pph气动双由令球阀源头工厂推荐 - 行业平台推荐
  • ESP32板载LED不亮?别慌,手把手教你用Arduino IDE搞定GPIO2闪烁(附Boot键下载避坑指南)
  • 2026年热门的佛山物流折叠仓储笼/可堆叠折叠仓储笼/仓库用折叠仓储笼公司选择指南 - 品牌宣传支持者
  • 2026年热门的镇江散热器/镇江铲片散热器/储能散热器长期合作厂家推荐 - 品牌宣传支持者
  • 小气所学习笔记——大洋环流
  • OpenWrt-Rpi QoS流量控制技术深度解析
  • 2026年适合化工的江苏pph电动双由令球阀/江苏pph双由令球阀/江苏pph电动法兰球阀/江苏耐高温pph球阀优质供应商推荐 - 品牌宣传支持者
  • 别再手动算DH参数了!用Python Robotics Toolbox快速建模你的六轴机械臂
  • 【含四月底最新安装包】保姆级拆解 OpenClaw 部署,零基础零代码一键完成
  • 从下棋到导航:聊聊启发式搜索(A*算法)如何悄悄改变你的日常生活
  • 手把手教你用MATLAB scatter3搞定科研论文里的三维散点图(含坐标轴美化与导出高清图)
  • Go学习第2天:程序结构+基础语法+数据类型
  • 主动双目深度图转3D点云全解|全网独家复现内参标定+彩色点生成+像素投影、助力机器人抓取、AGV避障、工业三维测量落地部署
  • YOLOv13涨点改进| CVPR 2026 | 独家特征融合改进篇| 引入MCA多尺度颜色注意力融合,发论文热点创新,动态选择更重要的通道和信息,提升多尺特征融合质量,目标检测,暗光增强任务高效涨点
  • Horizon UAG网关服务器部署后,别忘了做这5项关键安全与优化设置
  • 别再一个个改文件权限了!阿里云OSS存储桶ACL‘公共读’一键配置保姆级教程
  • 六、消息队列 MQ
  • 别再瞎调学习率了!用PyTorch的CosineAnnealingWarmRestarts让你的模型收敛又快又稳
  • 保姆级教程:手把手教你用GEE计算Landsat影像的缨帽变换(亮度/绿度/湿度)
  • 告别纯GUI操作:用APDL命令流批量处理x_t模型并自动分析
  • 2026年简易货梯实测评测:广州液压货梯/广州直顶式升降机/广州直顶式货梯/广州简易升降机/广州简易升降货梯/广州简易货梯/选择指南 - 优质品牌商家
  • ST LIS2DH12TR渠道商
  • 信息学奥赛图论入门:从‘香甜的黄油’这道题,理解最短路径算法的实际应用场景
  • c++数据结构之c++11(二)
  • 2026年口碑好的抛丸机叶轮/盐城抛丸机配件/盐城抛丸机户罩/抛丸机定向套公司哪家好 - 行业平台推荐
  • Halcon算子参数里的三个冒号(:)到底怎么用?新手避坑指南与实战解析