当前位置:   article > 正文

12.1_黑马Redis实战篇Redis优化秒杀&Redis消息队列实现异步秒杀

12.1_黑马Redis实战篇Redis优化秒杀&Redis消息队列实现异步秒杀

目录

实战篇22

实战篇23

实战篇24

实战篇25

实战篇26

实战篇27

实战篇28

实战篇29

实战篇30


实战篇22

将任务分布给不同的线程去做,可以加快程序运行速度。

放到lua脚本,保证原子性。同时,这样的优化,可以减轻数据库的压力。 

  

实战篇23

thinking:sismember?

thinking:intValue?

intValue的用法_一般社员的博客-CSDN博客

实战篇24

thinking:阻塞队列?

BlockingQueue:当一个线程尝试在队列里面获取元素时,如果没有元素,线程就会被阻塞,直到队列中有元素,他才会被唤醒并且获取元素

thinking:如何翻译文档或者单词?

IDEA翻译插件Translation配置翻译引擎解决翻译错误的问题_飞牛鱼鱼的博客-CSDN博客

如果要翻译文档,出现这样的效果

则鼠标移到文档里面,然后右键,translate documentation

实战篇25

实战篇26

实战篇27

实战篇28

实战篇29 

不会漏消息

 

 

实战篇30

thinking:toString()、String.valueOf()、(String) 强转的区别?

toString()、String.valueOf()、(String) 强转的区别_string.valueof和tostring的区别-CSDN博客

String.valueof()与toString()方法的区别_stringvalueof和tostring_想起飞的张张的博客-CSDN博客

 thinking:beanutil.fillbeanwithmap?

