From 913e40535d8e75793392de16092218e202f1f698 Mon Sep 17 00:00:00 2001 From: Burce Date: Wed, 6 May 2020 07:21:09 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=E7=BB=9F=E4=B8=80=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=AE=A1=E7=90=86=EF=BC=8C=E8=8E=B7=E5=8F=96=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=88=97=E8=A1=A8=E5=92=8C=E5=88=A0=E9=99=A4=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/iformall/aop/RedisCacheAspect.java | 85 +++++++++++-------- .../com/iformall/aop/RedisCacheAspect.java | 82 ++++++++++-------- .../iformall/controller/WxMallController.java | 25 ++++-- .../com/iformall/service/RedisService.java | 22 +++++ .../service/impl/RedisServiceImpl.java | 45 ++++++++++ 5 files changed, 181 insertions(+), 78 deletions(-) create mode 100644 mallinkService/src/main/java/com/iformall/service/RedisService.java create mode 100644 mallinkService/src/main/java/com/iformall/service/impl/RedisServiceImpl.java diff --git a/mallinkBApi/src/main/java/com/iformall/aop/RedisCacheAspect.java b/mallinkBApi/src/main/java/com/iformall/aop/RedisCacheAspect.java index de3212d83..83ddc9c16 100644 --- a/mallinkBApi/src/main/java/com/iformall/aop/RedisCacheAspect.java +++ b/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 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); } - -} +} \ No newline at end of file diff --git a/mallinkCApi/src/main/java/com/iformall/aop/RedisCacheAspect.java b/mallinkCApi/src/main/java/com/iformall/aop/RedisCacheAspect.java index fb1935a2f..e58baeb75 100644 --- a/mallinkCApi/src/main/java/com/iformall/aop/RedisCacheAspect.java +++ b/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 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); } - } diff --git a/mallinkCApi/src/main/java/com/iformall/controller/WxMallController.java b/mallinkCApi/src/main/java/com/iformall/controller/WxMallController.java index fe72a463a..9b2c39b8b 100644 --- a/mallinkCApi/src/main/java/com/iformall/controller/WxMallController.java +++ b/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 keys) { + return new ResultData(redisService.delRedisKey(keys)); + } + } diff --git a/mallinkService/src/main/java/com/iformall/service/RedisService.java b/mallinkService/src/main/java/com/iformall/service/RedisService.java new file mode 100644 index 000000000..1cffaefcd --- /dev/null +++ b/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 hotApi() ; + + /** + * 清除指定key + * @param keys + * @return + */ + boolean delRedisKey(List keys) ; +} diff --git a/mallinkService/src/main/java/com/iformall/service/impl/RedisServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/RedisServiceImpl.java new file mode 100644 index 000000000..bd21b508b --- /dev/null +++ b/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 hotApi() { + return stringRedisTemplate.opsForHash().keys(KEY_HOT_API); + } + + @Override + public boolean delRedisKey(List 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; + } +}