提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、Springboot
    • 1.相关配置
    • 2.业务代码
      • 1)redis
      • 2)rocketMQ
      • 3)schedule超时检查
      • 4)service
      • 5)Controller
  • 二、结果

前言

基于RocketMQ设计秒杀。

要求:

  1. 秒杀商品LagouPhone,数量100个。
    
  2. 秒杀商品不能超卖。
    
  3. 抢购链接隐藏
    
  4. Nginx+Redis+RocketMQ+Tomcat+MySQL
    

一、Springboot

1.相关配置

IP
192.168.142.134 redis
192.168.142.135 rocketMQ

1.application.yml

server:port: 8083
spring:application:name: ms_rocketmqdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://***/springbootdata? serverTimezone=UTCname: **password: **jpa:database: MySQLshow-sql: truehibernate:naming:physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl #避免将驼峰命名转换为下划线命名redis:host: 192.168.142.135port: 6379jedis:pool:min-idle: 0max-idle: 8max-active: 80timeout: 3000max-wait: 30000ms
rocketmq:name-server: 192.168.142.134:9876producer:group: my_grp

2.pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.qch.rocketmq</groupId><artifactId>springboot_ms_rocketmq</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot_ms_rocketmq</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><rocketmq-spring-boot-starter-version>2.0.3</rocketmq-spring-boot-starter-version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.3</version></dependency><!--数据库--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!--rocketmq--><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>${rocketmq-spring-boot-starter-version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!--devtools热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>true</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork></configuration></plugin></plugins></build></project>

2.业务代码

1)redis

@Configurationpublic class RedisConfig {@Autowiredprivate RedisConnectionFactory factory;@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new StringRedisSerializer());redisTemplate.setConnectionFactory(factory);return redisTemplate;}}@Component
public class RedisService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public void setValue(String key, Object value, long expireTime) {redisTemplate.opsForValue().set(key, JSON.toJSONString(value));redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);}public Object getValue(String key, Class clazz) {Object cache = redisTemplate.opsForValue().get(key);if (cache == null) return null;Object obj = JSONObject.parseObject(cache.toString(), clazz);return obj;}}

2)rocketMQ

@Component
public class MyProducer {@Autowiredprivate RocketMQTemplate rocketMQTemplate;public void sendMessage(String message) {rocketMQTemplate.syncSend("tp_goods_order",message);}}@Slf4j
@Component
@RocketMQMessageListener(topic = "tp_goods_order",consumerGroup = "consumer_grp_order")
public class MyRocketListener implements RocketMQListener<String> {@Autowiredprivate GoodsService goodsService;@Overridepublic void onMessage(String message) {log.info("生成订单ing...: {}", message);try {OrderDO orders = JSONObject.parseObject(message, OrderDO.class);goodsService.proOrder(orders);} catch (Exception e) {e.printStackTrace();}}
}

3)schedule超时检查

@Slf4j
@Component
public class OrderTask {@Autowiredprivate OrderService ordersService;@Scheduled(cron = "*/30 * * * * ?")public void scanExpireOrder() {log.info("检查超时订单......");ordersService.passTimeProcess(20);}
}

4)service

GoodsService


