diff --git a/src/main/java/com/itstyle/seckill/service/IRedPacketService.java b/src/main/java/com/itstyle/seckill/service/IRedPacketService.java new file mode 100644 index 0000000..03baaa3 --- /dev/null +++ b/src/main/java/com/itstyle/seckill/service/IRedPacketService.java @@ -0,0 +1,23 @@ +package com.itstyle.seckill.service; + +import com.itstyle.seckill.common.entity.Result; + +public interface IRedPacketService { + + + /** + * 秒杀一 + * @param redPacketId + * @return + */ + Result startSeckil(long redPacketId,int userId); + + /** + * 秒杀二 + * @param redPacketId + * @param userId + * @return + */ + Result startTwoSeckil(long redPacketId,int userId); + +} diff --git a/src/main/java/com/itstyle/seckill/service/impl/RedPacketService.java b/src/main/java/com/itstyle/seckill/service/impl/RedPacketService.java new file mode 100644 index 0000000..61576c3 --- /dev/null +++ b/src/main/java/com/itstyle/seckill/service/impl/RedPacketService.java @@ -0,0 +1,138 @@ +package com.itstyle.seckill.service.impl; + +import com.itstyle.seckill.common.dynamicquery.DynamicQuery; +import com.itstyle.seckill.common.entity.RedPacketRecord; +import com.itstyle.seckill.common.entity.Result; +import com.itstyle.seckill.common.redis.RedisUtil; +import com.itstyle.seckill.distributedlock.redis.RedissLockUtil; +import com.itstyle.seckill.service.IRedPacketService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Timestamp; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@Service("redPacketService") +public class RedPacketService implements IRedPacketService { + + @Autowired + private RedisUtil redisUtil; + @Autowired + private DynamicQuery dynamicQuery; + + @Override + @Transactional + public Result startSeckil(long redPacketId,int userId) { + Integer money = 0; + boolean res=false; + try { + /** + * 获取锁 + */ + res = RedissLockUtil.tryLock(redPacketId+"", TimeUnit.SECONDS, 3, 10); + if(res){ + long restPeople = redisUtil.decr(redPacketId+"-restPeople",1); + /** + * 如果是最后一人 + */ + if(restPeople==1){ + money = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString()); + }else{ + Integer restMoney = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString()); + Random random = new Random(); + //随机范围:[1,剩余人均金额的两倍] + money = random.nextInt((int) (restMoney / (restPeople+1) * 2 - 1)) + 1; + } + redisUtil.decr(redPacketId+"-money",money); + /** + * 异步入库 + */ + RedPacketRecord record = new RedPacketRecord(); + record.setMoney(money); + record.setRedPacketId(redPacketId); + record.setUid(userId); + record.setCreateTime(new Timestamp(System.currentTimeMillis())); + saveRecord(record); + /** + * 异步入账 + */ + }else{ + /** + * 获取锁失败相当于抢红包失败,红包个数加一 + */ + redisUtil.incr(redPacketId+"-num",1); + } + } catch (Exception e) { + e.printStackTrace(); + }finally { + if(res){//释放锁 + RedissLockUtil.unlock(redPacketId+""); + } + } + return Result.ok(money); + } + + @Override + @Transactional + public Result startTwoSeckil(long redPacketId, int userId) { + Integer money = 0; + boolean res=false; + try { + /** + * 获取锁 保证红包数量和计算红白金额的原子性操作 + */ + res = RedissLockUtil.tryLock(redPacketId+"", TimeUnit.SECONDS, 3, 10); + if(res){ + long restPeople = redisUtil.decr(redPacketId+"-num",1); + if(restPeople>0){ + /** + * 如果是最后一人 + */ + if(restPeople==1){ + money = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString()); + }else{ + Integer restMoney = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString()); + Random random = new Random(); + //随机范围:[1,剩余人均金额的两倍] + money = random.nextInt((int) (restMoney / (restPeople+1) * 2 - 1)) + 1; + } + redisUtil.decr(redPacketId+"-money",money); + /** + * 异步入库 + */ + RedPacketRecord record = new RedPacketRecord(); + record.setMoney(money); + record.setRedPacketId(redPacketId); + record.setUid(userId); + record.setCreateTime(new Timestamp(System.currentTimeMillis())); + saveRecord(record); + /** + * 异步入账 + */ + }else{ + return Result.error("手慢了,红包派完了"); + } + }else{ + /** + * 获取锁失败相当于抢红包失败,红包个数加一 + */ + redisUtil.incr(redPacketId+"-num",1); + } + } catch (Exception e) { + e.printStackTrace(); + }finally { + if(res){//释放锁 + RedissLockUtil.unlock(redPacketId+""); + } + } + return Result.ok(money); + } + + @Async + void saveRecord(RedPacketRecord record){ + dynamicQuery.save(record); + } +} diff --git a/src/main/java/com/itstyle/seckill/web/RedPacketController.java b/src/main/java/com/itstyle/seckill/web/RedPacketController.java index d9a596c..8127b7e 100644 --- a/src/main/java/com/itstyle/seckill/web/RedPacketController.java +++ b/src/main/java/com/itstyle/seckill/web/RedPacketController.java @@ -41,6 +41,11 @@ public class RedPacketController { @Autowired private IRedPacketService redPacketService; + /** + * 抢红包 拆红包 抢到基本能拆到 + * @param redPacketId + * @return + */ @ApiOperation(value="抢红包一",nickname="爪哇笔记") @PostMapping("/start") public Result start(long redPacketId){ @@ -70,7 +75,7 @@ public class RedPacketController { long count = redisUtil.decr(redPacketId+"-num",1); if(count>0){ Result result = redPacketService.startSeckil(redPacketId,userId); - Double amount = DoubleUtil.divide(Double.parseDouble(result.get("msg").toString()), (double) 100); + Double amount = DoubleUtil.divide(Double.parseDouble(result.get("msg").toString()), (double) 100); LOGGER.info("用户{}抢红包成功,金额:{}", userId,amount); }else{ LOGGER.info("用户{}抢红包失败",userId); @@ -88,4 +93,64 @@ public class RedPacketController { } return Result.ok(); } + + /** + * 抢红包 拆红包 抢到不一定能拆到 + * @param redPacketId + * @return + */ + @ApiOperation(value="抢红包二",nickname="爪哇笔记") + @PostMapping("/startTwo") + public Result startTwo(long redPacketId){ + int skillNum = 100; + final CountDownLatch latch = new CountDownLatch(skillNum);//N个抢红包 + /** + * 初始化红包数据,抢红包拦截 + */ + redisUtil.cacheValue(redPacketId+"-num",10); + /** + * 初始化红包金额,单位为分 + */ + redisUtil.cacheValue(redPacketId+"-money",20000); + /** + * 模拟100个用户抢10个红包 + */ + for(int i=1;i<=skillNum;i++){ + int userId = i; + Runnable task = () -> { + /** + * 抢红包 判断剩余金额 + */ + Integer money = (Integer) redisUtil.getValue(redPacketId+"-money"); + if(money>0){ + /** + * 虽然能抢到 但是不一定能拆到 + * 类似于微信的 点击红包显示抢的按钮 + */ + Result result = redPacketService.startTwoSeckil(redPacketId,userId); + if(result.get("code").toString().equals("500")){ + LOGGER.info("用户{}手慢了,红包派完了",userId); + }else{ + Double amount = DoubleUtil.divide(Double.parseDouble(result.get("msg").toString()), (double) 100); + LOGGER.info("用户{}抢红包成功,金额:{}", userId,amount); + } + }else{ + /** + * 直接显示手慢了,红包派完了 + */ + //LOGGER.info("用户{}手慢了,红包派完了",userId); + } + latch.countDown(); + }; + executor.execute(task); + } + try { + latch.await(); + Integer restMoney = Integer.parseInt(redisUtil.getValue(redPacketId+"-money").toString()); + LOGGER.info("剩余金额:{}",restMoney); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return Result.ok(); + } } \ No newline at end of file