3.13 分布式缓存
This commit is contained in:
parent
a6f7d83ecb
commit
144a55c1cf
15
pom.xml
15
pom.xml
@ -63,6 +63,21 @@
|
|||||||
<artifactId>jsoup</artifactId>
|
<artifactId>jsoup</artifactId>
|
||||||
<version>1.15.3</version>
|
<version>1.15.3</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.mysql</groupId>
|
<groupId>com.mysql</groupId>
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package edu.whut.smilepicturebackend.controller;
|
package edu.whut.smilepicturebackend.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
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.annotation.AuthCheck;
|
||||||
import edu.whut.smilepicturebackend.common.BaseResponse;
|
import edu.whut.smilepicturebackend.common.BaseResponse;
|
||||||
import edu.whut.smilepicturebackend.common.DeleteRequest;
|
import edu.whut.smilepicturebackend.common.DeleteRequest;
|
||||||
@ -21,12 +24,17 @@ import edu.whut.smilepicturebackend.service.UserService;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
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.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@ -35,6 +43,16 @@ import java.util.List;
|
|||||||
public class PictureController {
|
public class PictureController {
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final PictureService pictureService;
|
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));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑图片(给用户使用)
|
* 编辑图片(给用户使用)
|
||||||
*/
|
*/
|
||||||
|
@ -14,6 +14,13 @@ spring:
|
|||||||
url: jdbc:mysql://localhost:3306/smile-picture
|
url: jdbc:mysql://localhost:3306/smile-picture
|
||||||
username: root
|
username: root
|
||||||
password: 123456
|
password: 123456
|
||||||
|
# Redis 配置
|
||||||
|
redis:
|
||||||
|
database: 1
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
password: 123456
|
||||||
|
timeout: 5000
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
max-file-size: 10MB
|
max-file-size: 10MB
|
||||||
|
@ -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, "删除后的值不为空");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user