spring boot + Mybatis + redis 秒杀系统

来源:互联网 发布:python列表和集合转化 编辑:程序博客网 时间:2024/06/05 09:27

最近开了一些高并发的东西,以及一些秒杀系统,但感觉都没有完整的描述。于是自己就动手实现了一个简单版本的抢购系统。

本系统采用spring boot + mybatis + redis实现。

项目结构图如下:



项目工程已放到GitHub上了,https://github.com/feibabm/seckill,需要的请自行下载。

本文主要借鉴网上一些通用的做法,做出一个例子,主要实现了一个抢购接口:
http://localhost:8080/seckill/product/1?userId=1


seckill.sql文件为建表sql
pro_insert.sql文件为success_killed表中数据添加10000条用户预约记录
seckill_insert.sql文件为seckill生成一条产品信息


具体验证逻辑是执行test文件夹下的两个test类:
RemoteInvote.java
RemoteInvote2.java
这两个test类没有什么差别,主要是为了增加并发量

主要的抢票逻辑如下:


public SecKillResult secKillProduct(String userPhone, long productId) {        String state = (String)redisTemplate.opsForValue().get(userPhone + "_"+ productId);        //用户信息加载        if(null == state){            SuccessKilled successKilled = new SuccessKilled();            successKilled.setSeckillId(productId);            successKilled.setUserPhone(Long.valueOf(userPhone));            successKilled = successKilledMapper.selectOne(successKilled);            if(null == successKilled){                return new SecKillResult(false, "该用户没有预约");            }else{                synchronized (this){                    state = (String)redisTemplate.opsForValue().get(userPhone + "_"+ productId);                    if(null == state){                        redisTemplate.opsForValue().set(userPhone + "_" + productId, successKilled.getState().toString(), 300, TimeUnit.SECONDS);                        state = String.valueOf(successKilled.getState());                    }                }            }        }        if(state.equals("-1")){            //查询产品信息//            ProductInfo productInfo = (ProductInfo)redisTemplate.opsForValue().get(productId + "");            List values = redisTemplate.opsForHash().values(productId + "");            if(values.size() == 0){                Seckill seckill = seckillMapper.selectByPrimaryKey(productId);                if(null == seckill){                    return new SecKillResult(false, "没有该秒杀商品信息");                }                synchronized (this){                    if(!redisTemplate.opsForHash().hasKey(productId + "", "number")){//                        productInfo = new ProductInfo(seckill.getSeckillId(), seckill.getNumber(), seckill.getStartTime(), seckill.getEndTime());                        HashMap<String, String> productHash = new HashMap<>();                        productHash.put("number", seckill.getNumber() + "");                        productHash.put("startTime", seckill.getStartTime().getTime() + "");                        productHash.put("endTime", seckill.getEndTime().getTime() + "");                        redisTemplate.opsForHash().putAll(productId +"", productHash);                        redisTemplate.expire(productId + "", 300, TimeUnit.SECONDS);                        values = redisTemplate.opsForHash().values(productId + "");                    }                }            }            if( new Date(Long.valueOf((String)values.get(1))).after(new Date(System.currentTimeMillis()))){                return new SecKillResult(false, "抢购还没有开始");            } else if(new Date(Long.valueOf((String)values.get(2))).before(new Date(System.currentTimeMillis()))){                return new SecKillResult(false, "抢购已经结束");            } else {                Long userState = redisTemplate.opsForValue().increment(userPhone + "_" + productId, 1);                if(userState == 0){//                    Long increment = redisTemplate.opsForValue().increment(productId, -1);                    Long number = redisTemplate.opsForHash().increment(productId + "", "number", -1);                    if(number >= 0){                        //消息队列异步更新库存,以及用户的预约信息                        QueueEntity queueEntity = new QueueEntity(userPhone, productId);                        ExecutorPool.queue.offer(queueEntity);                    }else {                        return new SecKillResult(false, "商品已经抢购完成");                    }                }else {                    redisTemplate.opsForValue().increment(userPhone + "_" + productId, -1);                    return new SecKillResult(false, "您已抢购过该产品");                }            }        } else {            return new SecKillResult(false, "您已抢购过该产品");        }        return null;    }


自己用两进程线程,大概两分钟掉了20000次,暂时没有出现啥问题。如果有什么问题,希望大家指出,谢谢。


原创粉丝点击