hutool工具包快速入门_beanutil.fillbeanwithmap-CSDN博客

  1. package com.hmdp.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import com.hmdp.dto.Result;
  4. import com.hmdp.entity.VoucherOrder;
  5. import com.hmdp.mapper.VoucherOrderMapper;
  6. import com.hmdp.service.ISeckillVoucherService;
  7. import com.hmdp.service.IVoucherOrderService;
  8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  9. import com.hmdp.utils.RedisIdWorker;
  10. import com.hmdp.utils.UserHolder;
  11. import org.redisson.api.RLock;
  12. import org.redisson.api.RedissonClient;
  13. import org.springframework.aop.framework.AopContext;
  14. import org.springframework.core.io.ClassPathResource;
  15. import org.springframework.data.redis.connection.stream.*;
  16. import org.springframework.data.redis.core.StringRedisTemplate;
  17. import org.springframework.data.redis.core.script.DefaultRedisScript;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import javax.annotation.PostConstruct;
  21. import javax.annotation.Resource;
  22. import java.time.Duration;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.concurrent.ExecutorService;
  27. import java.util.concurrent.Executors;
  28. /**
  29. * <p>
  30. * 服务实现类
  31. * </p>
  32. *
  33. * @author 虎哥
  34. * @since 2021-12-22
  35. */
  36. @Service
  37. public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
  38. @Resource
  39. private ISeckillVoucherService seckillVoucherService;
  40. @Resource
  41. private RedisIdWorker redisIdWorker;
  42. @Resource
  43. private StringRedisTemplate stringRedisTemplate;
  44. @Resource
  45. private RedissonClient redissonClient;
  46. //提前读好这个文件,避免多次读取
  47. private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
  48. //因为是静态的,因此在静态代码块里面搞
  49. static {
  50. SECKILL_SCRIPT = new DefaultRedisScript<>();
  51. SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
  52. SECKILL_SCRIPT.setResultType(Long.class);
  53. }
  54. //private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 *1024);
  55. //创建线程池
  56. private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();
  57. //线程任务 这个要在初始化的时候就要完成了。
  58. @PostConstruct
  59. private void init() {
  60. SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
  61. }
  62. private class VoucherOrderHandler implements Runnable {
  63. String queueName = "stream.orders";
  64. @Override
  65. public void run() {
  66. while (true) {
  67. try {
  68. //1. 获取消息中的订单信息
  69. List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
  70. Consumer.from("g1", "c1"),
  71. StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
  72. StreamOffset.create(queueName, ReadOffset.lastConsumed())
  73. );
  74. //2. 判断消息获取是否成功
  75. if (list == null || list.isEmpty()) {
  76. //2.1 如果获取失败,说明pending-list没有异常消息,结束循环
  77. break;
  78. }
  79. //3.解析消息中的订单消息
  80. MapRecord<String, Object, Object> record = list.get(0);
  81. Map<Object, Object> values = record.getValue();
  82. VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
  83. //4.如果获取成功,可以下单
  84. handleVoucherOrder(voucherOrder);
  85. //5.ACK确认
  86. stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
  87. } catch (Exception e) {
  88. log.error("处理pending-list订单异常", e);
  89. handlePendingList();
  90. try {
  91. Thread.sleep(200);
  92. } catch (InterruptedException ex) {
  93. ex.printStackTrace();
  94. }
  95. }
  96. }
  97. }
  98. private void handlePendingList() {
  99. while (true) {
  100. try {
  101. //1. 获取pending-list中的订单信息
  102. List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
  103. Consumer.from("g1", "c1"),
  104. StreamReadOptions.empty().count(1),
  105. StreamOffset.create(queueName, ReadOffset.from("0"))
  106. );
  107. //2. 判断消息获取是否成功
  108. if (list == null || list.isEmpty()) {
  109. //2.1 如果获取失败,说明没有消息,继续下一次循环
  110. continue;
  111. }
  112. //3.解析消息中的订单消息
  113. MapRecord<String, Object, Object> record = list.get(0);
  114. Map<Object, Object> values = record.getValue();
  115. VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
  116. //4.如果获取成功,可以下单
  117. handleVoucherOrder(voucherOrder);
  118. //5.ACK确认
  119. stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
  120. } catch (Exception e) {
  121. log.error("处理订单异常", e);
  122. }
  123. }
  124. }
  125. }
  126. private void handleVoucherOrder(VoucherOrder voucherOrder) {
  127. //获取用户
  128. Long userId = voucherOrder.getUserId();
  129. //创建锁对象
  130. RLock lock = redissonClient.getLock("lock:order:" + userId);
  131. //获取锁
  132. //不传参数,代表我失败了立即返回
  133. boolean isLock = lock.tryLock();
  134. //判断是否获取锁成功
  135. if (!isLock) {
  136. //获取锁失败,返回错误或重试
  137. log.error("不允许重复下单");
  138. return;
  139. }
  140. try {
  141. proxy.createVoucherOrder(voucherOrder);
  142. } finally {
  143. //释放锁
  144. lock.unlock();
  145. }
  146. }
  147. private IVoucherOrderService proxy;
  148. @Override
  149. public Result seckillVoucher(Long voucherId) {
  150. //获取用户
  151. Long userId = UserHolder.getUser().getId();
  152. //获取订单id
  153. long orderId = redisIdWorker.nextId("order");
  154. //1.执行lua脚本
  155. Long result = stringRedisTemplate.execute(
  156. SECKILL_SCRIPT,
  157. Collections.emptyList(),
  158. voucherId.toString(), userId.toString(), String.valueOf(orderId)
  159. );
  160. //2.判断结果是为0
  161. int r = result.intValue();
  162. if (r != 0) {
  163. //2.1 不为0.代表没有购买资格
  164. return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
  165. }
  166. //3 获取代理对象
  167. proxy = (IVoucherOrderService) AopContext.currentProxy();
  168. //4. 返回订单id
  169. return Result.ok(orderId);
  170. }
  171. //@Override
  172. // public Result seckillVoucher(Long voucherId) {
  173. // //获取用户
  174. // Long userId = UserHolder.getUser().getId();
  175. // //1.执行lua脚本
  176. // Long result = stringRedisTemplate.execute(
  177. // SECKILL_SCRIPT,
  178. // Collections.emptyList(),
  179. // voucherId.toString(), userId.toString()
  180. // );
  181. // //2.判断结果是为0
  182. // int r = result.intValue();
  183. // if(r != 0){
  184. // //2.1 不为0.代表没有购买资格
  185. // return Result.fail(r ==1 ? "库存不足" : "不能重复下单");
  186. // }
  187. // //2.2 为0,有购买资格,把下单信息保存到阻塞队列
  188. // VoucherOrder voucherOrder =new VoucherOrder();
  189. // //2.3 订单id
  190. // long orderId = redisIdWorker.nextId("order");
  191. // voucherOrder.setId(orderId);
  192. // //2.4 用户id
  193. // voucherOrder.setUserId(userId);
  194. // //2.5 代金券id
  195. // voucherOrder.setVoucherId(voucherId);
  196. // //2.6 放入阻塞队列
  197. // orderTasks.add(voucherOrder);
  198. // //3 获取代理对象
  199. // proxy = (IVoucherOrderService) AopContext.currentProxy();
  200. // //4. 返回订单id
  201. // return Result.ok(orderId);
  202. // }
  203. @Transactional
  204. public void createVoucherOrder(VoucherOrder voucherOrder) {
  205. //5,一人一单
  206. Long userId = voucherOrder.getUserId();
  207. //5.1 查询订单
  208. int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
  209. //5.2 判断是否存在
  210. if (count > 0) {
  211. // 用户已经购买过了
  212. log.error("用户已经购买过一次");
  213. return;
  214. }
  215. //6,扣减库存
  216. boolean success = seckillVoucherService
  217. .update().setSql("stock = stock - 1")
  218. .eq("voucher_id", voucherOrder.getVoucherId())
  219. .gt("stock", 0)
  220. .update();
  221. if (!success) {
  222. //扣除失败
  223. log.error("库存不足!");
  224. return;
  225. }
  226. //7,创建订单
  227. save(voucherOrder);
  228. }
  229. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/682886
推荐阅读
相关标签
  

闽ICP备14008679号