@Slf4j
@Service
public class GoodsService {@Autowiredprivate GoodsRepository goodsRepository;@Autowiredprivate OrderService orderService;@Autowiredprivate RedisService redisService;@Autowiredprivate MyProducer myProducer;public List<GoodDO> getAllList() {List<GoodDO> list  = goodsRepository.findAll();if (!list.isEmpty()) {list.forEach(item -> {redisService.setValue("goods:" + item.getId(), item, 60);});}return list;}public GoodDO seckill(Long goodsId, String userId,String orderId) {GoodDO one = (GoodDO) redisService.getValue("goods:" + goodsId, GoodDO.class);if(one==null){one = goodsRepository.getOne(goodsId);}if (one != null && one.getReserveNum() > 0) {OrderDO orders = new OrderDO();orders.setGoodId(goodsId);orders.setUserId(userId);orders.setOrderNo(orderId);myProducer.sendMessage(JSON.toJSONString(orders));}return one;}@Transactionalpublic synchronized void proOrder(OrderDO orderDO) {try {log.info("orders: {}", orderDO);if (orderDO != null) {// 先查询一次缓存GoodDO cache = (GoodDO) redisService.getValue("goods:" + orderDO.getGoodId(), GoodDO.class);if (cache != null&& cache.getReserveNum() == 0) {return;}Optional<GoodDO> optional = goodsRepository.findById(orderDO.getGoodId());GoodDO goods = optional.isPresent() ? optional.get() : null;if (goods != null && goods.getReserveNum() > 0) {OrderDO orderServiceByUserIdAndGoodId = orderService.findByOrderId(orderDO.getOrderNo());if (orderServiceByUserIdAndGoodId != null)return;// 库存-1goods.setReserveNum(Math.max(goods.getReserveNum() - 1, 0));goodsRepository.save(goods);orderDO.setStatus(0);orderService.save(orderDO);// 刷新缓存redisService.setValue("goods:"+goods.getId(), goods, 60);}}} catch (Exception e) {e.printStackTrace();}}
}

OrderService


@Slf4j
@Service
public class OrderService {@Autowiredprivate GoodsRepository goodsRepository;@Autowiredprivate RedisService redisService;@Autowiredprivate OrderRepository orderRepository;public OrderDO findByOrderId(String orderId) {OrderDO orderDO = new OrderDO();orderDO.setOrderNo(orderId);Example<OrderDO> example = Example.of(orderDO);List<OrderDO> list = orderRepository.findAll(example);if (!list.isEmpty()) {return list.get(0);}return null;}public void save(OrderDO orderDO) {orderDO.setCreateTime(new Date());orderRepository.save(orderDO);}public void passTimeProcess(Integer limitTime) {OrderDO orderReq = new OrderDO();orderReq.setStatus(0);Example<OrderDO> example = Example.of(orderReq);List<OrderDO> list = orderRepository.findAll(example);long now = System.currentTimeMillis();if (!list.isEmpty()) {for (OrderDO orderDO : list) {if((orderDO.getCreateTime().getTime()+limitTime*1000)<=now){orderDO.setStatus(2);orderRepository.save(orderDO);Optional<GoodDO> byId = goodsRepository.findById(orderDO.getGoodId());if(byId.isPresent()){GoodDO goodDO = byId.get();goodDO.setReserveNum(goodDO.getReserveNum()+1);goodsRepository.save(goodDO);redisService.setValue("goods:" + goodDO.getId(), goodDO, 60);}}}}}
}

5)Controller

GoodController

@RestController
@RequestMapping("/good")
public class GoodController {@Autowiredprivate GoodsService goodsService;@RequestMapping("/list")public ModelAndView list() {ModelAndView view = new ModelAndView("/goods");List<GoodDO> list = goodsService.getAllList();view.addObject("userId", UUID.randomUUID().toString().replace("-",""));view.addObject("list", list);return view;}@RequestMapping("/seckill/{goodId}/{userId}")public ModelAndView pay(@PathVariable("goodId") String goodId,@PathVariable("userId") String userId ) {ModelAndView modelAndView=new ModelAndView("/wait.html");//生成主键订单idString orderId = UUID.randomUUID().toString().replace("-","");GoodDO goodDO = goodsService.seckill(Long.parseLong(goodId), userId, orderId);if(goodDO.getReserveNum()<=0){modelAndView.setViewName("/nogoods.html");}else {modelAndView.addObject("userId",userId);modelAndView.addObject("orderId",orderId);}return modelAndView;}
}

OrderController


@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate OrderService orderService;@RequestMapping("/payPage/{userId}/{orderId}")public ModelAndView payPage(@PathVariable("orderId") String orderId,@PathVariable("userId") String userId ) {ModelAndView modelAndView=new ModelAndView("/order.html");OrderDO byOrderId = orderService.findByOrderId(orderId);if (byOrderId==null){modelAndView.setViewName("/nogoods.html");}else {modelAndView.addObject("order", byOrderId);}return modelAndView;}@RequestMapping("/pay/{orderId}")public ModelAndView payPage(@PathVariable("orderId") String orderId ) {ModelAndView modelAndView=new ModelAndView("/success.html");OrderDO byOrderId = orderService.findByOrderId(orderId);if (byOrderId==null){modelAndView.addObject("msg","订单不存在");}else {if(byOrderId.getStatus().equals(0)){byOrderId.setPayTime(new Date());byOrderId.setStatus(1);orderService.save(byOrderId);modelAndView.addObject("msg","支付成功");}else if(byOrderId.getStatus().equals(1)){modelAndView.addObject("msg","订单已支付完成,请勿重复操作");}else {modelAndView.addObject("msg","订单超时已取消");}}return modelAndView;}
}

6)html

goods.html

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.10.0/jquery.js"></script><!-- Optional JavaScript --><!-- jQuery first, then Popper.js, then Bootstrap JS --><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script><script src="https://cdn.staticfile.org/jquery-cookie/1.4.1/jquery.cookie.min.js"></script><!-- Bootstrap CSS --><!--    <link rel="stylesheet" href="/css/bootstrap.css">--><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.css" rel="stylesheet"><script>$(function() {});</script><title>shopping</title></head>
<body>
<div class="container"><div class="row" th:each="good:${list}" style="margin: 10px 0; line-height: 30px;"><div class="col-md-2" th:text="'商品名称s: '+ ${good.goodName}"></div><div class="col-md-2" th:text="'库存: '+ ${good.reserveNum}"></div><div class="col-md-4"><a class="btn btn-sm" th:href="@{'/good/seckill/'+${good.id}+'/'+${userId}}">立即抢购</a></div></div>
</div></body>
</html>

wait.html

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.10.0/jquery.js"></script><!-- Optional JavaScript --><!-- jQuery first, then Popper.js, then Bootstrap JS --><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script><script src="https://cdn.staticfile.org/jquery-cookie/1.4.1/jquery.cookie.min.js"></script><!-- Bootstrap CSS --><!--    <link rel="stylesheet" href="/css/bootstrap.css">--><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.css" rel="stylesheet"><script>$(function(){setTimeout(function(){window.location.href = '/order/payPage/' + $('#userId').val() + '/' + $('#orderId').val()}, 1 * 1000)})</script><title>shopping</title></head>
<body>
<div class="container"><!-- Content here --><input type="text" id="userId" th:value="${userId}"><input type="hidden" id="orderId" th:value="${orderId}"><span id="emailspan" >请等待。。。</span></div></body>
</html>

order.html

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.10.0/jquery.js"></script><!-- Optional JavaScript --><!-- jQuery first, then Popper.js, then Bootstrap JS --><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script><script src="https://cdn.staticfile.org/jquery-cookie/1.4.1/jquery.cookie.min.js"></script><!-- Bootstrap CSS --><!--    <link rel="stylesheet" href="/css/bootstrap.css">--><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.css" rel="stylesheet"><script>$(function() {});</script><title>shopping</title></head>
<body>
<div class="container"><!-- Content here --><div class="col-md-2" th:text="'用户: '+ ${order.userId}"></div><div class="col-md-2" th:text="'订单号: '+ ${order.orderNo}"></div><div class="col-md-2" th:text="'商品编号: '+ ${order.goodId}"></div><div class="col-md-3" th:text="'下单时间: '+ ${#dates.format(order.createTime, 'yyyy-MM-dd HH:mm:ss')}"></div><div class="col-md-4"><a class="btn btn-sm btn-primary" th:href="@{'/order/pay/'+${order.orderNo}}">支付</a></div>
</div></body>
</html>

二、结果


跳转页面

支付页面

超时


RocketMQ作业相关推荐

