Просмотр исходного кода

[优化]统一缓存管理,获取缓存列表和删除接口

release_toaliyun_real
Burce 6 лет назад
Родитель
Сommit
913e40535d
5 измененных файлов: 181 добавлений и 78 удалений
  1. +49
    -36
      mallinkBApi/src/main/java/com/iformall/aop/RedisCacheAspect.java
  2. +48
    -34
      mallinkCApi/src/main/java/com/iformall/aop/RedisCacheAspect.java
  3. +17
    -8
      mallinkCApi/src/main/java/com/iformall/controller/WxMallController.java
  4. +22
    -0
      mallinkService/src/main/java/com/iformall/service/RedisService.java
  5. +45
    -0
      mallinkService/src/main/java/com/iformall/service/impl/RedisServiceImpl.java

+ 49
- 36
mallinkBApi/src/main/java/com/iformall/aop/RedisCacheAspect.java Просмотреть файл

@@ -3,7 +3,9 @@ package com.iformall.aop;

import com.alibaba.fastjson.JSONObject;
import com.iformall.annotation.RedisCache;
import com.iformall.common.ErrorCode;
import com.iformall.domain.po.WxCUser;
import com.iformall.exception.MallinkException;
import com.iformall.service.WxCUserTokenService;
import com.iformall.utils.HashUtil;
import com.iformall.utils.RedisLock;
@@ -21,8 +23,8 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@@ -54,7 +56,7 @@ public class RedisCacheAspect {
}

@Around("cacheAspect()")
public Object round(ProceedingJoinPoint jp, RedisCache cache) {
public Object round(ProceedingJoinPoint jp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 查询token信息
//从header中获取token
@@ -73,11 +75,15 @@ public class RedisCacheAspect {

// 请求参数
Object[] args = jp.getArgs();

// 接口 返回结果
Object result;

String redisKey;

//得到注释的名称
MethodSignature signature = (MethodSignature) jp.getSignature();
//判断tag是否在用户权限中 如果存在加入参数查询
Method method = signature.getMethod();
RedisCache cache = method.getAnnotation(RedisCache.class) ;
if (cache.autoKey()) {
// 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3)
String requestUrl = request.getRequestURI();
@@ -94,47 +100,54 @@ public class RedisCacheAspect {

// redisKey 不存在,获取接口数据并返回
if (StringUtils.isBlank(redisKey)) {
result = proceed(jp, args, null);
result = jp.proceed(args);
} else {
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
String value = valueOperations.get(redisKey);
if (value == null) {
result = proceed(jp, args, StringUtils.join(redisKey, ":", "lock"));
String json_data = JSONObject.toJSONString(result);
valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit());
log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime());
String lockKey = StringUtils.join(redisKey, ":", "lock");
long time = System.currentTimeMillis() + 2000;
String timeStr = String.valueOf(time);
try {
//分布式锁,保证一个线程读DB,其它线程排队
if (redisLock.lock2(lockKey, timeStr)) {
log.debug("CacheAspect 读库中, key:{}: " + lockKey);
result = jp.proceed(args);
String json_data = JSONObject.toJSONString(result);
valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit());
log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime());
} else {
log.debug("CacheAspect 读库等待中, key:{}: " + lockKey);
Thread.sleep(2000);
value = valueOperations.get(redisKey);
result = parseCache(jp, redisKey, value);
}
} catch (Throwable throwable) {
log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()),
jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable);
if (throwable instanceof MallinkException) {
throw throwable;
}
throw new MallinkException(ErrorCode.SYS_SERVER_ERROR);
} finally {
redisLock.unlock(lockKey, timeStr);
}
} else {
Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType();
result = JSONObject.parseObject(value, returnType);
log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey);
result = parseCache(jp, redisKey, value);
}
// 计数器
stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1);
}
return result;
}

