商城项目秒杀通过Redisson设置信号量和秒杀随机码的设计保证秒杀业务稳定-----商城项目

package com.alatus.mall.seckill.service.impl;

import com.alatus.common.utils.R;
import com.alatus.mall.seckill.constant.SecKillConstants;
import com.alatus.mall.seckill.feign.CouponFeignService;
import com.alatus.mall.seckill.feign.ProductFeignService;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import com.alatus.mall.seckill.vo.SeckillSessionEntityVo;
import com.alatus.mall.seckill.vo.SkuInfoVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    private CouponFeignService couponFeignService;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息

        }
    }
    private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) {
        seckillSessionVos.stream().forEach(session -> {
            //获取秒杀活动开始和结束时间
            long start = session.getStartTime().getTime();
            long end = session.getEndTime().getTime();
            String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getId()+session.getName()+start+"-"+end;
            List<String> ids = session.getRelationSkus().stream().map(item->item.getId().toString()).collect(Collectors.toList());
//            保存活动的信息
            redisTemplate.opsForList().leftPushAll(key,ids);
        });
    }
    private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){
        seckillSessionVos.forEach(session -> {
//            准备哈希操作
            BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
            session.getRelationSkus().forEach(relationSku -> {
//                缓存商品
                SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo();
//                sku的基本信息
                R info = productFeignService.getSkuInfo(relationSku.getSkuId());
                if(info.getCode() == 0){
                    SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){});
                    secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                }
//                sku的秒杀信息
                BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                设置秒杀的时间
                secKillSkuRedisTo.setStartTime(session.getStartTime().getTime());
                secKillSkuRedisTo.setEndTime(session.getEndTime().getTime());
//                设置随机码
                String token = UUID.randomUUID().toString().replace("-", "");
                secKillSkuRedisTo.setRandomCode(token);
//                设置信号量
                RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token);
                semaphore.trySetPermits(relationSku.getSeckillCount());
                String skuJson = JSON.toJSONString(secKillSkuRedisTo);
                ops.put(relationSku.getId().toString(),skuJson);
            });
        });
    }
}
package com.alatus.mall.seckill.service.impl;

import com.alatus.common.utils.R;
import com.alatus.mall.seckill.constant.SecKillConstants;
import com.alatus.mall.seckill.feign.CouponFeignService;
import com.alatus.mall.seckill.feign.ProductFeignService;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import com.alatus.mall.seckill.vo.SeckillSessionEntityVo;
import com.alatus.mall.seckill.vo.SkuInfoVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    private CouponFeignService couponFeignService;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息

        }
    }
    private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) {
        seckillSessionVos.stream().forEach(session -> {
            //获取秒杀活动开始和结束时间
            long start = session.getStartTime().getTime();
            long end = session.getEndTime().getTime();
            String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getId()+session.getName()+start+"-"+end;
            List<String> ids = session.getRelationSkus().stream().map(item->item.getId().toString()).collect(Collectors.toList());
//            保存活动的信息
            redisTemplate.opsForList().leftPushAll(key,ids);
        });
    }
    private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){
        seckillSessionVos.forEach(session -> {
//            准备哈希操作
            BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
            session.getRelationSkus().forEach(relationSku -> {
//                缓存商品
                SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo();
//                sku的基本信息
                R info = productFeignService.getSkuInfo(relationSku.getSkuId());
                if(info.getCode() == 0){
                    SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){});
                    secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                }
//                sku的秒杀信息
                BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                设置秒杀的时间
                secKillSkuRedisTo.setStartTime(session.getStartTime().getTime());
                secKillSkuRedisTo.setEndTime(session.getEndTime().getTime());
//                设置随机码
                String token = UUID.randomUUID().toString().replace("-", "");
                secKillSkuRedisTo.setRandomCode(token);
//                设置信号量
                RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token);
                semaphore.trySetPermits(relationSku.getSeckillCount());
                String skuJson = JSON.toJSONString(secKillSkuRedisTo);
                ops.put(relationSku.getId().toString(),skuJson);
            });
        });
    }
}
package com.alatus.mall.coupon.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.math.BigDecimal;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
 * 秒杀活动商品关联
 * 
 * @author alatus
 * @date 2024-03-12 13:32:54
 */
@Data
@TableName("sms_seckill_sku_relation")
public class SeckillSkuRelationEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * id
	 */
	@TableId
	private Long id;
	/**
	 * 活动id
	 */
	private Long promotionId;
	/**
	 * 活动场次id
	 */
	private Long promotionSessionId;
	/**
	 * 商品id
	 */
	private Long skuId;
	/**
	 * 秒杀价格
	 */
	private BigDecimal seckillPrice;
	/**
	 * 秒杀总量
	 */
	private Integer seckillCount;
	/**
	 * 每人限购数量
	 */
	private BigDecimal seckillLimit;
	/**
	 * 排序
	 */
	private Integer seckillSort;

}

 

