spring redisTemplate+redis实现简单的抢购逻辑

有关电商抢购的具体实现方案实现,首先需要明确我们抢购最关键的因素无非就是商品的库存,具体抢购的那些商品,用户抢购成功后如何和抢购商品形成关联关系。

这个给出大概的思路:

1.进行商品的备货

2.抢购的时候,每抢够成功一次,商品的库存-1,同时记录抢购到该商品的用户。

3.抢购结束后,同步数据,生成相应的订单。

抢购的话,建议采用redis数据库,响应速度快,性能也稳定,也可以承受高并发的访问量,抢购结束后,生成的订单再存放如数据库

 

在决定解决方案之前,我也曾想过事务这一块的问题,那么如果高并发的情况下,redis怎么处理抢购这一块的事务呢,后面去网上找了一下资料http://www.runoob.com/redis/redis-transactions.html

spring redisTemplate+redis实现简单的抢购逻辑_第1张图片

我的代码操作也是单条语句的,所以具有原子性,因此不用担心事务这一块的问题。

具体的代码实现如下:

第一步,先初始化库存数据,准备开始抢购:

    /**
     * 初始化产品库存
     */
    public void synchronizedProduct() {
        redisTemplate.opsForZSet().add("rush", "小米6", 5);
        redisTemplate.opsForZSet().add("rush", "小米8", 5);
        redisTemplate.opsForZSet().add("rush", "小米9", 5);
        redisTemplate.opsForZSet().add("rush", "小米mix3", 5);
    }

第二步,开始抢购,获取产品id和关联用户id,如果redis里面的库存已经用完,就显示已售窑,如果抢购成功,根据产品id:sale作为键值key,然后放入一个Map中,其中值是一个List,可以将抢购者的用户id放置List中

 控制层代码:

    @PostMapping("/shop")
    public RestResult rushShop(String productName,Integer userId){
        RushResult rushResult = rushService.reduceProduct(productName,userId);
        if(rushResult.isSaleOut()){
            return new RestResult(200,"抢购成功",rushResult);
        }else{
            return new  RestResult(200,"已售窑",rushResult);
        }
    }

业务层代码:

    /**
     * 抢购逻辑
     */
    public RushResult reduceProduct(String productName, Integer userId) {
        //先获取库存
        Double storeValue = redisTemplate.opsForZSet().score("rush", productName);
        if (storeValue == null || storeValue == 0) {
            //标记字段设置为false
            boolean isSaleOut = false;
            return new RushResult(storeValue, isSaleOut);
        } else {
            //库存减一
            Double levelStoreValue = redisTemplate.opsForZSet().incrementScore("rush", productName, -1);
            //存放进一个List中
            redisTemplate.opsForList().rightPush(productName + ":sale", userId);
            //抢购成功标记
            boolean isSaleOut = true;
            return new RushResult(levelStoreValue, isSaleOut);
        }
    }

  第三步,前端刷新页面时,显示最新的数据,将产品库存做一个排序,将最火的排在前面,但其实显示销量的需求不常见,因为很多商家并不愿意透露具体的备货量给客户

    public Map rankProduct() {
        //获取所有的产品,做一个排序
        Set productSet = redisTemplate.opsForZSet().rangeByScore("rush", 0, 1000);
        Map rushMap = new LinkedHashMap<>();
        for (String productName :
                productSet) {
            //取出相关的库存
            Double storeValue = redisTemplate.opsForZSet().score("rush", productName);
            rushMap.put(productName, storeValue);
        }
        return rushMap;
    }

  第四步,抢购结束后,将所有的抢购名单内的数据转移到数据库,生成相关订单

    public List> getAllRushUser() {
        //这里可以操作数据库,获取所有的抢购商品id,这里为了方便演示,就直接写死了
        String[] productIdArray = {"小米6", "小米8", "小米9", "小米mix3"};
        List> productMapList = new ArrayList<>();
        for (String productId :
                productIdArray) {
            //从队列中获取所有的用户id
            List rushUserList = redisTemplate.opsForList().range(productId + ":sale", 0, -1);
            Map productRushMap = new HashMap<>();
            productRushMap.put(productId, rushUserList);
            productMapList.add(productRushMap);
        }
        //这里进行数据库操作,将List里面的所有数据信息放置入数据库中,但是insert严禁使用for循环,建议用mybatis的foreach语句
        //todo
        return productMapList;
    }

   redis增加一个配置,确保数据不会被序列化:

    /**
     *  存储二进制字节码, 所以自定义序列化类
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

   基本代码就如上面展示了

   如果关于抢购这一块有更好的实现方式,或者我的文章有不正确的内容,可以在评论区中讨论一下  @_@

   

你可能感兴趣的:(spring,redis)