RocketMQ实现一个简单的秒杀接口

news/2025/2/13 20:04:07/

预设场景:

“秒杀”这一词多半出现在购物方面,但是又不单单只是购物,比如12306购票和学校抢课(大学生的痛苦)也可以看成一个秒杀。秒杀应该是一个“三高”,这个三高不是指高血脂,高血压和高血糖。而是指“高并发”,“高性能”和“高可用”。

假设有一百个库存商品需要抢购,可以试用mq进行削峰,防止宕机。
在这里插入图片描述
在这里插入图片描述

1.创建rocketmq server模块。

1.1. 配置相关文件

  1. springboot2.6.13版本
    在这里插入图片描述
  2. xml依赖
 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- rocketmq --><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.3</version></dependency></dependencies>
  1. application.properties相关配置
#应用名
spring.application.name=seckill-server
server.port=8081
rocketmq.producer.groupName=${spring.application.name}
# mq的nameserver地址
rocketmq.producer.namesrvAddr=127.0.0.1:9876

1.2. controller代码

  1. 这里我使用1000冗余数量,控制接口访问数,正常来讲,应该使用中间件去同步真实库存,我这里省略了。
  2. 我这里的业务逻辑比较简单,可以根据自身需要更改逻辑。
@RestController
public class OpenOrderController{int redundancy = 1000;@Autowiredprivate RocketMQTemplate rocketMQTemplate;@GetMapping("/secKill")public String secKill(String id){redundancy--;if(redundancy > 0){rocketMQTemplate.convertAndSend("seckill-topic", id);return id+"正在抢购中请等待";}else{return "商品已售完";}}
}

2.创建rocketmq-consumer模块

1.1. 配置相关文件

  1. xml依赖配置
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.3</version></dependency>
  1. application.properties相关配置
spring.application.name=seckill-consumer
server.port=8082
rocketmq.consumer.group=${spring.application.name}
rocketmq.name-server=127.0.0.1:9876

1.2. controller代码

这里是真实的消息处理,springboot的监听处理极其简化了监听器的配置过程。
这里吧库存设置成一个简单的成员变量,实际上在分布式项目中可能使用redis同步真实库存。

在真实的场景中我们可以在这一步进行鉴权,是不是目标用户(黑户),生成订单等,发送短信(回调执行结果)等操作。由于已经由MQ进行了流量削峰,这一步可以进行更多的操作,有条不紊的进行业务逻辑的执行,

下面是示例代码:

@Component
@RocketMQMessageListener(topic = "seckill-topic", consumerGroup = "seckill-consumer-group")
public class SeckillConsumer implements RocketMQListener<String> {int realInventory = 100;@Overridepublic void onMessage(String id) {// 处理秒杀请求// 执行库存扣减和订单生成等操作// 返回秒杀结果给用户realInventory--;if(realInventory >= 0){System.out.println("当前商品剩余"+realInventory);System.out.println(id + "抢到商品");}else{System.out.println("商品已售完");}}
}

3.创建测试示例

  1. 使用1w线程发送1w请求进行接口测试。
public class HttpTest {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(1000);for (int i = 0; i < 10000; i++) {executorService.execute(() -> {try {URL url = new URL("http://localhost:8081/secKill?id=" + UUID.randomUUID().toString());HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.connect();int responseCode = connection.getResponseCode();if (responseCode == HttpURLConnection.HTTP_OK) {System.out.println("Request success!");} else {System.out.println("Request failed!");}} catch (IOException e) {e.printStackTrace();}});}executorService.shutdown();}
}
  1. 测试结果
    测试代码控制台
    在这里插入图片描述
    生产者控制台
    在这里插入图片描述
    消费者控制台
    在这里插入图片描述

这里抛出一个问题?
为什么会出现消息的乱序消费呢?如何实现顺序消费呢?

答:springboot默认是异步多线程消费的,无法保证顺序。
consumeMode = ConsumeMode.ORDERLY ConsumeMode.ORDERLY的作用是让消费者单线程顺序接收消息,从而保证消息的全局顺序


http://www.ppmy.cn/news/103438.html

相关文章

js中将字符串[user[name=jjdf,age=1],user[name=ggdf,age=5]]转为json串对象

js中将字符串转为json串对象 var str "[user[namejjdf,age1],user[nameggdf,age5]]"; string2json(str); function string2json(str){str str.substring(1,str.length-1);str str.replaceAll(/\user/g,)str str.replaceAll("","\":\"&q…

PyTorch-优化器以及网络模型的修改

目的&#xff1a;优化器可以将神经网络中的参数根据损失函数和反向传播来进行优化&#xff0c;以得到最佳的参数值&#xff0c;让模型预测的更准确。 1. SGD import torch import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flat…

K8S headless 与 clusterip的区别

在Kubernetes中&#xff0c;Headless Service和ClusterIP Service都是Service资源的类型&#xff0c;它们都用于为应用程序提供服务发现和负载均衡等功能。下面是Headless Service和ClusterIP Service的区别&#xff1a; 一、Headless Service Headless Service是一种不使用Cl…

OLAP系列:三、clickhouse Docker集群部署指南(3分片1副本模式)

一、背景 一是为了学习容器&#xff0c;另外也是帮助一些同学解决机器资源紧缺&#xff0c;能够在一台是宿主机部署一套clickhouse集群服务。 本章内容适合开发测试环境使用&#xff0c;生产环境还需要更多细节的处理工作&#xff0c;只能作为参考。 二、部署准备 1、机器准…

如何快速判断GitLab 是否出现 OOM

查看系统日志&#xff1a; 使用 dmesg 命令来查看系统日志&#xff0c;搜索 Out of memory 关键字&#xff1a; sudo dmesg | grep -i "out of memory"如果输出结果中包含 Out of memory 或 oom-killer 等关键字&#xff0c;则表示系统出现了 OOM。 查看 GitLab 日…

Flutter 笔记 | Flutter 文件IO、网络请求、JSON、日期与国际化

文件IO操作 Dart的 IO 库包含了文件读写的相关类&#xff0c;它属于 Dart 语法标准的一部分&#xff0c;所以通过 Dart IO 库&#xff0c;无论是 Dart VM 下的脚本还是 Flutter&#xff0c;都是通过 Dart IO 库来操作文件的&#xff0c;不过和 Dart VM 相比&#xff0c;Flutte…

LeetCode刷题(ACM模式)-03哈希表

参考引用&#xff1a;代码随想录 注&#xff1a;每道 LeetCode 题目都使用 ACM 代码模式&#xff0c;可直接在本地运行&#xff0c;蓝色字体为题目超链接 0. 哈希表理论基础 0.1 哈希表 哈希表&#xff08;Hash table&#xff0c;也称散列表&#xff09;是根据关键码的值而直…

2023下半年想做自媒体,这3款抖音爆款配音工具建议收藏!

现在自媒体成为趋势&#xff0c;越来越多的人想成为视频博主&#xff0c;但不知道做好了视频应该要怎么配音&#xff1f;不知道大家平时刷视频的时候有没有发现&#xff0c;现在很多短视频都会用到配音&#xff0c;一个有趣好听有情感的配音可以为自己的视频配音增加亮点。因此…