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

预设场景:

“秒杀”这一词多半出现在购物方面,但是又不单单只是购物,比如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;
    @Autowired
    private 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;
    @Override
    public 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的作用是让消费者单线程顺序接收消息,从而保证消息的全局顺序

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是使用RocketMQ实现一个简单的消息队列的步骤: 1. 下载RocketMQ 首先你需要从RocketMQ官网下载RocketMQ压缩包,并解压到你的本地目录。 2. 启动Name Server和Broker 在解压后的RocketMQ目录下,进入bin目录,执行以下命令启动Name Server和Broker: ``` sh mqnamesrv sh mqbroker -n localhost:9876 ``` 3. 创建Topic 在RocketMQ中,消息是发布到Topic中的,所以你需要先创建一个Topic。可以使用RocketMQ提供的命令行工具创建Topic: ``` sh bin/mqadmin updateTopic -n localhost:9876 -t testTopic ``` 4. 发送消息 使用Producer发送消息到Topic中,以下是Java代码示例: ```java import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message; public class Producer { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("producer_group"); producer.setNamesrvAddr("localhost:9876"); producer.start(); Message msg = new Message("testTopic", "tagA", "Hello RocketMQ".getBytes()); producer.send(msg); producer.shutdown(); } } ``` 5. 消费消息 使用Consumer消费Topic中的消息,以下是Java代码示例: ```java import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; public class Consumer { public static void main(String[] args) throws Exception { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group"); consumer.setNamesrvAddr("localhost:9876"); consumer.subscribe("testTopic", "*"); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { for (MessageExt msg : msgs) { System.out.println(new String(msg.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); System.out.println("Consumer started."); } } ``` 以上就是使用RocketMQ实现简单消息队列的步骤。当然,还有很多高级特性,比如延时消息、事务消息等,你可以自行了解和实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只会写bug的靓仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值