Redis+Lua实现高并发下抢红包

Redis+Lua实现高并发下抢红包

    • 一. 实现业务功能点
    • 二. 抢红包流程示意
    • 三. 代码部分
    • 四.致谢

一. 实现业务功能点

  1. 将所有红包全部存储到Redis ( 红包池 )
  2. 用户抢了多少红包, 记录红包被抢的详情信息;
  3. 用户只能抢一次红包, 不能重复抢红包

二. 抢红包流程示意

Redis+Lua实现高并发下抢红包_第1张图片

三. 代码部分

  • 完整代码已上传至github 点击获取

  • 代码运行环境
    redis + lua + jdk1.8 + maven
    ps: 从Redis 2.6 版本开始,内嵌支持 Lua 环境。通过使用EVAL或EVALSHA命令可以使用 Lua 解释器来执行脚本

  • 实现代码

    红包池初始化

public class GenRedPack {

    /**
     * 多线程模拟红包池初始化
     */
    public static void genRedPack(){
        final  JedisUtils jedis = new JedisUtils(Basic.ip, Basic.port, Basic.auth);
        // 发枪器
        final  CountDownLatch latch = new CountDownLatch(Basic.threadCount);
        // 清空所有redis缓存
        jedis.flushall();
        // 每个线程需要初始化多少红包
        int per = Basic.redPackCount/Basic.threadCount;
        for (int i = 0; i < Basic.threadCount; i++) {
            final int page = i;
            new Thread( () -> {
                latch.countDown();
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.getStackTrace();
                }
                JSONObject object = new JSONObject();

                for (int j = page * per; j < (page+1) * per; j++){
                    object.put("id", "rid_" + j);// 红包id
                    object.put("money", j);
                    jedis.lpush(Basic.redPackPoolKey, object.toJSONString());
                }
            }).start();
        }
    }
}

模拟并发抢红包

public class GetRedPack {
    /**
     * 多线程模拟用户抢红包
     */
    public static void getRedPack(){
        // 发枪器
        final CountDownLatch latch = new CountDownLatch(Basic.threadCount);
        for (int i = 0; i < Basic.threadCount; i++) {
            new Thread(()->{
                latch.countDown();
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                final  JedisUtils jedis = new JedisUtils(Basic.ip, Basic.port, Basic.auth);
                while (true){
                    String userId = UUID.randomUUID().toString();

                    Object eval = jedis.eval(Basic.getRedPackScript, 4, Basic.redPackPoolKey, Basic.redPackDetailListKey, Basic.userIdRecordKey, userId);
                    if (eval!=null){
                        System.out.println("用户id:" + userId + " 抢红包详情为" + eval);
                    }else{
                        if (jedis.llen(Basic.redPackPoolKey)==0){
                            break;
                        }
                    }
                }
            }).start();
        }
    }
}

常量定义

public interface Basic {
    String ip = "192.168.168.14";
    int port = 6379;
    String auth = "123456";
    int redPackCount = 1000;

    int threadCount = 20;
    String redPackPoolKey = "redPackPoolKey"; //LIST类型来模拟红包池子
    String redPackDetailListKey = "redPackDetailListKey";//LIST类型,记录所有用户抢红包的详情
    String userIdRecordKey = "userIdRecordKey";//记录已经抢过红包的用户ID,防止重复抢

    /*
     * KEYS[1]:redPackPool //模拟红包池,用来从红包池抢红包
     * KEYS[2]:redPackDetailList://记录所有用户抢红包的详情
     * KEYS[3]:userIdRecord//记录所有已经抢过红包的用户ID
     * KEYS[4]:userId  //模拟抢红包的用户ID
     */
    String getRedPackScript =
            // 判断是否用户是否已抢过红包 如果已抢过直接返回nil
            "if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then \n" +
                    "\t\treturn nil\n" +
                    "else\n" +
                    //从红包池取出一个红包
                    "\tlocal redPack = redis.call('rpop', KEYS[1]);\n" +
                    //判断红包是否为空
                    "\tif redPack then \n" +
                    "\t\tlocal x = cjson.decode(redPack);\n" +
                    "\t\tx['userId'] = KEYS[4];\n" +
                    "\t\tlocal re = cjson.encode(x);\n" +
                    // 记录用户已抢过userIdRecordKey
                    "\t\tredis.call('hset', KEYS[3], KEYS[4], '1');\n" +
                    // 保存用户抢红包详情
                    "\t\tredis.call('lpush', KEYS[2],re);\n" +
                    "\t\treturn re;\n" +
                    "\tend\n" +
                    "end\t\n" +
                    "return nil";
}

测试类

public class RedPackTest {
    public static void main(String[] args) {
        GenRedPack.genRedPack();//初始化红包

        GetRedPack.getRedPack();//从红包池抢红包
    }
}

pom.xml



    4.0.0
    com.lilei.project
    redis-redpack
    war
    0.0.1-SNAPSHOT
    redis Maven Webapp
    
        
            redis.clients
            jedis
            2.7.2
        
        
            com.alibaba
            fastJson
            1.2.31
        
    
    
        redis-redpack
    



四.致谢

感谢大家阅读这篇文章 如有任何问题 可在评论下留言 笔者会在第一时间回复!!

你可能感兴趣的:(学习之路,redis,java,lua,github)