赞
踩
将任务分布给不同的线程去做,可以加快程序运行速度。
放到lua脚本,保证原子性。同时,这样的优化,可以减轻数据库的压力。
BlockingQueue:当一个线程尝试在队列里面获取元素时,如果没有元素,线程就会被阻塞,直到队列中有元素,他才会被唤醒并且获取元素
如果要翻译文档,出现这样的效果
则鼠标移到文档里面,然后右键,translate documentation
不会漏消息
toString()、String.valueOf()、(String) 强转的区别_string.valueof和tostring的区别-CSDN博客
String.valueof()与toString()方法的区别_stringvalueof和tostring_想起飞的张张的博客-CSDN博客
- package com.hmdp.service.impl;
-
- import cn.hutool.core.bean.BeanUtil;
- import com.hmdp.dto.Result;
- import com.hmdp.entity.VoucherOrder;
- import com.hmdp.mapper.VoucherOrderMapper;
- import com.hmdp.service.ISeckillVoucherService;
- import com.hmdp.service.IVoucherOrderService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.hmdp.utils.RedisIdWorker;
- import com.hmdp.utils.UserHolder;
- import org.redisson.api.RLock;
- import org.redisson.api.RedissonClient;
- import org.springframework.aop.framework.AopContext;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.data.redis.connection.stream.*;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.script.DefaultRedisScript;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
-
- import javax.annotation.PostConstruct;
- import javax.annotation.Resource;
- import java.time.Duration;
- import java.util.Collections;
- import java.util.List;
- import java.util.Map;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- /**
- * <p>
- * 服务实现类
- * </p>
- *
- * @author 虎哥
- * @since 2021-12-22
- */
- @Service
- public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
- @Resource
- private ISeckillVoucherService seckillVoucherService;
- @Resource
- private RedisIdWorker redisIdWorker;
- @Resource
- private StringRedisTemplate stringRedisTemplate;
- @Resource
- private RedissonClient redissonClient;
-
- //提前读好这个文件,避免多次读取
- private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
-
- //因为是静态的,因此在静态代码块里面搞
- static {
- SECKILL_SCRIPT = new DefaultRedisScript<>();
- SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
- SECKILL_SCRIPT.setResultType(Long.class);
- }
-
- //private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 *1024);
- //创建线程池
- private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();
- //线程任务 这个要在初始化的时候就要完成了。
-
- @PostConstruct
- private void init() {
- SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
- }
-
- private class VoucherOrderHandler implements Runnable {
- String queueName = "stream.orders";
-
- @Override
- public void run() {
- while (true) {
- try {
- //1. 获取消息中的订单信息
- List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
- Consumer.from("g1", "c1"),
- StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
- StreamOffset.create(queueName, ReadOffset.lastConsumed())
- );
- //2. 判断消息获取是否成功
- if (list == null || list.isEmpty()) {
- //2.1 如果获取失败,说明pending-list没有异常消息,结束循环
- break;
- }
- //3.解析消息中的订单消息
- MapRecord<String, Object, Object> record = list.get(0);
- Map<Object, Object> values = record.getValue();
- VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
- //4.如果获取成功,可以下单
- handleVoucherOrder(voucherOrder);
- //5.ACK确认
- stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
- } catch (Exception e) {
- log.error("处理pending-list订单异常", e);
- handlePendingList();
- try {
- Thread.sleep(200);
- } catch (InterruptedException ex) {
- ex.printStackTrace();
- }
- }
- }
- }
-
- private void handlePendingList() {
- while (true) {
- try {
- //1. 获取pending-list中的订单信息
- List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
- Consumer.from("g1", "c1"),
- StreamReadOptions.empty().count(1),
- StreamOffset.create(queueName, ReadOffset.from("0"))
- );
- //2. 判断消息获取是否成功
- if (list == null || list.isEmpty()) {
- //2.1 如果获取失败,说明没有消息,继续下一次循环
- continue;
- }
- //3.解析消息中的订单消息
- MapRecord<String, Object, Object> record = list.get(0);
- Map<Object, Object> values = record.getValue();
- VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);
- //4.如果获取成功,可以下单
- handleVoucherOrder(voucherOrder);
- //5.ACK确认
- stringRedisTemplate.opsForStream().acknowledge(queueName, "g1", record.getId());
- } catch (Exception e) {
- log.error("处理订单异常", e);
- }
- }
- }
- }
-
- private void handleVoucherOrder(VoucherOrder voucherOrder) {
- //获取用户
- Long userId = voucherOrder.getUserId();
- //创建锁对象
- RLock lock = redissonClient.getLock("lock:order:" + userId);
- //获取锁
- //不传参数,代表我失败了立即返回
- boolean isLock = lock.tryLock();
- //判断是否获取锁成功
- if (!isLock) {
- //获取锁失败,返回错误或重试
- log.error("不允许重复下单");
- return;
- }
- try {
- proxy.createVoucherOrder(voucherOrder);
- } finally {
- //释放锁
- lock.unlock();
- }
- }
-
- private IVoucherOrderService proxy;
-
- @Override
- public Result seckillVoucher(Long voucherId) {
- //获取用户
- Long userId = UserHolder.getUser().getId();
- //获取订单id
- long orderId = redisIdWorker.nextId("order");
-
- //1.执行lua脚本
- Long result = stringRedisTemplate.execute(
- SECKILL_SCRIPT,
- Collections.emptyList(),
- voucherId.toString(), userId.toString(), String.valueOf(orderId)
- );
- //2.判断结果是为0
- int r = result.intValue();
- if (r != 0) {
- //2.1 不为0.代表没有购买资格
- return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
- }
- //3 获取代理对象
- proxy = (IVoucherOrderService) AopContext.currentProxy();
- //4. 返回订单id
- return Result.ok(orderId);
- }
-
- //@Override
- // public Result seckillVoucher(Long voucherId) {
- // //获取用户
- // Long userId = UserHolder.getUser().getId();
- // //1.执行lua脚本
- // Long result = stringRedisTemplate.execute(
- // SECKILL_SCRIPT,
- // Collections.emptyList(),
- // voucherId.toString(), userId.toString()
- // );
- // //2.判断结果是为0
- // int r = result.intValue();
- // if(r != 0){
- // //2.1 不为0.代表没有购买资格
- // return Result.fail(r ==1 ? "库存不足" : "不能重复下单");
- // }
- // //2.2 为0,有购买资格,把下单信息保存到阻塞队列
- // VoucherOrder voucherOrder =new VoucherOrder();
- // //2.3 订单id
- // long orderId = redisIdWorker.nextId("order");
- // voucherOrder.setId(orderId);
- // //2.4 用户id
- // voucherOrder.setUserId(userId);
- // //2.5 代金券id
- // voucherOrder.setVoucherId(voucherId);
- // //2.6 放入阻塞队列
- // orderTasks.add(voucherOrder);
- // //3 获取代理对象
- // proxy = (IVoucherOrderService) AopContext.currentProxy();
- // //4. 返回订单id
- // return Result.ok(orderId);
- // }
- @Transactional
- public void createVoucherOrder(VoucherOrder voucherOrder) {
- //5,一人一单
- Long userId = voucherOrder.getUserId();
- //5.1 查询订单
- int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
- //5.2 判断是否存在
- if (count > 0) {
- // 用户已经购买过了
- log.error("用户已经购买过一次");
- return;
- }
- //6,扣减库存
- boolean success = seckillVoucherService
- .update().setSql("stock = stock - 1")
- .eq("voucher_id", voucherOrder.getVoucherId())
- .gt("stock", 0)
- .update();
- if (!success) {
- //扣除失败
- log.error("库存不足!");
- return;
- }
- //7,创建订单
- save(voucherOrder);
- }
- }
-
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。