  1. RocketMQ 使用及常见问题

    前言 本文档是针对RocketMQ使用及常见问题的说明. 一.获取项目.安装包及文档 1. alibaba/RocketMQ https://github.com/alibaba/RocketMQ 2 ...

  2. 再见,Kafka!RocketMQ已成气候!

    你知道吗?在消息中间件的编年史上,RocketMQ可谓独当一面!作为Apache 顶级项目(TLP),Apache RocketMQ 是国内首个非 Hadoop 生态体系的顶级项目,开源至今被全球广泛 ...

  3. 《开源软件开发导论》作业1

    关于开源的五个问题 作业要求 五个问题 1.开源的三个阶段具体含义是什么? 2.开源的版权如何使用? 3.一个开源项目如何得以正常运行? 4.开源如何保证安全? 5.对我们学生来说开源应该算什么? 作 ...

  4. rocketmq框架详细介绍

    文章目录 消息队列 应用场景 rocketmq 为什么选择RocketMQ消息队列 RocketMQ所拥有的功能 rocketmq应用场景 应用解耦 流量削峰 数据分发 异步处理 日志处理 顺序消息 ...

  5. Spark/Flink广播实现作业配置动态更新

    点击上方"zhisheng",选择"设为星标" 后台回复"ffa"可以查看 Flink 资料 前言 在实时计算作业中,往往需要动态改变一些配 ...