private Object proceed(ProceedingJoinPoint jp, Object[] args, String lockKey) {
// 超时1秒
long time = System.currentTimeMillis() + 1000;
String timeStr = String.valueOf(time);
try {
//分布式锁,保证一个线程读DB,其它线程排队
if (StringUtils.isBlank(lockKey)) {
//log.debug("CacheAspect 读库并返回,无锁");
return jp.proceed(args);
} else if (redisLock.lock2(lockKey, timeStr)) {
log.debug("CacheAspect 读库中, key:{}: " + lockKey);
return jp.proceed(args);
} else {
// TODO 挂起,重试
log.debug("CacheAspect 排队中, key:{}: " + lockKey);
}
} catch (Throwable throwable) {
log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()),
jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable);
} finally {
redisLock.unlock(lockKey, timeStr);
private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) {
if (StringUtils.isBlank(value)) {
log.debug("CacheAspect 缓存存在,解析缓存数据为空:{},key:{}", value, redisKey);
return null;
}
return null;
Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType();
log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey);
return JSONObject.parseObject(value, returnType);
}

}
}

+ 48
- 34
mallinkCApi/src/main/java/com/iformall/aop/RedisCacheAspect.java Просмотреть файл

@@ -3,7 +3,9 @@ package com.iformall.aop;

import com.alibaba.fastjson.JSONObject;
import com.iformall.annotation.RedisCache;
import com.iformall.common.ErrorCode;
import com.iformall.domain.po.WxCUser;
import com.iformall.exception.MallinkException;
import com.iformall.service.WxCUserTokenService;
import com.iformall.utils.HashUtil;
import com.iformall.utils.RedisLock;
@@ -22,6 +24,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
@@ -53,7 +56,7 @@ public class RedisCacheAspect {
}

@Around("cacheAspect()")
public Object round(ProceedingJoinPoint jp, RedisCache cache) {
public Object round(ProceedingJoinPoint jp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 查询token信息
//从header中获取token
@@ -72,11 +75,15 @@ public class RedisCacheAspect {

// 请求参数
Object[] args = jp.getArgs();

// 接口 返回结果
Object result;

String redisKey;

//得到注释的名称
MethodSignature signature = (MethodSignature) jp.getSignature();
//判断tag是否在用户权限中 如果存在加入参数查询
Method method = signature.getMethod();
RedisCache cache = method.getAnnotation(RedisCache.class) ;
if (cache.autoKey()) {
// 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3)
String requestUrl = request.getRequestURI();
@@ -93,47 +100,54 @@ public class RedisCacheAspect {

// redisKey 不存在,获取接口数据并返回
if (StringUtils.isBlank(redisKey)) {
result = proceed(jp, args, null);
result = jp.proceed(args);
} else {
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
String value = valueOperations.get(redisKey);
if (value == null) {
result = proceed(jp, args, StringUtils.join(redisKey, ":", "lock"));
String json_data = JSONObject.toJSONString(result);
valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit());
log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime());
String lockKey = StringUtils.join(redisKey, ":", "lock");
long time = System.currentTimeMillis() + 2000;
String timeStr = String.valueOf(time);
try {
//分布式锁,保证一个线程读DB,其它线程排队
if (redisLock.lock2(lockKey, timeStr)) {
log.debug("CacheAspect 读库中, key:{}: " + lockKey);
result = jp.proceed(args);
String json_data = JSONObject.toJSONString(result);
valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit());
log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime());
} else {
log.debug("CacheAspect 读库等待中, key:{}: " + lockKey);
Thread.sleep(2000);
value = valueOperations.get(redisKey);
result = parseCache(jp, redisKey, value);
}
} catch (Throwable throwable) {
log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()),
jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable);
if (throwable instanceof MallinkException) {
throw throwable;
}
throw new MallinkException(ErrorCode.SYS_SERVER_ERROR);
} finally {
redisLock.unlock(lockKey, timeStr);
}
} else {
Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType();
result = JSONObject.parseObject(value, returnType);
log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey);
result = parseCache(jp, redisKey, value);
}
// 计数器
stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1);
}
return result;
}

