通用的報表緩存設計(Spring AOP + Redis)非常全面!快看看吧!

優化背景

1:數據統計類型的項目,用戶量多、業務數據量大、計算邏輯複雜。
2:項目開發工作臨近完成,不宜大改動。

方案

1:針對背景第1點,需要使用Redis把複用性高的統計結果緩存起來。減少重複的計算,減少數據庫壓力,提高服務響應速度。
2:使用 註解+AOP的技術,達到代碼修改最小化、緩存可配置化。
下圖是我整個設計的詳細思維:


通用的報表緩存設計(Spring AOP + Redis)非常全面!快看看吧!

實現

流程


通用的報表緩存設計(Spring AOP + Redis)非常全面!快看看吧!

流 程

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

RestResult

around

(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

RedisCashe

getCacheConfig

(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

String

getKey

(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

DphuRptBiz

implements

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】

看完本篇文章是什麼感受呢?是不是學習到了一些,如果覺得還行的話,動動您的金手指點個關注收藏轉發吧!謝謝您的支持!繼續加油整理有用的文章分享給大家!一起加油!歡迎指導評論!我會收集評論對大家進行了解回答!謝謝您的支持!


分享到:


相關文章: