1、商城项目商品服务三级分类缓存优化
2、新增redis分布式锁 3、新增redisson分布式锁 3、新增redis缓存配置
This commit is contained in:
parent
7495a0eca1
commit
77968524a9
8
pom.xml
8
pom.xml
|
|
@ -52,6 +52,7 @@
|
|||
<webmagic.version>0.7.5</webmagic.version>
|
||||
<spring-cloud-alicloud-oss.version>2.2.0.RELEASE</spring-cloud-alicloud-oss.version>
|
||||
<elasticsearch.version>7.12.1</elasticsearch.version>
|
||||
<redisson.version>3.12.0</redisson.version>
|
||||
|
||||
</properties>
|
||||
|
||||
|
|
@ -114,6 +115,13 @@
|
|||
<version>${elasticsearch.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--分布式锁,提供分布式对象等功能框架-->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjs</groupId>
|
||||
<artifactId>xjs-business-common</artifactId>
|
||||
|
|
|
|||
|
|
@ -9,26 +9,42 @@
|
|||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>公共模块-Redis模块</name>
|
||||
|
||||
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
|
||||
|
||||
<description>
|
||||
ruoyi-common-redis缓存服务
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot Boot Redis -->
|
||||
|
||||
<!-- SpringBoot Boot Redis 排除lettuce客户端,引入jedis客户端 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- RuoYi Common Core-->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package com.ruoyi.common.redis.configure;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.cache.CacheProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
|
@ -27,7 +31,12 @@ import java.time.Duration;
|
|||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
@EnableConfigurationProperties(CacheProperties.class)
|
||||
public class RedisConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Autowired
|
||||
private CacheProperties cacheProperties;
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings(value = {"unchecked", "rawtypes"})
|
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
|
|
@ -66,10 +75,24 @@ public class RedisConfig extends CachingConfigurerSupport {
|
|||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jacksonSeial.setObjectMapper(om);
|
||||
// 定制缓存数据序列化方式及时效
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)) //时效1天
|
||||
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial))
|
||||
.disableCachingNullValues();
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial));
|
||||
|
||||
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
|
||||
//将配置文件中所有的配置都生效
|
||||
if (redisProperties.getTimeToLive() != null) {
|
||||
//定义随机时间值,防止缓存大量过期
|
||||
long salt = RandomUtil.randomLong(10, 100);
|
||||
Duration time = redisProperties.getTimeToLive();
|
||||
Duration plusSeconds = time.plusSeconds(salt);
|
||||
config = config.entryTtl(plusSeconds);
|
||||
}
|
||||
if (!redisProperties.isCacheNullValues()) {
|
||||
config = config.disableCachingNullValues();
|
||||
}
|
||||
|
||||
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
|
||||
return cacheManager;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package com.ruoyi.common.redis.configure;
|
||||
|
||||
import lombok.Data;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Redisson分布式锁配置
|
||||
*
|
||||
* @author xiejs
|
||||
* @since 2022-04-08
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "redisson")
|
||||
@Data
|
||||
public class RedissonConfig {
|
||||
|
||||
private String ip;
|
||||
|
||||
private Integer port;
|
||||
|
||||
|
||||
/**
|
||||
* 所有对 Redisson 的使用都是通过 RedissonClient
|
||||
*
|
||||
* @return RedissonClient
|
||||
*/
|
||||
@Bean(destroyMethod = "shutdown",name = "redissonClient")
|
||||
public RedissonClient redissonClient() {
|
||||
// 1、创建配置
|
||||
Config config = new Config();
|
||||
// Redis url should start with redis:// or rediss://
|
||||
config.useSingleServer().setAddress("redis://" + ip + ":" + port + "");
|
||||
|
||||
// 2、根据 Config 创建出 RedissonClient 实例
|
||||
return Redisson.create(config);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -13,17 +13,17 @@ public class RedisConst {
|
|||
/**
|
||||
* 翻译字典常量key
|
||||
*/
|
||||
public static final String TRAN_DICT = "tianxing:tran_dict";
|
||||
public static final String TRAN_DICT = "bussiness:tianxing:tran_dict";
|
||||
|
||||
/**
|
||||
* 英语一言常量key
|
||||
*/
|
||||
public static final String ONE_ENGLISH = "tianxing:one_english";
|
||||
public static final String ONE_ENGLISH = "bussiness:tianxing:one_english";
|
||||
|
||||
/**
|
||||
* 热搜常量key
|
||||
*/
|
||||
public static final String HOT = "tianxing:hot";
|
||||
public static final String HOT = "bussiness:tianxing:hot";
|
||||
|
||||
/**
|
||||
* websocket常量key
|
||||
|
|
@ -33,32 +33,32 @@ public class RedisConst {
|
|||
/**
|
||||
* ip信息常量key
|
||||
*/
|
||||
public static final String IP_INFO = "ip_info";
|
||||
public static final String IP_INFO = "bussiness:ip_info";
|
||||
|
||||
/**
|
||||
* 实时天气常量信息key
|
||||
*/
|
||||
public static final String NOW_WEATHER = "weather:now";
|
||||
public static final String NOW_WEATHER = "bussiness:weather:now";
|
||||
|
||||
/**
|
||||
* 预报天气常量信息key
|
||||
*/
|
||||
public static final String FORECAST_WEATHER = "weather:forecast";
|
||||
public static final String FORECAST_WEATHER = "bussiness:weather:forecast";
|
||||
|
||||
/**
|
||||
* 爬虫记录循环次数常量信息:_36wallpaper
|
||||
*/
|
||||
public static final String REPTILE_36_WALLPAPER_COUNT = "reptile:_36wallpaper.count";
|
||||
public static final String REPTILE_36_WALLPAPER_COUNT = "bussiness:reptile:_36wallpaper.count";
|
||||
|
||||
/**
|
||||
* 爬虫记录循环次数常量信息:weixin.sougou
|
||||
*/
|
||||
public static final String REPTILE_WEIXIN_SOUGOU_COUNT = "reptile:weixin.sougou.count";
|
||||
public static final String REPTILE_WEIXIN_SOUGOU_COUNT = "bussiness:reptile:weixin.sougou.count";
|
||||
|
||||
/**
|
||||
* 爬虫记录循环次数常量信息:weixin.link
|
||||
*/
|
||||
public static final String REPTILE_WEIXIN_LINK_COUNT = "reptile:weixin.link.count";
|
||||
public static final String REPTILE_WEIXIN_LINK_COUNT = "bussiness:reptile:weixin.link.count";
|
||||
|
||||
|
||||
//--------------------------mall-key-----------------------------------
|
||||
|
|
@ -78,6 +78,11 @@ public class RedisConst {
|
|||
*/
|
||||
public static final String CATALOG_BEFORE = MALL_PREFIX + "catalog:before";
|
||||
|
||||
/**
|
||||
* Redis分布式锁key
|
||||
*/
|
||||
public static final String LOCK = "lock";
|
||||
|
||||
|
||||
//-------------------有效时间-----------------------
|
||||
public static final Integer TRAN_DICT_EXPIRE = 1; //小时
|
||||
|
|
@ -92,5 +97,7 @@ public class RedisConst {
|
|||
|
||||
public static final Long FORECAST_WHEATHER_EXPIRE = 10L; //分钟
|
||||
|
||||
public static final Long LOCK_EXPIRE = 30L; //秒
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class CopyWritingServiceImpl extends ServiceImpl<CopyWritingMapper, CopyW
|
|||
*/
|
||||
@Override
|
||||
//将该方法的查询结果存放阿紫springboot的默认缓存中 cacheNames:缓存空间唯一名称 key:缓存id
|
||||
@Cacheable(cacheNames = "copyWriting",key = "#id")
|
||||
@Cacheable(cacheNames = "bussiness:copy_writing",key = "#id")
|
||||
public CopyWriting selectCopyWritingById(Long id)
|
||||
{
|
||||
return copyWritingMapper.selectCopyWritingById(id);
|
||||
|
|
@ -71,7 +71,7 @@ public class CopyWritingServiceImpl extends ServiceImpl<CopyWritingMapper, CopyW
|
|||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(cacheNames = "copyWriting",key = "#ids")
|
||||
@CacheEvict(cacheNames = "bussiness:copy_writing",key = "#ids")
|
||||
public int deleteCopyWritingByIds(Long[] ids)
|
||||
{
|
||||
return copyWritingMapper.deleteCopyWritingByIds(ids);
|
||||
|
|
@ -84,7 +84,7 @@ public class CopyWritingServiceImpl extends ServiceImpl<CopyWritingMapper, CopyW
|
|||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(cacheNames = "copyWriting",key = "#id")
|
||||
@CacheEvict(cacheNames = "bussiness:copy_writing",key = "#id")
|
||||
public int deleteCopyWritingById(Long id)
|
||||
{
|
||||
return copyWritingMapper.deleteCopyWritingById(id);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.xjs.validation.group.UpdateGroup;
|
|||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -85,6 +86,7 @@ public class CategoryController {
|
|||
return R.ok();
|
||||
}
|
||||
|
||||
@CacheEvict(value = {"mall:catalog"},key = "'getLevel1Categorys'")
|
||||
@PutMapping("/update/sort")
|
||||
@ApiOperation("修改商品分类排序")
|
||||
@Log(title = "商品分类", businessType = BusinessType.UPDATE)
|
||||
|
|
@ -99,6 +101,7 @@ public class CategoryController {
|
|||
* 删除
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
@CacheEvict(value = {"mall:catalog"},key = "'getLevel1Categorys'")
|
||||
@ApiOperation("删除")
|
||||
@Log(title = "商品分类", businessType = BusinessType.DELETE)
|
||||
public R delete(@RequestBody Long[] catIds) {
|
||||
|
|
@ -106,6 +109,8 @@ public class CategoryController {
|
|||
return R.error("请选择删除的分类");
|
||||
}
|
||||
categoryService.removeMenuByIds(Arrays.asList(catIds));
|
||||
//删除缓存
|
||||
stringRedisTemplate.delete(Arrays.asList(CATALOG_BEFORE,CATALOG_AFTER));
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.xjs.mall.product.controller.web;
|
|||
import com.xjs.mall.product.entity.CategoryEntity;
|
||||
import com.xjs.mall.product.service.CategoryService;
|
||||
import com.xjs.mall.product.vo.Catelog2Vo;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
|
|
@ -24,21 +26,49 @@ public class IndexController {
|
|||
@Autowired
|
||||
private CategoryService categoryService;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@GetMapping({"/", "index.html"})
|
||||
public String indexPage(Model model) {
|
||||
//查出所有一级分类
|
||||
List<CategoryEntity> categoryEntityList=categoryService.getLevel1Categorys();
|
||||
List<CategoryEntity> categoryEntityList = categoryService.getLevel1Categorys();
|
||||
model.addAttribute("categorys", categoryEntityList);
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/index/json/catalog.json")
|
||||
@ResponseBody
|
||||
public Map<String,List<Catelog2Vo>> getCatalogJson() {
|
||||
public Map<String, List<Catelog2Vo>> getCatalogJson() {
|
||||
return categoryService.getCatalogJson();
|
||||
}
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping("hello")
|
||||
public String hello() {
|
||||
//获取一把锁
|
||||
RLock lock = redissonClient.getLock("my-lock");
|
||||
|
||||
//加锁
|
||||
lock.lock(); //阻塞时等待,默认假的锁是30s时间
|
||||
//锁的自动续期,如果业务超长,运行期间自动给锁续期30s,不用担心业务时间长,锁自动过期被删除
|
||||
//加锁的业务只要 运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后过期
|
||||
|
||||
|
||||
try {
|
||||
System.out.println("加锁成功" + Thread.currentThread().getName());
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
System.out.println("释放锁" + Thread.currentThread().getName());
|
||||
lock.unlock();
|
||||
|
||||
}
|
||||
return "hello";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,21 +17,28 @@ import com.xjs.mall.product.service.AttrService;
|
|||
import com.xjs.mall.product.service.CategoryBrandRelationService;
|
||||
import com.xjs.mall.product.service.CategoryService;
|
||||
import com.xjs.mall.product.vo.Catelog2Vo;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.xjs.consts.RedisConst.CATALOG_AFTER;
|
||||
import static com.xjs.consts.RedisConst.CATALOG_BEFORE;
|
||||
import static com.xjs.consts.RedisConst.*;
|
||||
|
||||
|
||||
@Service("categoryService")
|
||||
@Transactional
|
||||
@Log4j2
|
||||
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
|
||||
|
||||
@Resource
|
||||
|
|
@ -49,6 +56,9 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
@Autowired
|
||||
private AttrService attrService;
|
||||
|
||||
@Resource
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
|
||||
@Override
|
||||
public List<CategoryEntity> listWithTree() {
|
||||
|
|
@ -111,7 +121,7 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
categoryDao.deleteBatchIds(asList);
|
||||
|
||||
//删除后清除缓存
|
||||
stringRedisTemplate.delete(Arrays.asList(CATALOG_BEFORE,CATALOG_AFTER));
|
||||
stringRedisTemplate.delete(Arrays.asList(CATALOG_BEFORE, CATALOG_AFTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -126,6 +136,7 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = {"mall:catalog"},key = "'getLevel1Categorys'")
|
||||
public void updateCascade(CategoryEntity category) {
|
||||
//更新自己
|
||||
super.updateById(category);
|
||||
|
|
@ -134,10 +145,11 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
|
||||
|
||||
//删除缓存
|
||||
stringRedisTemplate.delete(Arrays.asList(CATALOG_BEFORE,CATALOG_AFTER));
|
||||
stringRedisTemplate.delete(Arrays.asList(CATALOG_BEFORE, CATALOG_AFTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = {"mall:catalog"},key = "#root.method.name",sync = true) //sync同步会锁方法,单机锁
|
||||
public List<CategoryEntity> getLevel1Categorys() {
|
||||
LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CategoryEntity::getParentCid, 0);
|
||||
|
|
@ -146,14 +158,19 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
|
||||
@Override
|
||||
public Map<String, List<Catelog2Vo>> getCatalogJson() {
|
||||
/**
|
||||
* 1、空结果缓存,解决缓存穿透
|
||||
* 2、设置过期时间加随机值,解决缓存雪崩
|
||||
* 3、加锁,解决缓存击穿
|
||||
*/
|
||||
|
||||
|
||||
//先查缓存
|
||||
String catalogJSON = stringRedisTemplate.opsForValue().get(CATALOG_BEFORE);
|
||||
if (StringUtils.isEmpty(catalogJSON)) {
|
||||
//缓存没有查数据库并且放入
|
||||
Map<String, List<Catelog2Vo>> catalogJsonFormDb = this.getCatalogJsonFormDb();
|
||||
String jsonString = JSON.toJSONString(catalogJsonFormDb);
|
||||
stringRedisTemplate.opsForValue().set(CATALOG_BEFORE, jsonString);
|
||||
return catalogJsonFormDb;
|
||||
|
||||
return this.getCatalogJsonFormDbWithRedissonLock();
|
||||
|
||||
} else {
|
||||
return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
|
||||
});
|
||||
|
|
@ -161,13 +178,19 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
|
||||
}
|
||||
|
||||
//可优化
|
||||
private Map<String, List<Catelog2Vo>> getCatalogJsonFormDb() {
|
||||
//获取锁之后再去缓存确认,如果没有就继续查询
|
||||
String catalogJSON = stringRedisTemplate.opsForValue().get(CATALOG_BEFORE);
|
||||
if (StringUtils.isNotEmpty(catalogJSON)) {
|
||||
return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
|
||||
});
|
||||
}
|
||||
|
||||
//查出所有1级分类
|
||||
List<CategoryEntity> level1Categorys = getLevel1Categorys();
|
||||
|
||||
//封装数据
|
||||
return level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
|
||||
Map<String, List<Catelog2Vo>> collect = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
|
||||
//每一个的一级分类,查到这个一级分类的二级分类
|
||||
List<CategoryEntity> categoryEntities = super.baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>().eq(CategoryEntity::getParentCid, v.getCatId()));
|
||||
|
||||
|
|
@ -195,6 +218,83 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity
|
|||
|
||||
return catelog2Vos;
|
||||
}));
|
||||
|
||||
//缓存没有查数据库并且放入
|
||||
String jsonString = JSON.toJSONString(collect);
|
||||
stringRedisTemplate.opsForValue().set(CATALOG_BEFORE, jsonString);
|
||||
|
||||
return collect;
|
||||
}
|
||||
|
||||
//使用redisson分布式锁
|
||||
private Map<String, List<Catelog2Vo>> getCatalogJsonFormDbWithRedissonLock() {
|
||||
|
||||
RLock lock = redissonClient.getLock(LOCK + ":catalog");
|
||||
lock.lock();
|
||||
|
||||
log.info("获取分布式锁成功");
|
||||
Map<String, List<Catelog2Vo>> catalogJsonFormDb;
|
||||
try {
|
||||
|
||||
catalogJsonFormDb = getCatalogJsonFormDb();
|
||||
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return catalogJsonFormDb;
|
||||
}
|
||||
|
||||
//使用Java本地可重入锁synchronized
|
||||
private Map<String, List<Catelog2Vo>> getCatalogJsonFormDbWithSynLock() {
|
||||
|
||||
//加锁,解决缓存击穿 (本地锁/单机锁 分布式服务锁不住)
|
||||
synchronized (this) {
|
||||
return this.getCatalogJsonFormDb();
|
||||
}
|
||||
}
|
||||
|
||||
//使用redis分布式锁
|
||||
private Map<String, List<Catelog2Vo>> getCatalogJsonFormDbWithRedisLock() {
|
||||
|
||||
//加锁,解决缓存击穿 分布式锁 去redis占坑
|
||||
String uuid = UUID.randomUUID().toString(); //添加唯一标识符
|
||||
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(LOCK, uuid, LOCK_EXPIRE, TimeUnit.SECONDS);
|
||||
if (Boolean.TRUE.equals(lock)) {
|
||||
log.info("获取分布式锁成功:{}", uuid);
|
||||
Map<String, List<Catelog2Vo>> catalogJsonFormDb = null;
|
||||
try {
|
||||
catalogJsonFormDb = this.getCatalogJsonFormDb();
|
||||
} finally {
|
||||
//lua脚本解锁
|
||||
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
||||
Long ret = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(LOCK), uuid);
|
||||
}
|
||||
|
||||
//加锁成功
|
||||
//设置过期时间必须和加锁同步(原子性)
|
||||
//stringRedisTemplate.expire(LOCK, LOCK_EXPIRE, TimeUnit.SECONDS);
|
||||
|
||||
//删除锁(判断是否是自己的锁)
|
||||
/*String lockValue = stringRedisTemplate.opsForValue().get(LOCK);
|
||||
if (uuid.equals(lockValue)) {
|
||||
stringRedisTemplate.delete(LOCK);
|
||||
}*/
|
||||
|
||||
|
||||
return catalogJsonFormDb;
|
||||
} else {
|
||||
log.warn("获取分布式锁失败:{}", uuid);
|
||||
//加锁失败...重试 自旋
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this.getCatalogJsonFormDbWithRedisLock();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//225,25,2
|
||||
|
|
|
|||
Loading…
Reference in New Issue