微信高并发抢红包秒杀实战案例

前言

群里有小伙伴咨询微信红包的架构,对于我来说,显然是不知道的,但是写一个相对高并发的抢红包案例还是完全可以的。

架构设计

业务流程

  • 老板发红包,此时缓存初始化红包个数,红包金额(单位分),并异步入库。
  • 抢红包,判断缓存剩余红包金额,剩余金额大于零则抢到红包,否则手慢了,红包派完了
  • 拆红包,根据 redPacketId 获取分布式锁,如果获取到锁,红包个数减一,如果剩余红包个数大于零抢红包成功、否则失败。成功则计算红包金额,缓存总红包金额减去抢到的红包金额,异步入库、异步到账。

数据库设计

  • 红包信息表
CREATE TABLE `red_racket` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
   `red_packet_id` bigint(20) NOT NULL COMMENT '红包唯一ID',
   `total_amount` int(11) NOT NULL COMMENT '红包金额单位分',
   `total_packet` int(11) NOT NULL COMMENT '红包个数',
   `type` int(11) NOT NULL COMMENT '红包类型',
   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
   `version` int(11) NOT NULL COMMENT '版本号',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='红包信息表'
  • 抢红包记录表
CREATE TABLE `red_packet_record` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
   `amount` int(11) NOT NULL COMMENT '抢到红包的金额',
   `red_packet_id` bigint(20) NOT NULL COMMENT '红包ID',
   `uid` int(11) NOT NULL COMMENT '抢到红包用户的用户标识',
   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='抢红包记录表'

代码案例

老板发了10个红包一共200人民币,100个人同时抢红包,伪代码分别为拆红包和抢红包相关业务逻辑。

模拟抢红包伪代码:

   /**
     * 抢红包 拆红包 抢到不一定能拆到
     * @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();
    }

业务层拆红包:

    @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{
                /**
                 * 获取锁失败相当于抢红包失败
                 */
                 return Result.error("手慢了,红包派完了");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(res){//释放锁
                RedissLockUtil.unlock(redPacketId+"");
            }
        }
        return Result.ok(money);
    }

演示

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

源码

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

原文地址:https://www.cnblogs.com/smallSevens/p/12269699.html

时间: 2024-08-11 05:29:25

微信高并发抢红包秒杀实战案例的相关文章

如何解决高并发,秒杀问题

相信不少人会被这个问题困扰,分享大家一篇这样的文章,希望能够帮到你! 一.秒杀业务为什么难做?1)im系统,例如qq或者微博,每个人都读自己的数据(好友列表.群列表.个人信息):2)微博系统,每个人读你关注的人的数据,一个人读多个人的数据:3)秒杀系统,库存只有一份,所有人会在集中的时间读和写这些数据,多个人读一个数据. 例如:小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万. 又例如:12306抢票,票是有限的,库存一份,瞬时流量非常多,都读相同的库存.读写冲突,锁非

大数据高并发系统架构实战方案

大数据高并发系统架构实战方案(LVS负载均衡.Nginx.共享存储.海量数据.队列缓存 ) 随着互联网的发展,高并发.大数据量的网站要求越来越高.而这些高要求都是基础的技术和细节组合而成的.本课程就从实际案例出发给大家原景重现高并发架构常用技术点及详细演练.通过该课程的学习,普通的技术人员就可以快速搭建起千万级的高并发大数据网站平台,课程涉及内容包括:LVS实现负载均衡.Nginx高级配置实战.共享存储实现动态内容静态化加速实战.缓存平台安装配置使用.mysql主从复制安装配置实战等.课程二十.

关于高并发和秒杀系统,你知道的和不知道的一些事

这篇文章也算是对于课程 <PHP秒杀系统 高并发高性能的极致挑战> 的一个整理,视频之外的另外一种形式吧. 大家也许开发过高并发的系统或者秒杀程序,但肯定都有接触过,像电商平台的秒杀.抢购等活动,还有12306春运抢票. 互联网公司,做一些有奖活动,而且数量有限,奖品给力,如果是先到先得的策略,那就类似秒杀系统了. 这类系统最大的问题就是活动周期短,瞬间流量大(高并发),很少人可以成功下单,绝大多数人都是很遗憾.所以从运营体验上,没有成功下单的人,心里肯定是不好受的,如果这时候,因为技术.网络

(转)电商网站50W-100W高并发,秒杀功能是怎么实现的?

电商网站50W-100W高并发,秒杀功能是怎么实现的? 在淘宝.天猫.京东等国内大型电商平台“造节”的带领下,国内各电商平台纷纷跟进,双十一.双十二.618等电商专属节日也吸引了大量的用户参与.节前生意惨淡.访客寥寥,节日当天流量增长却异常迅猛,这对于广大程序猿同学和运维人员来说,无疑是巨大的考验. 秒杀系统的流量虽然很高,但是实际有效流量比较小:利用系统的层次结构,在每个阶段提前校验,拦截无效流量,可以减少大量无效流量涌入数据库,从而保障业务系统的正常运行: 第一步:利用浏览器缓存和CDN加速

微信高并发资金交易系统设计方案——百亿红包背后的技术支撑

背景介绍 2017年1月28日,正月初一,微信公布了用户在除夕当天收发微信红包的数量--142亿个,而其收发峰值也已达到76万每秒.百亿级别的红包,如何保障并发性能与资金安全?这给微信带来了超级挑战.面对挑战,微信红包在分析了业界"秒杀"系统解决方案的基础上,采用了SET化.请求排队串行化.双维度分库表等设计,形成了独特的高并发.资金安全系统解决方案.实践证明,该方案表现稳定,且实现了除夕夜系统零故障运行. 本文将为读者介绍百亿级别红包背后的系统高并发设计方案,包括微信红包的两大业务特

大数据高并发系统架构实战方案视频教程

课程下载地址:http://pan.baidu.com/s/1dEyJiWL 密码:8bzy 随着互联网的发展,高并发.大数据量的网站要求越来越高.而这些高要求都是基础的技术和细节组合而成的.本课程就从实际案例出发给大家原景重现高并发架构常用技术点及详细演练.通过该课程的学习,普通的技术人员就可以快速搭建起千万级的高并发大数据网站平台. 01.大数据高并发架构实战案例分享-概述 02.Piranha安装快速搭建LVS负载均衡集群 03.LVS负载均衡DR模式安装调试介绍 04.LVS负载均衡深入

大数据高并发系统架构实战方案(LVS负载均衡、Nginx、共享存储、海量数据、队列缓存)

课程简介: 随着互联网的发展,高并发.大数据量的网站要求越来越高.而这些高要求都是基础的技术和细节组合而成的.本课程就从实际案例出发给大家原景重现高并发架构常用技术点及详细演练. 通过该课程的学习,普通的技术人员就可以快速搭建起千万级的高并发大数据网站平台. 亮点一:真实环境还原,课程采用了VM环境重现大网站集群服务器环境,真实环境还原再现. 亮点二:基础实用,细节决定成败,课程内容在演练过程中重点介绍各种细节,保证初级人员快速入门及高级进阶. 亮点三:讲师丰富的海量平台运作经验 讲师tom5多

【总结】瞬时高并发(秒杀/活动)Redis方案(转)

转载地址:http://bradyzhu.iteye.com/blog/2270698 1,Redis 丰富的数据结构(Data Structures) 字符串(String) Redis字符串能包含任意类型的数据 一个字符串类型的值最多能存储512M字节的内容 利用INCR命令簇(INCR, DECR, INCRBY)来把字符串当作原子计数器使用 使用APPEND命令在字符串后添加内容 列表(List) Redis列表是简单的字符串列表,按照插入顺序排序 你可以添加一个元素到列表的头部(左边:

PHP高并发商城秒杀

1.什么是秒杀 秒杀活动是一些购物平台推出的集中人气的活动,一般商品数量很少,价格很便宜,限定开始购买的时间,会在以秒为单位的时间内被购买一空.比如原价千元甚至万元的商品以一元的价格出售,但数量只有一件,在某天的某个时间开始出售,这就造成很多人去抢这一件商品.当然想抢到是需要很多因素的,比如你的电脑配置.网速,还有你的运气. 2.秒杀会带来的问题 (1).高并发 比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验. (2).超卖 任何商品都会有数量上限,如何