it_lv 3 долоо хоног өмнө
parent
commit
546e6e8d6f
1 өөрчлөгдсөн 54 нэмэгдсэн , 46 устгасан
  1. 54 46
      第6章 详情优化.md

+ 54 - 46
第6章 详情优化.md

@@ -398,21 +398,34 @@ public AlbumInfo getAlbumInfoFromDB(Long id) {
 ```java
 package com.atguigu.tingshu.common.cache;
 
+import com.atguigu.tingshu.common.constant.RedisConstant;
+
 import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
 
-/**
- * 自定义缓存注解
- */
 @Target({ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Inherited
 @Documented
 public @interface GuiGuCache {
+
+    /**
+     * 存入Redis业务数据以及锁的前缀
+     * @return
+     */
+    String prefix() default "";
+
     /**
-     * 缓存业务及分布式锁key的前缀
+     * 存入Redis缓存业务数据过期时间
      * @return
      */
-    String prefix() default "data:";
+    long ttl() default RedisConstant.ALBUM_TIMEOUT;
+
+    /**
+     * 时间单位
+     * @return
+     */
+    TimeUnit unit() default TimeUnit.SECONDS;
 }
 ```
 
@@ -425,7 +438,6 @@ package com.atguigu.tingshu.common.cache;
 
 import cn.hutool.core.util.RandomUtil;
 import com.atguigu.tingshu.common.constant.RedisConstant;
-import com.atguigu.tingshu.common.login.GuiGuLogin;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
@@ -437,87 +449,83 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
  * @author: atguigu
- * @create: 2024-08-17 08:57
+ * @create: 2025-06-10 11:41
  */
 @Slf4j
 @Aspect
 @Component
 public class GuiGuCacheAspect {
-
     @Autowired
     private RedisTemplate redisTemplate;
 
     @Autowired
     private RedissonClient redissonClient;
 
+
     /**
-     * 自定义缓存注解的切面逻辑
+     * 对所有方法上使用自定义缓存注解方法进行增强
      *
-     * @param pjp        切入点对象
-     * @param guiGuCache 方法使用使用注解对象
+     * @param joinPoint
+     * @param guiGuCache
      * @return
      * @throws Throwable
      */
     @Around("@annotation(guiGuCache)")
-    public Object doBasicProfiling(ProceedingJoinPoint pjp, GuiGuCache guiGuCache) throws Throwable {
+    public Object around(ProceedingJoinPoint joinPoint, GuiGuCache guiGuCache) throws Throwable {
         try {
-            //1.优先从缓存Redis中获取业务数据
-            //1.1 构建业务数据key 形式=注解前缀+方法参数
-            //1.1.1 获取注解前缀
-            String prefix = guiGuCache.prefix();
-            //1.1.2 获取方法参数如果存在多个参数采用_拼接
+            //1.优先从缓存中获取业务数据
+            //1.1 将方法参数采用"_"进行拼接作为Key的一部分
             String params = "none";
-            Object[] args = pjp.getArgs();
+            Object[] args = joinPoint.getArgs();
             if (args != null && args.length > 0) {
-                List<Object> objects = Arrays.asList(args);
-                params = objects.stream()
+                params = Arrays.stream(args)
                         .map(Object::toString)
                         .collect(Collectors.joining("_"));
             }
-            String dataKey = prefix + params;
-            //1.2 查询Redis获取业务数据
-            Object objectResult = redisTemplate.opsForValue().get(dataKey);
-
-            //2.如果命中缓存,直接返回业务数据即可(不需要查库)
-            if (objectResult != null) {
-                return objectResult;
+            //1.2 构建缓存业务数据Key
+            String dataKey = guiGuCache.prefix() + params;
+            //1.3 查询Redis中缓存数据
+            Object result = redisTemplate.opsForValue().get(dataKey);
+            if (result != null) {
+                return result;
             }
 
-            //3.如果未命中缓存,先获取分布式锁
-            //3.1 构建分布式锁key
+            //2.获取分布式锁:基于Redisson框架提供分布式锁
+            //2.1 构建锁Key 形式=业务Key+锁后缀
             String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX;
-            //3.2 获取锁对象
+            //2.2 基于RedissonClient对象创建锁对象
             RLock lock = redissonClient.getLock(lockKey);
-            //3.3 获取分布式锁
-            boolean flag = lock.tryLock();
 
-            //4.获取分布锁锁成功,执行查询数据库(目标方法执行),并设置缓存
+            //2.3 尝试获取分布式锁
+            boolean flag = lock.tryLock(1, TimeUnit.SECONDS);
+
+            //3.获取锁成功,执行查库业务方法(目标方法)
             if (flag) {
                 try {
-                    //4.1 执行目标方法(执行自定义注解修饰方法-查询数据库方法)
-                    objectResult = pjp.proceed();
-                    //4.2 将查询数据库业务数据放入Redis缓存
-                    int ttl = RandomUtil.randomInt(100, 600);
-                    redisTemplate.opsForValue().set(dataKey, objectResult, RedisConstant.ALBUM_TIMEOUT + ttl, TimeUnit.SECONDS);
-                    return objectResult;
+                    //3.1 执行目标查询数据库方法
+                    result = joinPoint.proceed();
+                    //3.2 将查询结果放入Redis
+                    long ttl = guiGuCache.ttl() + RandomUtil.randomInt(1200);
+                    redisTemplate.opsForValue().set(dataKey, result, ttl, guiGuCache.unit());
+                    return result;
                 } finally {
-                    //5.释放分布式
+                    //3.3 释放
                     lock.unlock();
                 }
             } else {
-                //6.获取分布式锁失败,进行自旋(自旋可能获取锁成功线程会将业务数据已经放入缓存)
-                return this.doBasicProfiling(pjp, guiGuCache);
+                //4.获取锁失败,自旋,可能就会命中缓存
+                Thread.sleep(500);
+                return this.around(joinPoint, guiGuCache);
             }
         } catch (Throwable e) {
-            //7.兜底处理-直接查询数据库
-            log.error("[自定义缓存切面]Redis服务不可用,执行兜底方案,查库。异常:{}", e);
-            return pjp.proceed();
+            //5.兜底处理:如果Redis服务不可用,则执行目标方法
+            log.error("Redis服务异常:{}", e.getMessage());
+            return joinPoint.proceed();
         }
     }
 }