栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

微信抢红包过期失效实战案例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

微信抢红包过期失效实战案例

前言

微信红包业务,发红包之后如果24小时之内没有被领取完就自动过期失效。

架构设计

业务流程
  • 老板发红包,此时缓存初始化红包个数,红包金额(单位分),并异步入库。

  • 红包数据入延迟队列,唯一标识+失效时间

  • 红包数据出延迟队列,根据唯一标识清空红包缓存数据、异步更新数据库、异步退回红包金额

代码案例

这里我们使用Java内置的DelayQueue来实现,DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。

老板发了10个红包一共200人民币,假装只有9个人抢红包。

发红包,缓存数据进入延迟队列:

   
    @ApiOperation(value="抢红包三",nickname="爪哇笔记")
    @PostMapping("/startThree")
    public Result startThree(long redPacketId){
 int skillNum = 9;
 final CountDownLatch latch = new CountDownLatch(skillNum);//N个抢红包
 
 redisUtil.cachevalue(redPacketId+"-num",10);
 
 redisUtil.cachevalue(redPacketId+"-money",20000);
 
 RedPacketMessage message = new RedPacketMessage(redPacketId,24);
 RedPacketQueue.getQueue().produce(message);
 
 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);
      }
  }
  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();
    }

红包队列消息:

public class RedPacketMessage implements Delayed {

    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    
    private static final long DELAY_MS = 1000L * 3;

    
    private final long redPacketId;

    
    private final long timestamp;

    
    private final long expire;

    
    private final String description;

    public RedPacketMessage(long redPacketId, long expireSeconds) {
 this.redPacketId = redPacketId;
 this.timestamp = System.currentTimeMillis();
 this.expire = this.timestamp + expireSeconds * 1000L;
 this.description = String.format("红包[%s]-创建时间为:%s,超时时间为:%s", redPacketId,
  LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).format(F),
  LocalDateTime.ofInstant(Instant.ofEpochMilli(expire), ZoneId.systemDefault()).format(F));
    }

    public RedPacketMessage(long redPacketId) {
 this.redPacketId = redPacketId;
 this.timestamp = System.currentTimeMillis();
 this.expire = this.timestamp + DELAY_MS;
 this.description = String.format("红包[%s]-创建时间为:%s,超时时间为:%s", redPacketId,
  LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).format(F),
  LocalDateTime.ofInstant(Instant.ofEpochMilli(expire), ZoneId.systemDefault()).format(F));
    }

    public long getRedPacketId() {
 return redPacketId;
    }

    public long getTimestamp() {
 return timestamp;
    }

    public long getExpire() {
 return expire;
    }

    public String getDescription() {
 return description;
    }

    @Override
    public long getDelay(TimeUnit unit) {
 return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
 return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }
}

红包延迟队列:

public class RedPacketQueue {

    
    private static DelayQueue queue = new DelayQueue<>();

    
    private RedPacketQueue(){}
    
    private static class SingletonHolder{
 
 private  static RedPacketQueue queue = new RedPacketQueue();
    }
    //单例队列
    public static RedPacketQueue getQueue(){
 return SingletonHolder.queue;
    }
    
    public  Boolean  produce(RedPacketMessage message){
 return queue.add(message);
    }
    
    public  RedPacketMessage consume() throws InterruptedException {
 return queue.take();
    }
}

红包延迟队列过期消费,监听任务:

@Component("redPacket")
public class TaskRunner implements ApplicationRunner {

    private final static Logger LOGGER = LoggerFactory.getLogger(TaskRunner.class);

    @Autowired
    private RedisUtil redisUtil;

    ExecutorService executorService = Executors.newSingleThreadExecutor(r -> {
 Thread thread = new Thread(r);
 thread.setName("RedPacketDelayWorker");
 thread.setDaemon(true);
 return thread;
    });

    @Override
    public void run(ApplicationArguments var){
 executorService.execute(() -> {
     while (true) {
  try {
      RedPacketMessage message = RedPacketQueue.getQueue().consume();
      if(message!=null){
   long redPacketId = message.getRedPacketId();
   LOGGER.info("红包{}过期了",redPacketId);
   
   int num = (int) redisUtil.getValue(redPacketId+"-num");
   int restMoney = (int) redisUtil.getValue(redPacketId+"-money");
   LOGGER.info("剩余红包个数{},剩余红包金额{}",num,restMoney);
   
   redisUtil.removevalue(redPacketId+"-num");
   redisUtil.removevalue(redPacketId+"-money");
   
      }
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
     }
 });
    }
}
适用场景

淘宝订单到期,下单成功后60s之后给用户发送短信通知,限时支付、缓存系统等等。

演示

在Application中有接口演示说明,你可以在抢红包 Red Packet Controller接口中输入任何参数进行测试,也可以配合数据库稍加修改即可作为生产环境的抢红包功能模块。

小结

以上方案并没有实现持久化和分布式,生产环境可根据实际业务需求选择使用。

源码

https://gitee.com/52itstyle/spring-boot-seckill

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/238178.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号