private Object proceed(ProceedingJoinPoint jp, Object[] args, String lockKey) {
// 超时1秒
long time = System.currentTimeMillis() + 1000;
String timeStr = String.valueOf(time);
try {
//分布式锁,保证一个线程读DB,其它线程排队
if (StringUtils.isBlank(lockKey)) {
//log.debug("CacheAspect 读库并返回,无锁");
return jp.proceed(args);
} else if (redisLock.lock2(lockKey, timeStr)) {
log.debug("CacheAspect 读库中, key:{}: " + lockKey);
return jp.proceed(args);
} else {
// TODO 挂起,重试
log.debug("CacheAspect 排队中, key:{}: " + lockKey);
}
} catch (Throwable throwable) {
log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()),
jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable);
} finally {
redisLock.unlock(lockKey, timeStr);
private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) {
if (StringUtils.isBlank(value)) {
log.debug("CacheAspect 缓存存在,解析缓存数据为空:{},key:{}", value, redisKey);
return null;
}
return null;
Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType();
log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey);
return JSONObject.parseObject(value, returnType);
}

}

+ 17
- 8
mallinkCApi/src/main/java/com/iformall/controller/WxMallController.java Просмотреть файл

@@ -7,8 +7,6 @@ import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.po.*;
import com.iformall.domain.po.base.TenantEntity;
import com.iformall.domain.vo.WxMerchantVo;
import com.iformall.enums.EnumMerchantStatus;
import com.iformall.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -17,13 +15,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

@RestController
@RequestMapping("/api/mall")
@@ -46,6 +40,9 @@ public class WxMallController extends BaseController {
@Autowired
private WxLevelConfigService wxLevelConfigService;

@Autowired
private RedisService redisService;

@RedisCache
@AuthIgnore
@ApiOperation("商场图标")
@@ -124,4 +121,16 @@ public class WxMallController extends BaseController {
return new ResultData(version);
}

@ApiOperation("获取热点缓存接口对应redis key")
@GetMapping("/hotApis")
public ResultData hotApis() {
return new ResultData(redisService.hotApi());
}

@ApiOperation("删除指定key")
@PostMapping("/delRedisKey")
public ResultData delRedisKey(@RequestBody List<String> keys) {
return new ResultData(redisService.delRedisKey(keys));
}

}

+ 22
- 0
mallinkService/src/main/java/com/iformall/service/RedisService.java Просмотреть файл

@@ -0,0 +1,22 @@
package com.iformall.service;

import java.util.List;
import java.util.Set;

public interface RedisService {

String KEY_HOT_API = "hotapi" ;

/**
* 获取热点缓存接口列表
* @return
*/
Set<Object> hotApi() ;

/**
* 清除指定key
* @param keys
* @return
*/
boolean delRedisKey(List<String> keys) ;
}

+ 45
- 0
mallinkService/src/main/java/com/iformall/service/impl/RedisServiceImpl.java Просмотреть файл

@@ -0,0 +1,45 @@
package com.iformall.service.impl;

import com.iformall.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class RedisServiceImpl implements RedisService {

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Override
public Set<Object> hotApi() {
return stringRedisTemplate.opsForHash().keys(KEY_HOT_API);
}

@Override
public boolean delRedisKey(List<String> keys) {
if (CollectionUtils.isEmpty(keys)) {
return false;
}
boolean result;
stringRedisTemplate.multi();
try {
stringRedisTemplate.opsForValue().getOperations().delete(keys);
stringRedisTemplate.opsForHash().delete(KEY_HOT_API, keys.toArray());
result = true;
} catch (Exception e) {
log.error("delRedisKey error", e);
stringRedisTemplate.discard();
result = false;
} finally {
stringRedisTemplate.exec();
}
return result;
}
}

Загрузка…
Отмена
Сохранить