優化背景
1:數據統計類型的項目,用戶量多、業務數據量大、計算邏輯複雜。
2:項目開發工作臨近完成,不宜大改動。
方案
1:針對背景第1點,需要使用Redis把複用性高的統計結果緩存起來。減少重複的計算,減少數據庫壓力,提高服務響應速度。
2:使用 註解+AOP的技術,達到代碼修改最小化、緩存可配置化。
下圖是我整個設計的詳細思維:
實現
流程
Redis配置
spring.redis.host=172.26.175.74
spring.redis.port=6379
spring.redis.password=admin
#0:不啟用 1:啟用
report.redis.enable=1
註解
<code>import
java.lang.annotation.*; (ElementType.METHOD) (RetentionPolicy.RUNTIME)public
RedisCashe {String
cacheName
()
;int
expire
()
; }/<code>
AOP緩存(關鍵)
<code>public
class
RedisProcess
{private
static
final
Logger logger = LoggerFactory.getLogger(RedisProcess.
class
); RedisTemplate redisTemplate;private
static
final
List typeList;static
{ ArrayList rs =new
ArrayList<>(); rs.add(String.
class
.getTypeName
()); rs.add(int
.
class
.getTypeName
()); rs.add(Integer.
class
.getTypeName
()); rs.add(double
.
class
.getTypeName
()); rs.add(BigDecimal.
class
.getTypeName
()); rs.add(boolean
.
class
.getTypeName
()); rs.add(byte
.
class
.getTypeName
()); rs.add(Date.
class
.getTypeName
()); rs.add(Long.
class
.getTypeName
()); typeList= rs; } ("@annotation(com.config.RedisCashe)"
)public
void
pointcutMethod
()
{ logger.debug("************攔截命中***************"
); } ("${report.redis.enable}"
)private
int
reportCacheEnable ; ("pointcutMethod()"
)
public
RestResultaround
(ProceedingJoinPoint joinPoint)
{ logger.debug("************攔截命中***************"
);long
startTime = System.currentTimeMillis(); Object cdn = joinPoint.getArgs()[0
]; String key = getKey(joinPoint);if
(reportCacheEnable==1
&& redisTemplate.hasKey(key)) { Object obj = redisTemplate.opsForValue().get(key); logger.info("Redis的KEY值:"
+ key); logger.info("**********從Redis中查到了數據**********"
);return
(RestResult) obj; } logger.info("Redis緩存命中耗時:"
+ (System.currentTimeMillis() - startTime)); logger.info("**********沒有從Redis查到數據**********"
);try
{ logger.info("**********執行業務方法**********"
); RestResult rs = (RestResult) joinPoint.proceed(); logger.info("**********Redis緩存**********"
);if
(reportCacheEnable==1
) { RedisCashe cacheCfg =getCacheConfig(joinPoint); redisTemplate.opsForValue().set(key,rs,cacheCfg.expire(), TimeUnit.SECONDS); } logger.info("Redis緩存未命中耗時:"
+ (System.currentTimeMillis() - startTime));return
rs; }catch
(Throwable e) { e.printStackTrace(); }return
null
; }private
RedisCashegetCacheConfig
(ProceedingJoinPoint joinPoint)
{ String methodName =joinPoint.getSignature().getName(); RedisCashe rs ; Method[] methods =joinPoint.getTarget().getClass().getMethods();for
(Method m :methods) {if
(methodName.equals(m.getName()) ) { Annotation[] annotations =m.getDeclaredAnnotations();for
(Annotation a: annotations)if
(RedisCashe.
class
.isInstance
(a
)) { rs = ( RedisCashe) a;return
rs; } } }return
null
; }private
StringgetKey
(ProceedingJoinPoint joinPoint)
{ Object[] args = joinPoint.getArgs(); RedisCashe cacheCfg =getCacheConfig(joinPoint); String key="RptCacheName:"
+cacheCfg.cacheName();for
(int
i=0
;i< args.length;i++) { Object o=args[i]; String oType =o.getClass().getTypeName();if
(typeList.contains(oType)) { key+="@"
+ o.toString(); }else
{ key+="@"
+new
Gson().toJson(o); } }return
key; } }/<code>
業務調用
<code> ("dphuRptBiz"
)public
class
DphuRptBizimplements
IDphuRptBiz { DphuRptMapper mapper; ( cacheName ="getDphuDatePassPrc"
, expire =3600
*60
*24
)public
RestResult> getDphuDatePassPrc(DphuInput inp) { inp.setResult(new
ArrayList()); RestResult> rs=new
RestResult<>(); rs.setResult(1
); mapper.getDphuDatePassPassRpt(inp); rs.setMsg("獲取成功"
); rs.setData(inp.getResult());return
rs; } ( cacheName ="getDphuWorstPrc"
, expire =3600
*60
*24
)public
RestResult> getDphuWorstPrc(Date
vCurrentDate,String
vSumaryType,String
vGroupBy,String
vWhere,String
vTop) { DphuWorstInput inp =new
DphuWorstInput(); inp.setvCurrentDate(vCurrentDate); inp.setvSumaryType(vSumaryType); inp.setvGroupBy(vGroupBy); inp.setvWhere(vWhere); inp.setvTop(vTop); inp.setResult(new
ArrayList()); RestResult> rs=new
RestResult<>(); rs.setResult(1
); mapper.getDphuWorstRpt(inp); rs.setMsg("獲取成功"
); rs.setData(inp.getResult());return
rs; } }/<code>
關鍵是註解: @RedisCashe( cacheName = “getDphuDatePassPrc”, expire = 3600 * 60 * 24)
1:定義了緩存的Key格式(方面後期Redis操作)
2:定義過期時間
好的,大功告成。
測試結果:
第一次
[ DEBUG] [2020-03-24 14:56:19] 攔截命中***
[ INFO ] [2020-03-24 14:56:19] - Redis緩存AOP處理所用時間:11
[ INFO ] [2020-03-24 14:56:19] 沒有從Redis查到數據
[ INFO ] [2020-03-24 14:56:19] 執行業務方法
[ INFO ] [2020-03-24 14:56:25] Redis緩存
第二次
[ DEBUG] [2020-03-24 14:59:47] 攔截命中***
[ INFO ] [2020-03-24 14:59:47] 從Redis中查到了數據
[ INFO ] [2020-03-24 14:59:47] - Redis的KEY值:Base{rptName=‘getDphuDatePassPrc’, rptCondition={“result”:[],“vCurDate”:“2020-01-01 00:00:00”,“vGroupBy”:“factory_code”,“vSumaryType”:“10”,“vTop”:“4”,“vWhere”:" 1=1 "}}
[ INFO ] [2020-03-24 14:59:47] - REDIS的VALUE值:{“data”:[’’],“msg”:“獲取成功”,“result”:1}
PS:
1:第一次沒有命中緩存,耗時6秒
2:第二次命中緩存,耗時小於1秒
總結
1:性能提示顯著,應對多併發輕鬆自如。
2:使用切面編程,業務代碼沒有任何改動,緩存沒有景響到原來的開發進度。
整理來自:
https://blog.csdn.net/richyliu44/article/details/105071250,csdn博客【Richy Liu】
看完本篇文章是什麼感受呢?是不是學習到了一些,如果覺得還行的話,動動您的金手指點個關注收藏轉發吧!謝謝您的支持!繼續加油整理有用的文章分享給大家!一起加油!歡迎指導評論!我會收集評論對大家進行了解回答!謝謝您的支持!
關鍵字: joinPoint getTypeName logger