feat(PmsConstants.java): 商品详情缓存

1、添加商品详情本地缓存
2、商品详情分布式锁保证原子操作

Closes I3V770
This commit is contained in:
DaniR 2021-06-21 21:07:55 +08:00
parent 17dc8fdb59
commit 05dd9a07bf
3 changed files with 117 additions and 26 deletions

View File

@ -9,4 +9,8 @@ public interface PmsConstants {
String LOCKED_STOCK_PREFIX = "stock:locked:"; String LOCKED_STOCK_PREFIX = "stock:locked:";
String LOCK_SKU_PREFIX="lock:sku:"; String LOCK_SKU_PREFIX="lock:sku:";
String PRODUCT_DETAIL_CACHE = "product:detail:cache:";
String LOCK_PRODUCT_DETAIL="lock:product:detail";
} }

View File

@ -0,0 +1,42 @@
package com.youlai.mall.pms.config;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.youlai.mall.pms.pojo.dto.app.ProductFormDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
/**
* @author DaniR
* @version 1.0
* @description 本地缓存设置
* @createDate 2021/6/16 10:08
*/
@Slf4j
@Component
public class ProductLocalCache {
private Cache<String, ProductFormDTO> localCache = null;
@PostConstruct
private void init(){
localCache = CacheBuilder.newBuilder()
//设置本地缓存容器的初始容量
.initialCapacity(10)
//设置本地缓存的最大容量
.maximumSize(500)
//设置写缓存后多少秒过期
.expireAfterWrite(60, TimeUnit.SECONDS).build();
}
public void setLocalCache(String key, ProductFormDTO object){
localCache.put(key,object);
}
public ProductFormDTO get(String key){
return localCache.getIfPresent(key);
}
}

View File

@ -5,58 +5,103 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.redis.utils.RedisUtils;
import com.youlai.mall.pms.config.ProductLocalCache;
import com.youlai.mall.pms.mapper.PmsSpuMapper; import com.youlai.mall.pms.mapper.PmsSpuMapper;
import com.youlai.mall.pms.pojo.dto.SpuDTO;
import com.youlai.mall.pms.pojo.dto.app.ProductFormDTO; import com.youlai.mall.pms.pojo.dto.app.ProductFormDTO;
import com.youlai.mall.pms.pojo.entity.PmsSpuAttributeValue;
import com.youlai.mall.pms.pojo.entity.PmsSku; import com.youlai.mall.pms.pojo.entity.PmsSku;
import com.youlai.mall.pms.pojo.entity.PmsSpec; import com.youlai.mall.pms.pojo.entity.PmsSpec;
import com.youlai.mall.pms.pojo.entity.PmsSpu; import com.youlai.mall.pms.pojo.entity.PmsSpu;
import com.youlai.mall.pms.pojo.dto.SpuDTO; import com.youlai.mall.pms.pojo.entity.PmsSpuAttributeValue;
import com.youlai.mall.pms.service.IPmsSpuAttributeValueService;
import com.youlai.mall.pms.service.IPmsSkuService; import com.youlai.mall.pms.service.IPmsSkuService;
import com.youlai.mall.pms.service.IPmsSpecService; import com.youlai.mall.pms.service.IPmsSpecService;
import com.youlai.mall.pms.service.IPmsSpuAttributeValueService;
import com.youlai.mall.pms.service.IProductService; import com.youlai.mall.pms.service.IProductService;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import static com.youlai.mall.pms.common.constant.PmsConstants.LOCK_PRODUCT_DETAIL;
import static com.youlai.mall.pms.common.constant.PmsConstants.PRODUCT_DETAIL_CACHE;
/** /**
* @author haoxr * @author haoxr
* @date 2020-11-06 * @date 2020-11-06
*/ */
@Service @Service
@Slf4j
@AllArgsConstructor @AllArgsConstructor
public class ProductServiceImpl extends ServiceImpl<PmsSpuMapper, PmsSpu> implements IProductService { public class ProductServiceImpl extends ServiceImpl<PmsSpuMapper, PmsSpu> implements IProductService {
private IPmsSkuService iPmsSkuService; private final IPmsSkuService iPmsSkuService;
private IPmsSpuAttributeValueService iPmsSpuAttributeValueService; private final IPmsSpuAttributeValueService iPmsSpuAttributeValueService;
private IPmsSpecService iPmsSpecService; private final IPmsSpecService iPmsSpecService;
private final RedisUtils redisUtils;
private final RedissonClient redissonClient;
private final ProductLocalCache productLocalCache;
@Override @Override
public ProductFormDTO getProductById(Long spuId) { public ProductFormDTO getProductById(Long spuId) {
// spu //1一级本地缓存设置
PmsSpu spu = this.getById(spuId); ProductFormDTO product = productLocalCache.get(PRODUCT_DETAIL_CACHE + spuId);
SpuDTO SpuDTO = new SpuDTO(); if (null != product) {
BeanUtil.copyProperties(spu, SpuDTO); log.info("get LocalCache product:" + product);
if (StrUtil.isNotBlank(spu.getPics())) { return product;
// spu专辑图片转换处理 json字符串 -> List
List<String> pics = JSONUtil.toList(JSONUtil.parseArray(spu.getPics()), String.class);
SpuDTO.setPics(pics);
} }
// 属性 //2二级缓存设置Redis中获取商品详情信息
List<PmsSpuAttributeValue> attrs = iPmsSpuAttributeValueService.list( product = (ProductFormDTO) redisUtils.get(PRODUCT_DETAIL_CACHE + spuId);
new LambdaQueryWrapper<PmsSpuAttributeValue>( if (null != product) {
).eq(PmsSpuAttributeValue::getSpuId, spuId) log.info("get redis product:" + product);
); return product;
}
//3分布式锁保证原子操作
RLock lock = redissonClient.getLock(LOCK_PRODUCT_DETAIL + spuId);
try {
if (lock.tryLock()) {
// spu
PmsSpu spu = this.getById(spuId);
SpuDTO SpuDTO = new SpuDTO();
BeanUtil.copyProperties(spu, SpuDTO);
if (StrUtil.isNotBlank(spu.getPics())) {
// spu专辑图片转换处理 json字符串 -> List
List<String> pics = JSONUtil.toList(JSONUtil.parseArray(spu.getPics()), String.class);
SpuDTO.setPics(pics);
}
// 属性
List<PmsSpuAttributeValue> attrs = iPmsSpuAttributeValueService.list(
new LambdaQueryWrapper<PmsSpuAttributeValue>(
).eq(PmsSpuAttributeValue::getSpuId, spuId)
);
// 规格
List<PmsSpec> specs = iPmsSpecService.listBySpuId(spuId);
// sku
List<PmsSku> skuList = iPmsSkuService.list(new LambdaQueryWrapper<PmsSku>().eq(PmsSku::getSpuId, spuId));
// 规格 product = new ProductFormDTO(SpuDTO, attrs, specs, skuList);
List<PmsSpec> specs = iPmsSpecService.listBySpuId(spuId); //TODO 4需要判断商品是否是秒杀商品根据秒杀信息更新商品秒杀相关信息
log.info("get db product:" + product);
// sku redisUtils.set(PRODUCT_DETAIL_CACHE + spuId, product, 3600);
List<PmsSku> skuList = iPmsSkuService.list(new LambdaQueryWrapper<PmsSku>().eq(PmsSku::getSpuId, spuId)); productLocalCache.setLocalCache(PRODUCT_DETAIL_CACHE + spuId,product);
} else {
ProductFormDTO product = new ProductFormDTO(SpuDTO, attrs, specs, skuList); log.info("get redis2 product:" + product);
product = (ProductFormDTO) redisUtils.get(PRODUCT_DETAIL_CACHE + spuId);
if (null!=product) {
productLocalCache.setLocalCache(PRODUCT_DETAIL_CACHE + spuId, product);
}
}
} finally {
if (lock.isLocked()) {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
return product; return product;
} }
} }