商城项目秒杀通过Redisson设置信号量和秒杀随机码的设计保证秒杀业务稳定-----商城项目
精选
原创
©著作权归作者所有:来自51CTO博客作者旧约Alatus的原创作品,请联系作者获取转载授权,否则将追究法律责任
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.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.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.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.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;
}