作者 朱兆平

用户目录缓存处理优化-未完全完成

... ... @@ -19,8 +19,8 @@ public @interface RedisCacheable {
//缓存key,KEY的名称必须是【#+参数的名称,或者#+{对象名称}】,参考spel表达式
String cacheKey();
//有效期时间(单位:秒),默认8个小时
int expire() default 28800;
//有效期时间(单位:秒),默认5分钟
int expire() default 5*60;
//缓存主动刷新时间(单位:秒),默认不主动刷新
int reflash() default -1;
... ...
... ... @@ -6,9 +6,12 @@ import com.tianbo.warehouse.annotation.cache.annotation.RedisCachePut;
import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable;
import com.tianbo.warehouse.annotation.cache.util.JDKSerializeUtil;
import com.tianbo.warehouse.annotation.cache.util.redis.DefaultKeyGenerator;
import com.tianbo.warehouse.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -17,6 +20,8 @@ import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Aspect
... ... @@ -49,16 +54,18 @@ public class RedisCacheableAspect {
ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
byte[] value = (byte[]) valueOper.get(key);
if(value != null){
log.info("[CACHE-HIT]-命中:[{}],缓存时效:[{}]分",key,redisTemplate.getExpire(key, TimeUnit.MINUTES));
//如果缓存有值,需要判断刷新缓存设置和当前缓存的失效时间
int reflash = cache.reflash();
if(reflash > 0){
if(reflash > 0 && StringUtils.isNotEmpty(key)){
//查询当前缓存失效时间是否在主动刷新规则范围内
long exp = redisTemplate.getExpire(key, TimeUnit.SECONDS);
if(exp <= reflash){
//主动刷新缓存,为不影响本次获取效率,采用异步线程刷新缓存
}
}
return JDKSerializeUtil.unserialize(value);
valueData = JDKSerializeUtil.unserialize(value);
return valueData;
}
//缓存中没有值,执行实际数据查询方法
if(valueData == null) {
... ... @@ -66,9 +73,13 @@ public class RedisCacheableAspect {
}
//写入缓存
if(cache.expire() > 0) {
valueOper.set(key, JDKSerializeUtil.serialize(valueData),cache.expire(),TimeUnit.SECONDS); //否则设置缓存时间 ,序列化存储
//否则设置缓存时间 ,序列化存储
valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)),cache.expire(),TimeUnit.SECONDS);
log.info("[CACHE-SET]-已设置缓存:[{}],缓存时效:[{}秒]",key,cache.expire());
} else {
valueOper.set(key, JDKSerializeUtil.serialize(valueData));
//永不过期
valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)));
log.info("[CACHE-SET]-已设置永久缓存:[{}]",key);
}
}
return valueData;
... ... @@ -89,15 +100,17 @@ public class RedisCacheableAspect {
public Object cachePut (final ProceedingJoinPoint pjp , RedisCachePut cacheput) throws Throwable{
try{
//生成缓存KEY
String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheNames(), cacheput.cacheKey());
// String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheNames(), cacheput.cacheKey());
String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheKey());
Object valueData = pjp.proceed();
//写入缓存
for(String key : keys){
ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
if(cacheput.expire() > 0) {
valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0]),cacheput.expire(),TimeUnit.SECONDS);
valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)),cacheput.expire(),TimeUnit.SECONDS);
log.info("[CACHE-SET]-更新缓存:[{}],缓存时效:[{}秒]",key,cacheput.expire());
} else {
valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0]));
valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)));
}
}
return valueData;
... ... @@ -151,6 +164,7 @@ public class RedisCacheableAspect {
@Around(value = "@annotation(redisCacheDelTarget)")
public Object cacheEvict (final ProceedingJoinPoint pjp , RedisCacheDelTarget redisCacheDelTarget) throws Throwable{
try{
Set<String> deleteBatchByKeys = RedisUtils.deleteBatchByKeys(redisCacheDelTarget.cacheKey());
String cacheKey = redisCacheDelTarget.cacheKey();
boolean allEntries = redisCacheDelTarget.allEntries();
if(allEntries){
... ...
... ... @@ -51,8 +51,9 @@ public class DefaultKeyGenerator {
MethodSignature methodSignature = (MethodSignature) signature;
String targetClassName = pjp.getTarget().getClass().getName();
String targetMethodName = methodSignature.getName();
//method参数列表
//method参数
String[] parameterNames = methodSignature.getParameterNames();
//method参数值
Object[] args = pjp.getArgs();
StringBuffer sb = new StringBuffer();
... ... @@ -98,21 +99,20 @@ public class DefaultKeyGenerator {
* 开始生成缓存key名称
*/
MethodSignature methodSignature = (MethodSignature) signature;
String targetClassName = pjp.getTarget().getClass().getName();
String targetClassName = pjp.getTarget().getClass().getSimpleName();
String targetMethodName = methodSignature.getName();
//method参数列表
String[] parameterNames = methodSignature.getParameterNames();
Object[] args = pjp.getArgs();
StringBuffer sb = new StringBuffer();
sb.append(targetClassName).append("_")
.append(targetMethodName).append("_")
.append(cacheKey).append("_");
sb.append(cacheKey).append(":").append(targetClassName).append(":")
.append(targetMethodName).append(":");
//evaluationContext存储参数的kEY与Value
for(int i = 0; i < parameterNames.length; i++){
String parameterName = parameterNames[i];
sb.append("P").append(parameterName).append("_")
.append("V").append(args[i]).append("_");
sb.append("P").append(parameterName).append(":")
.append("V").append(args[i]).append(":");
evaluationContext.setVariable(parameterName,args[i]);
}
... ...
... ... @@ -192,7 +192,7 @@ public class PermissionServiceImp implements PermissionService {
}
@Override
// @RedisCacheable(cacheKey = "getUserMenuTreeByUserId")
@RedisCacheable(cacheKey = "getUserMenuTreeByUserId")
public List<PERMISSION> getUserMenuTreeByUserId(@NotNull Integer userId) {
List<PERMISSION> loginedUserMenus = permissionMapper.getUserMenuTreeByUserId(userId);
List<PERMISSION> loginedUserMenusTree = loginedUserMenus.stream()
... ...
... ... @@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheDelTarget;
import com.tianbo.warehouse.dao.DepartmentMapper;
import com.tianbo.warehouse.dao.ROLEMapper;
import com.tianbo.warehouse.dao.RolePermissionMapper;
... ... @@ -90,6 +91,7 @@ public class RoleServiceImp implements RoleService{
return roleMapper.insertSelective(record);
}
@RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId")
@Transactional(rollbackFor = Exception.class)
@Override
public int setRolePermissoin(RolePermission record){
... ...
... ... @@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheDelTarget;
import com.tianbo.warehouse.dao.PERMISSIONMapper;
import com.tianbo.warehouse.dao.ROLEMapper;
import com.tianbo.warehouse.dao.USERSMapper;
... ... @@ -14,6 +15,7 @@ import com.tianbo.warehouse.model.UserRole;
import com.tianbo.warehouse.service.PermissionService;
import com.tianbo.warehouse.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
... ... @@ -23,6 +25,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Random;
@Cacheable
@Service(value = "userService")
public class UserServiceImpl implements UserService{
... ... @@ -153,6 +156,7 @@ public class UserServiceImpl implements UserService{
* @return
*/
@Override
@RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId")
@Transactional(rollbackFor = Exception.class)
public int setUserRole(UserRole userRole){
try{
... ... @@ -166,10 +170,6 @@ public class UserServiceImpl implements UserService{
userRoleMapper.insertSelective(ur);
}
}
/**
* 重写redis用户权限等相关资料
*/
return 1;
}catch (Exception e){
e.printStackTrace();
... ...
package com.tianbo.warehouse.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
... ... @@ -27,6 +26,14 @@ public class RedisUtils {
this.redisTemplate = redisTemplate;
}
public static RedisUtils redisUtils;
@PostConstruct
public void init(){
redisUtils = this;
redisUtils.redisTemplate=this.redisTemplate;
}
/**
* 指定缓存失效时间
* @param key 键
... ... @@ -83,6 +90,39 @@ public class RedisUtils {
}
}
/**
* 【 递归删除redis key-value 】
* 效率会低点 但是删除方法安全
* @author yangjunxiong
* @date 2021/6/21 10:05
**/
public static Set<String> deleteBatchByKeys(String key) {
Set<String> keys = new HashSet<>();
redisUtils.redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
.match(key + "*")
.count(5000).build())) {
while (cursor.hasNext()) {
keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return keys;
});
if (CollectionUtils.isEmpty(keys)) {
return Collections.emptySet();
}
for (String key1 : keys) {
redisUtils.redisTemplate.delete(key1);
}
if (!CollectionUtils.isEmpty(keys)) {
return RedisUtils.deleteBatchByKeys(key);
}
return keys;
}
//============================String=============================
/**
* 普通缓存获取
... ...
... ... @@ -232,7 +232,7 @@ WHERE
where role_id = #{roleId,jdbcType=INTEGER}
</update>
<select id="findAll" parameterType="java.lang.String" resultMap="TreeWithResultMap">
<select id="findAll" parameterType="java.lang.String" resultMap="TreeWithPermResultMap">
SELECT
<include refid="Base_Column_List" />
FROM role
... ...