diff --git a/han-note-note/han-note-note-biz/pom.xml b/han-note-note/han-note-note-biz/pom.xml index 605257b..8272190 100644 --- a/han-note-note/han-note-note-biz/pom.xml +++ b/han-note-note/han-note-note-biz/pom.xml @@ -112,6 +112,12 @@ caffeine + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DelayDeleteNoteRedisCacheConsumer.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DelayDeleteNoteRedisCacheConsumer.java new file mode 100644 index 0000000..ffb2efb --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DelayDeleteNoteRedisCacheConsumer.java @@ -0,0 +1,31 @@ +package com.hanserwei.hannote.note.biz.comsumer; + +import com.hanserwei.hannote.note.biz.constant.MQConstants; +import com.hanserwei.hannote.note.biz.constant.RedisKeyConstants; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RocketMQMessageListener(consumerGroup = "han_note_group_" + MQConstants.TOPIC_DELAY_DELETE_NOTE_REDIS_CACHE, // Group + topic = MQConstants.TOPIC_DELAY_DELETE_NOTE_REDIS_CACHE // 消费的主题 Topic + ) +public class DelayDeleteNoteRedisCacheConsumer implements RocketMQListener { + + @Resource + private RedisTemplate redisTemplate; + + @Override + public void onMessage(String body) { + Long noteId = Long.valueOf(body); + log.info("## 延迟消息消费成功, noteId: {}", noteId); + + // 删除 Redis 笔记缓存 + String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); + redisTemplate.delete(noteDetailRedisKey); + } +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DeleteNoteLocalCacheConsumer.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DeleteNoteLocalCacheConsumer.java new file mode 100644 index 0000000..2f0a3fd --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/comsumer/DeleteNoteLocalCacheConsumer.java @@ -0,0 +1,23 @@ +package com.hanserwei.hannote.note.biz.comsumer; + +import com.hanserwei.hannote.note.biz.constant.MQConstants; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.MessageModel; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RocketMQMessageListener( + consumerGroup = "han_note_group", + topic = MQConstants.TOPIC_DELETE_NOTE_LOCAL_CACHE, + messageModel = MessageModel.BROADCASTING +) +public class DeleteNoteLocalCacheConsumer implements RocketMQListener { + @Override + public void onMessage(String body) { + Long noteId = Long.valueOf(body); + log.info("## 消费者消费成功, noteId: {}", noteId); + } +} diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/RocketMQConfig.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/RocketMQConfig.java new file mode 100644 index 0000000..f290020 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/RocketMQConfig.java @@ -0,0 +1,10 @@ +package com.hanserwei.hannote.note.biz.config; + +import org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import(RocketMQAutoConfiguration.class) +public class RocketMQConfig { +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/MQConstants.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/MQConstants.java new file mode 100644 index 0000000..dc40c77 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/MQConstants.java @@ -0,0 +1,14 @@ +package com.hanserwei.hannote.note.biz.constant; + +public interface MQConstants { + + /** + * Topic 主题:删除笔记的本地缓存 + */ + String TOPIC_DELETE_NOTE_LOCAL_CACHE = "DeleteNoteLocalCacheTopic"; + + /** + * Topic 主题:延迟双删 Redis 笔记缓存 + */ + String TOPIC_DELAY_DELETE_NOTE_REDIS_CACHE = "DelayDeleteNoteRedisCacheTopic"; +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java index 2bb6ea8..41d97ac 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java @@ -11,6 +11,7 @@ import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder; import com.hanserwei.framework.common.exception.ApiException; import com.hanserwei.framework.common.response.Response; import com.hanserwei.framework.common.utils.JsonUtils; +import com.hanserwei.hannote.note.biz.constant.MQConstants; import com.hanserwei.hannote.note.biz.constant.RedisKeyConstants; import com.hanserwei.hannote.note.biz.domain.dataobject.NoteDO; import com.hanserwei.hannote.note.biz.domain.dataobject.TopicDO; @@ -33,7 +34,12 @@ import jakarta.annotation.Resource; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -60,6 +66,8 @@ public class NoteServiceImpl extends ServiceImpl implement private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Resource private RedisTemplate redisTemplate; + @Resource + private RocketMQTemplate rocketMQTemplate; /** * 笔记详情本地缓存 @@ -344,6 +352,10 @@ public class NoteServiceImpl extends ServiceImpl implement } } + // 删除 Redis 缓存 + String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); + redisTemplate.delete(noteDetailRedisKey); + // 更新笔记元数据表 String content = updateNoteReqVO.getContent(); NoteDO noteDO = NoteDO.builder() @@ -361,13 +373,39 @@ public class NoteServiceImpl extends ServiceImpl implement if (!updateResult){ throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL); } + + // 一致性保证:延迟双删策略 + // 异步发送延时消息 + Message message = MessageBuilder.withPayload(String.valueOf(noteId)) + .build(); + + rocketMQTemplate.asyncSend(MQConstants.TOPIC_DELAY_DELETE_NOTE_REDIS_CACHE, message, + new SendCallback() { + @Override + public void onSuccess(SendResult sendResult) { + log.info("## 延时删除 Redis 笔记缓存消息发送成功..."); + } + + @Override + public void onException(Throwable e) { + log.error("## 延时删除 Redis 笔记缓存消息发送失败...", e); + } + }, + 3000, // 超时时间(毫秒) + 1 // 延迟级别,1 表示延时 1s + ); + // 删除Redis缓存 - String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); + noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); redisTemplate.delete(noteDetailRedisKey); // 删除本地缓存 LOCAL_CACHE.invalidate(noteId); + // 同步发送广播模式 MQ,将所有实例中的本地缓存都删除掉 + rocketMQTemplate.syncSend(MQConstants.TOPIC_DELETE_NOTE_LOCAL_CACHE, noteId); + log.info("====> MQ:删除笔记本地缓存发送成功..."); + // 笔记内容更新 // 查询此篇笔记内容对应的 UUID NoteDO noteDO1 = this.getById(noteId); diff --git a/pom.xml b/pom.xml index 010df35..d0c6ea2 100755 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,8 @@ 0.9.16 5.9.0 3.9.4 + 2.3.4 + 5.3.2 @@ -265,6 +267,22 @@ caffeine ${caffeine.version} + + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-spring-boot.version} + + + org.apache.rocketmq + rocketmq-client + ${rocketmq-client.version} + + + org.apache.rocketmq + rocketmq-acl + ${rocketmq-client.version} +