feat(note):优化笔记详情查询性能
- 引入 CompletableFuture 实现 RPC 调用异步化 - 并行调用用户服务与内容服务提升响应速度 - 使用 allOf 统一处理多个异步任务结果 - 保留原有缓存逻辑及异常处理机制 - 调整代码结构提高可读性和维护性
This commit is contained in:
@@ -39,6 +39,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -167,6 +168,7 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public Response<FindNoteDetailRspVO> findNoteDetail(FindNoteDetailReqVO findNoteDetailReqVO) {
|
public Response<FindNoteDetailRspVO> findNoteDetail(FindNoteDetailReqVO findNoteDetailReqVO) {
|
||||||
// 查询笔记ID
|
// 查询笔记ID
|
||||||
Long noteId = findNoteDetailReqVO.getId();
|
Long noteId = findNoteDetailReqVO.getId();
|
||||||
@@ -208,7 +210,7 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
|
|||||||
.eq(NoteDO::getStatus, 1));
|
.eq(NoteDO::getStatus, 1));
|
||||||
|
|
||||||
// 若笔记不存在,则抛异常
|
// 若笔记不存在,则抛异常
|
||||||
if (Objects.isNull(noteDO)){
|
if (Objects.isNull(noteDO)) {
|
||||||
threadPoolTaskExecutor.execute(() -> {
|
threadPoolTaskExecutor.execute(() -> {
|
||||||
// 防止缓存穿透,将空数据存入 Redis 缓存 (过期时间不宜设置过长)
|
// 防止缓存穿透,将空数据存入 Redis 缓存 (过期时间不宜设置过长)
|
||||||
// 保底1分钟 + 随机秒数
|
// 保底1分钟 + 随机秒数
|
||||||
@@ -224,47 +226,61 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
|
|||||||
|
|
||||||
// RPC调用用户服务,获取用户信息
|
// RPC调用用户服务,获取用户信息
|
||||||
Long creatorId = noteDO.getCreatorId();
|
Long creatorId = noteDO.getCreatorId();
|
||||||
FindUserByIdRspDTO findUserByIdRspDTO = userRpcService.findById(creatorId);
|
CompletableFuture<FindUserByIdRspDTO> userResultFuture = CompletableFuture
|
||||||
|
.supplyAsync(() -> userRpcService.findById(creatorId), threadPoolTaskExecutor);
|
||||||
|
|
||||||
// RPC: 调用 K-V 存储服务获取内容
|
// RPC: 调用 K-V 存储服务获取内容
|
||||||
String content = null;
|
CompletableFuture<String> contentResultFuture = CompletableFuture.completedFuture(null);
|
||||||
if (Objects.equals(noteDO.getIsContentEmpty(), Boolean.FALSE)) {
|
if (Objects.equals(noteDO.getIsContentEmpty(), Boolean.FALSE)) {
|
||||||
content = keyValueRpcService.findNoteContent(noteDO.getContentUuid());
|
contentResultFuture = CompletableFuture
|
||||||
|
.supplyAsync(() -> keyValueRpcService.findNoteContent(noteDO.getContentUuid()), threadPoolTaskExecutor);
|
||||||
}
|
}
|
||||||
|
CompletableFuture<String> finalContentResultFuture = contentResultFuture;
|
||||||
|
CompletableFuture<FindNoteDetailRspVO> resultFuture = CompletableFuture
|
||||||
|
.allOf(userResultFuture, contentResultFuture)
|
||||||
|
.thenApply(s -> {
|
||||||
|
// 获取 Future 返回的结果
|
||||||
|
FindUserByIdRspDTO findUserByIdRspDTO = userResultFuture.join();
|
||||||
|
String content = finalContentResultFuture.join();
|
||||||
|
|
||||||
// 笔记类型
|
// 笔记类型
|
||||||
Integer noteType = noteDO.getType();
|
Integer noteType = noteDO.getType();
|
||||||
// 图文笔记图片链接(字符串)
|
// 图文笔记图片链接(字符串)
|
||||||
String imgUrisStr = noteDO.getImgUris();
|
String imgUrisStr = noteDO.getImgUris();
|
||||||
// 图文笔记图片链接(集合)
|
// 图文笔记图片链接(集合)
|
||||||
List<String> imgUris = null;
|
List<String> imgUris = null;
|
||||||
// 如果查询的是图文笔记,需要将图片链接的逗号分隔开,转换成集合
|
// 如果查询的是图文笔记,需要将图片链接的逗号分隔开,转换成集合
|
||||||
if (Objects.equals(noteType, NoteTypeEnum.IMAGE_TEXT.getCode())
|
if (Objects.equals(noteType, NoteTypeEnum.IMAGE_TEXT.getCode())
|
||||||
&& StringUtils.isNotBlank(imgUrisStr)) {
|
&& StringUtils.isNotBlank(imgUrisStr)) {
|
||||||
imgUris = List.of(imgUrisStr.split(","));
|
imgUris = List.of(imgUrisStr.split(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建返参 VO 实体类
|
// 构建返参 VO 实体类
|
||||||
FindNoteDetailRspVO findNoteDetailRspVO = FindNoteDetailRspVO.builder()
|
return FindNoteDetailRspVO.builder()
|
||||||
.id(noteDO.getId())
|
.id(noteDO.getId())
|
||||||
.type(noteDO.getType())
|
.type(noteDO.getType())
|
||||||
.title(noteDO.getTitle())
|
.title(noteDO.getTitle())
|
||||||
.content(content)
|
.content(content)
|
||||||
.imgUris(imgUris)
|
.imgUris(imgUris)
|
||||||
.topicId(noteDO.getTopicId())
|
.topicId(noteDO.getTopicId())
|
||||||
.topicName(noteDO.getTopicName())
|
.topicName(noteDO.getTopicName())
|
||||||
.creatorId(noteDO.getCreatorId())
|
.creatorId(noteDO.getCreatorId())
|
||||||
.creatorName(findUserByIdRspDTO.getNickName())
|
.creatorName(findUserByIdRspDTO.getNickName())
|
||||||
.avatar(findUserByIdRspDTO.getAvatar())
|
.avatar(findUserByIdRspDTO.getAvatar())
|
||||||
.videoUri(noteDO.getVideoUri())
|
.videoUri(noteDO.getVideoUri())
|
||||||
.updateTime(noteDO.getUpdateTime())
|
.updateTime(noteDO.getUpdateTime())
|
||||||
.visible(noteDO.getVisible())
|
.visible(noteDO.getVisible())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取拼装后的 FindNoteDetailRspVO
|
||||||
|
FindNoteDetailRspVO findNoteDetailRspVO = resultFuture.get();
|
||||||
// 异步线程中将笔记详情存入 Redis
|
// 异步线程中将笔记详情存入 Redis
|
||||||
threadPoolTaskExecutor.submit(() -> {
|
threadPoolTaskExecutor.submit(() -> {
|
||||||
String noteDetailJson1 = JsonUtils.toJsonString(findNoteDetailRspVO);
|
String noteDetailJson1 = JsonUtils.toJsonString(findNoteDetailRspVO);
|
||||||
// 过期时间(保底1天 + 随机秒数,将缓存过期时间打散,防止同一时间大量缓存失效,导致数据库压力太大)
|
// 过期时间(保底1天 + 随机秒数,将缓存过期时间打散,防止同一时间大量缓存失效,导致数据库压力太大)
|
||||||
long expireSeconds = 60*60*24 + RandomUtil.randomInt(60*60*24);
|
long expireSeconds = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
|
||||||
redisTemplate.opsForValue().set(noteDetailRedisKey, noteDetailJson1, expireSeconds, TimeUnit.SECONDS);
|
redisTemplate.opsForValue().set(noteDetailRedisKey, noteDetailJson1, expireSeconds, TimeUnit.SECONDS);
|
||||||
});
|
});
|
||||||
return Response.success(findNoteDetailRspVO);
|
return Response.success(findNoteDetailRspVO);
|
||||||
@@ -272,8 +288,9 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验笔记的可见性
|
* 校验笔记的可见性
|
||||||
* @param visible 是否可见
|
*
|
||||||
* @param userId 当前用户 ID
|
* @param visible 是否可见
|
||||||
|
* @param userId 当前用户 ID
|
||||||
* @param creatorId 笔记创建者
|
* @param creatorId 笔记创建者
|
||||||
*/
|
*/
|
||||||
private void checkNoteVisible(Integer visible, Long userId, Long creatorId) {
|
private void checkNoteVisible(Integer visible, Long userId, Long creatorId) {
|
||||||
@@ -285,7 +302,8 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验笔记的可见性(针对 VO 实体类)
|
* 校验笔记的可见性(针对 VO 实体类)
|
||||||
* @param userId 当前用户 ID
|
*
|
||||||
|
* @param userId 当前用户 ID
|
||||||
* @param findNoteDetailRspVO 笔记详情VO类
|
* @param findNoteDetailRspVO 笔记详情VO类
|
||||||
*/
|
*/
|
||||||
private void checkNoteVisibleFromVO(Long userId, FindNoteDetailRspVO findNoteDetailRspVO) {
|
private void checkNoteVisibleFromVO(Long userId, FindNoteDetailRspVO findNoteDetailRspVO) {
|
||||||
|
|||||||
Reference in New Issue
Block a user