  6. rocketmq及控制台搭建

    前置·yum安装JDK1.8(openjdk) 先检查系统自带jdk rpm -qa |grep java rpm -qa |grep jdk rpm -qa |grep gcj 批量卸载系统自带所有 ...

  7. LeetCode简单题之在既定时间做作业的学生人数

    题目 给你两个整数数组 startTime(开始时间)和 endTime(结束时间),并指定一个整数 queryTime 作为查询时间. 已知,第 i 名学生在 startTime[i] 时开始写作业 ...

  8. 作业函数的定义与调用

    作业函数的定义与调用 在 OneFlow 中,将训练.预测任务封装在一个函数中,统称为作业函数(job function),作业函数联系用户的业务逻辑与 OneFlow 管理的计算资源. 在 OneF ...

  9. 华东理工大学计算机应用基础,最新华东理工大学计算机应用基础网上作业及全部答案...

    精品文档 华东理工大学计算机应用基础网上作业及全部答案 2012计算机应用基础答案: 一.选择题 1.文件夹是用来对应用程序.文件进行分组的,以下说法中()正确.答:一个文件夹内可以包含文件.应用程序 ...

最新文章

  1. mysql安装图形化管理界面phpMyAdmin
  2. python的re2和re区别_浅谈Python中re.match()和re.search()的使用及区别
  3. 进程间通信管道进阶篇:linux下dup/dup2函数的用法
  4. CF#420 B. Okabe and Banana Trees 思维|暴力|几何
  5. C++ 面向对象程序三大特性之 多态
  6. Ubuntu快速配置指南
  7. 设计模式系列之「装饰模式」
  8. poj 2329 Nearest number - 2 这道题广搜为什么就是wa啊!!求解
  9. linux中的jiffies变量
  10. JDK_API官方标准中文版(希望对大家有用)
  11. 下载地址url中带有中文是url转换方法
  12. CPU位数、操作系统位数、编译器位数
  13. 名字作诗,拯救诗歌的最后一根稻草
  14. 【第27篇】MobileNetV2:倒置残差和线性瓶颈
  15. Unity鼠标事件详解
  16. Uboot 使用串口Kermit协议传输文件
  17. 报表分析软件有哪些呢?不急不急,给你推荐几款好用的
  18. 【win11】关闭 Windows 安全中心中的Defender 防病毒保护
  19. 第十六届D2大会(I)
  20. padding样式属性

热门文章

  1. mysql zfs快照_Solaris ZFS 快照和克隆使用指南
  2. 志汇同城-同城小程序8.6开源版源码赠送米花同城6.6.6+微同城模板
  3. 详细介绍机器学习中的交叉验证方法
  4. 安卓应用上传应用宝遇到的问题
  5. 详解准确率acc、精确率p、准确率acc、F1 score
  6. SCS【11】单细胞ATAC-seq 可视化分析 (Cicero)
  7. 改变可以改变的事,接受不能改变的事--达达的马蹄 kanninstar
  8. ESP32-C3入门教程 网络篇④——IP地址的设置和获取
  9. 什么是接地电阻?如何测量防雷接地电阻
  10. ERP 系统对集团化企业管理的重要意义