package com.alatus.mall.coupon.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.math.BigDecimal;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
 * 秒杀活动商品关联
 * 
 * @author alatus
 * @date 2024-03-12 13:32:54
 */
@Data
@TableName("sms_seckill_sku_relation")
public class SeckillSkuRelationEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    @TableId
    private Long id;
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private Integer seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

}
package com.alatus.mall.seckill.vo;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class SeckillSkuRelationEntityVo {
    private Long id;
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private Integer seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;
}

 

package com.alatus.mall.seckill.vo;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class SeckillSkuRelationEntityVo {
    private Long id;
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private Integer seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;
}
package com.alatus.mall.seckill.vo;

import lombok.Data;

import java.util.Date;
import java.util.List;

@Data
public class SeckillSessionEntityVo {
    private Long id;
    /**
     * 场次名称
     */
    private String name;
    /**
     * 每日开始时间
     */
    private Date startTime;
    /**
     * 每日结束时间
     */
    private Date endTime;
    /**
     * 启用状态
     */
    private Integer status;
    /**
     * 创建时间
     */
    private Date createTime;

    private List<SeckillSkuRelationEntityVo> relationSkus;
}

 

package com.alatus.mall.seckill.vo;

import lombok.Data;

import java.util.Date;
import java.util.List;

@Data
public class SeckillSessionEntityVo {
    private Long id;
    /**
     * 场次名称
     */
    private String name;
    /**
     * 每日开始时间
     */
    private Date startTime;
    /**
     * 每日结束时间
     */
    private Date endTime;
    /**
     * 启用状态
     */
    private Integer status;
    /**
     * 创建时间
     */
    private Date createTime;

    private List<SeckillSkuRelationEntityVo> relationSkus;
}
package com.alatus.mall.seckill.constant;

public class SecKillConstants {
//    秒杀活动缓存前缀
    public static final String SESSION_CACHE_PREFIX = "seckill:sessions:";
//    秒杀商品缓存前缀
    public static final String SKU_CACHE_PREFIX = "seckill:skus";
//    库存信号量前缀
    public static final String SKU_STOCK_SEMAPHORE = "seckill:stock:";
}

 

package com.alatus.mall.seckill.constant;

public class SecKillConstants {
//    秒杀活动缓存前缀
    public static final String SESSION_CACHE_PREFIX = "seckill:sessions:";
//    秒杀商品缓存前缀
    public static final String SKU_CACHE_PREFIX = "seckill:skus";
//    库存信号量前缀
    public static final String SKU_STOCK_SEMAPHORE = "seckill:stock:";
}
package com.alatus.mall.seckill.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        //    所有对redisson的操作都是通过这个client对象
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        return Redisson.create(config);
    }
}

 

package com.alatus.mall.seckill.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        //    所有对redisson的操作都是通过这个client对象
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        return Redisson.create(config);
    }
}
package com.alatus.mall.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        //    所有对redisson的操作都是通过这个client对象
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        return Redisson.create(config);
    }
}

 

package com.alatus.mall.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        //    所有对redisson的操作都是通过这个client对象
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        return Redisson.create(config);
    }
}
package com.alatus.mall.seckill.feign;

import com.alatus.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient("Mall-product")
public interface ProductFeignService {
    /**
     * 信息
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    R getSkuInfo(@PathVariable("skuId") Long skuId);
}

 

package com.alatus.mall.seckill.feign;

import com.alatus.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient("Mall-product")
public interface ProductFeignService {
    /**
     * 信息
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    R getSkuInfo(@PathVariable("skuId") Long skuId);
}
package com.alatus.mall.seckill.to;

import com.alatus.mall.seckill.vo.SkuInfoVo;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class SecKillSkuRedisTo {
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private BigDecimal seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

    /**
     * 商品详细信息
     */
    private SkuInfoVo skuInfo;
    /**
     * 秒杀开始时间
     */
    private Long startTime;
    /**
     * 秒杀结束时间
     */
    private Long endTime;
    /**
     * 秒杀随机码
     */
    private String randomCode;
}

 

package com.alatus.mall.seckill.to;

import com.alatus.mall.seckill.vo.SkuInfoVo;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class SecKillSkuRedisTo {
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private BigDecimal seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

    /**
     * 商品详细信息
     */
    private SkuInfoVo skuInfo;
    /**
     * 秒杀开始时间
     */
    private Long startTime;
    /**
     * 秒杀结束时间
     */
    private Long endTime;
    /**
     * 秒杀随机码
     */
    private String randomCode;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值