3.13 分布式缓存

This commit is contained in:
zhangsan 2025-03-13 18:27:29 +08:00
parent a6f7d83ecb
commit 144a55c1cf
4 changed files with 134 additions and 0 deletions

15
pom.xml
View File

@ -63,6 +63,21 @@
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 本地缓存 Caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>

View File

@ -1,7 +1,10 @@
package edu.whut.smilepicturebackend.controller;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import edu.whut.smilepicturebackend.annotation.AuthCheck;
import edu.whut.smilepicturebackend.common.BaseResponse;
import edu.whut.smilepicturebackend.common.DeleteRequest;
@ -21,12 +24,17 @@ import edu.whut.smilepicturebackend.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
@ -35,6 +43,16 @@ import java.util.List;
public class PictureController {
private final UserService userService;
private final PictureService pictureService;
private final StringRedisTemplate stringRedisTemplate;
/**
* 本地缓存
*/
private final Cache<String, String> LOCAL_CACHE = Caffeine.newBuilder()
.initialCapacity(1024)
.maximumSize(10_000L) // 最大 10000
.expireAfterWrite(Duration.ofMinutes(5)) // 缓存 5 分钟后移除
.build();
/**
* 上传图片可重新上传
@ -188,6 +206,52 @@ public class PictureController {
return ResultUtils.success(pictureService.getPictureVOPage(picturePage, request));
}
/**
* 分页获取图片列表封装类有缓存
*/
@Deprecated
@PostMapping("/list/page/vo/cache")
public BaseResponse<Page<PictureVO>> listPictureVOByPageWithCache(@RequestBody PictureQueryRequest pictureQueryRequest,
HttpServletRequest request) {
long current = pictureQueryRequest.getCurrent();
long size = pictureQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 普通用户默认只能看到审核通过的数据
pictureQueryRequest.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
// 查询缓存缓存中没有再查询数据库
// 构建缓存的 key,根据查询条件来构建->json
String queryCondition = JSONUtil.toJsonStr(pictureQueryRequest); //json->md5更轻量化去除括号双引号
String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
String cacheKey = String.format("smilepicture:listPictureVOByPage:%s", hashKey);
// // 1. 先从本地缓存中查询
// String cachedValue = LOCAL_CACHE.getIfPresent(cacheKey);
// if (cachedValue != null) {
// // 如果缓存命中返回结果
// Page<PictureVO> cachedPage = JSONUtil.toBean(cachedValue, Page.class);
// return ResultUtils.success(cachedPage);
// }
// 2. 本地缓存未命中查询 Redis 分布式缓存
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
String cachedValue = opsForValue.get(cacheKey);
if (cachedValue != null) {
Page<PictureVO> cachedPage = JSONUtil.toBean(cachedValue, Page.class);
return ResultUtils.success(cachedPage);
}
// 3. 查询数据库
Page<Picture> picturePage = pictureService.page(new Page<>(current, size),
pictureService.getQueryWrapper(pictureQueryRequest));
Page<PictureVO> pictureVOPage = pictureService.getPictureVOPage(picturePage, request);
// 4. 更新缓存
// 更新 Redis 缓存
String cacheValue = JSONUtil.toJsonStr(pictureVOPage);
// 设置缓存的过期时间5 - 10 分钟过期防止缓存雪崩!
int cacheExpireTime = 300 + RandomUtil.randomInt(0, 300);
opsForValue.set(cacheKey, cacheValue, cacheExpireTime, TimeUnit.SECONDS);
// 获取封装类
return ResultUtils.success(pictureVOPage);
}
/**
* 编辑图片给用户使用
*/

View File

@ -14,6 +14,13 @@ spring:
url: jdbc:mysql://localhost:3306/smile-picture
username: root
password: 123456
# Redis 配置
redis:
database: 1
host: 127.0.0.1
port: 6379
password: 123456
timeout: 5000
servlet:
multipart:
max-file-size: 10MB

View File

@ -0,0 +1,48 @@
package edu.whut.smilepicturebackend;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class RedisStringTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testRedisStringOperations() {
// 获取操作对象
ValueOperations<String, String> valueOps = stringRedisTemplate.opsForValue();
// Key Value
String key = "testKey";
String value = "testValue";
// 1. 测试新增或更新操作
valueOps.set(key, value);
String storedValue = valueOps.get(key);
assertEquals(value, storedValue, "存储的值与预期不一致");
// 2. 测试修改操作
String updatedValue = "updatedValue";
valueOps.set(key, updatedValue);
storedValue = valueOps.get(key);
assertEquals(updatedValue, storedValue, "更新后的值与预期不一致");
// 3. 测试查询操作
storedValue = valueOps.get(key);
assertNotNull(storedValue, "查询的值为空");
assertEquals(updatedValue, storedValue, "查询的值与预期不一致");
// // 4. 测试删除操作
// redisTemplate.delete(key);
// storedValue = valueOps.get(key);
// assertNull(storedValue, "删除后的值不为空");
}
}