大厂五剑客之redis实战分布式缓存彻底解决方案---12--微信红包---二周目

15 篇文章 0 订阅

主要的并发是在抢红包和拆红包的。

-------------------------------------------------------------------------------9-1--------------------------------------------------------------------------------------

数据库建表。

如何使用generater生成代码?

第一步:配置和引入

第二步:

第三步:

数据库表的设计:

CREATE TABLE `red_packet_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT, 
  `red_packet_id` bigint(11) NOT NULL DEFAULT  0 COMMENT '红包id,采用timestamp+5位随机数', 
  `total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '红包总金额,单位分',
  `total_packet` int(11) NOT NULL DEFAULT 0 COMMENT '红包总个数',
  `remaining_amount` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包金额,单位分',
  `remaining_packet` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包个数',
  `uid` int(20) NOT NULL DEFAULT 0 COMMENT '新建红包用户的用户标识',
  `create_time` timestamp  COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='红包信息表,新建一个红包插入一条记录';
CREATE TABLE `red_packet_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT, 
  `amount` int(11) NOT NULL DEFAULT '0' COMMENT '抢到红包的金额',
  `nick_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '抢到红包的用户的用户名',
  `img_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '抢到红包的用户的头像',
  `uid` int(20) NOT NULL DEFAULT '0' COMMENT '抢到红包用户的用户标识',
  `red_packet_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '红包id,采用timestamp+5位随机数', 
  `create_time` timestamp  COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='抢红包记录表,抢一个红包插入一条记录';

--------------------------------------------------------------------------9-2--------------------------------------------------------------------------------

发红包接口的实现。

实现的思路:

抢到红包之后不一定能拆开红包。

代码:新增红包。

    /***
     *
     * @param uid
     * @param totalNum
     * @return
     */
    @ResponseBody
    @RequestMapping("/addPacket")
    public String saveRedPacket(Integer uid, Integer totalNum, Integer totalAmount) {
        RedPacketInfo record = new RedPacketInfo();
        record.setUid(uid);
        record.setTotalAmount(totalAmount);
        record.setTotalPacket(totalNum);
        record.setCreateTime(new Date());
        record.setRemainingAmount(totalAmount);
        record.setRemainingPacket(totalNum);
        record.setUpdateTime(new Date());
        long redPacketId = System.currentTimeMillis();   //此时无法保证红包id唯一,最好是用雪花算法进行生成分布式系统唯一键
        record.setRedPacketId(redPacketId);
        redPacketInfoMapper.insert(record);
        // 在redis存入红包的总个数  和红包的总金额
        redisService.set(redPacketId + "_totalNum", totalNum + "");
        redisService.set(redPacketId + "_totalAmount", totalAmount + "");
        return "success";
    }

新增红包:数据库加一条红包记录,把红包的数量红包的金额和剩余红包的数量剩余红包的金额存入数据库。

redis的储存:剩余红包的数量,剩余红包的金额。

controller里面的成员变量:https://www.zhihu.com/question/271297000

代码:抢红包

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean decr(final String key, int value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.increment(key,-value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
   /**
     * 抢红包
     * @return
     */
    @ResponseBody
    @RequestMapping("/addScores")
    public String getRedPacket(Integer uid, long redPackId) {
        RedPacketRecord redPacketRecord = new RedPacketRecord();
        redPacketRecord.setUid(uid);
        redPacketRecord.setRedPacketId(redPackId);
        redPacketRecord.setAmount(11111);
        redPacketRecord.setCreateTime(new Date());
        redPacketRecordMapper.insertSelective(redPacketRecord);
        redisService.decr(redPackId+"_totalAmount",1);
        return "success";
    }

补充:实际上我们还要判断这个人是不是重复抢了红包的。

测试:http://localhost:8080/addPacket?uid=123&totalNum=10&totalAmount=100

--------------------------------------------------------------9-3--------------------------------------------------------------------------------

抢红包:

判断的逻辑:

抢红包:

   /**
     * 抢红包
     *实际开发要校验用户是不是存在的
     * @param redPacketId
     * @return
     */
    @ResponseBody
    @RequestMapping("/getPacket")
    public Integer getRedPacket(String redPacketId) {
        String redPacketName = redPacketId + TOTAL_NUM;
        String num = (String) redisService.get(redPacketName);
        if (StringUtils.isNotBlank(num)) {
            return Integer.parseInt(num);
        }
        return 0;
    }

测试:http://localhost:8080/getPacket?redPacketId=1581997706758

抢红包:不对redis和db做任何操作,只是返回有没有红包,有了就抢到,但是不一定可以领到钱。

--------------------------------------------------------------9-4--------------------------------------------------------------------------------

抢到红包不一定能领到。

 /**
     * 拆红包
     *
     * @param redPacketId
     * @return
     */
    @ResponseBody
    @RequestMapping("/getRedPacketMoney")
    public String getRedPacketMoney(int uid, long redPacketId) {
        Integer randomAmount = 0;
        // 存的是剩余的红包的数量
        String redPacketName = redPacketId + TOTAL_NUM;
        //存的是剩余红包的金额
        String totalAmountName = redPacketId + TOTAL_AMOUNT;
        // 红包的个数
        String num = (String) redisService.get(redPacketName);
        if (StringUtils.isBlank(num) || Integer.parseInt(num) == 0) {
            return "抱歉!红包已经抢完了";
        }
        String totalAmount = (String) redisService.get(totalAmountName);
        // 抢到的金额
        if (StringUtils.isNotBlank(totalAmount)) {
            Integer totalAmountInt = Integer.parseInt(totalAmount);
            Integer totalNumInt = Integer.parseInt(num);
            Integer maxMoney = totalAmountInt / totalNumInt * 2;
            Random random = new Random();
            randomAmount = random.nextInt(maxMoney);
        }
        //课堂作业:lua脚本将这两个命令一起请求
        redisService.decr(redPacketName, 1);
        redisService.decr(totalAmountName,randomAmount);  //redis decreby功能
        // 更新红包流水
        updateRacketInDB(uid, redPacketId,randomAmount);
        return randomAmount + "";
    }
 public void updateRacketInDB(int uid, long redPacketId, int amount) {
        RedPacketRecord redPacketRecord = new RedPacketRecord();
        redPacketRecord.setUid(uid);
        redPacketRecord.setRedPacketId(redPacketId);
        redPacketRecord.setAmount(1111);
        redPacketRecord.setCreateTime(new Date());
        redPacketRecordMapper.insertSelective(redPacketRecord);
        //这里应该查出RedPacketInfo的数量,将总数量和总金额减去
    }

--------------------------------------------------------------9-5--------------------------------------------------------------------------------

项目总结:

第一个问题:入账的时候,判断下数据库和redis的金额是不是一样的。

第二个问题:用户拆红包是不是限制下?可以用消息队列做一个只放进来40个用户假设有20个用户。

第三个问题:redis的红包数量和金额的操作。

这里的并发量用lua脚本。保证在同一次命令。可能进行了第一步之后第二步就有很多线程进来了。

第四个问题:

---------------------------------------------------------------------------9-6---------------------------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值