From ca886c2791b76bea1ed9fa1e7d88205edfe74e87 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 29 Jul 2023 09:10:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=85=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20Job=20=E8=BD=AE=E8=AF=A2=EF=BC=8C=E6=9B=BF=E4=BB=A3=20MQ=20?= =?UTF-8?q?=E5=B9=BF=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/mysql/file/FileConfigMapper.java | 6 ++ .../file/FileConfigRefreshConsumer.java | 29 -------- .../infra/mq/consumer/package-info.java | 4 + .../file/FileConfigRefreshMessage.java | 19 ----- .../module/infra/mq/message/package-info.java | 4 + .../mq/producer/file/FileConfigProducer.java | 20 ----- .../infra/mq/producer/package-info.java | 4 + .../infra/service/file/FileConfigService.java | 5 -- .../service/file/FileConfigServiceImpl.java | 67 +++++++++++------ .../file/FileConfigServiceImplTest.java | 73 +++++++++---------- 10 files changed, 95 insertions(+), 136 deletions(-) delete mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java delete mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java delete mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java index a2bf16be4..3bec97e0b 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; @Mapper public interface FileConfigMapper extends BaseMapperX { @@ -18,4 +21,7 @@ public interface FileConfigMapper extends BaseMapperX { .orderByDesc(FileConfigDO::getId)); } + @Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java deleted file mode 100644 index 415373064..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.consumer.file; - -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage; -import cn.iocoder.yudao.module.infra.service.file.FileConfigService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link FileConfigRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class FileConfigRefreshConsumer { - - @Resource - private FileConfigService fileConfigService; - - @EventListener - public void execute(FileConfigRefreshMessage message) { - log.info("[execute][收到 FileConfig 刷新消息]"); - fileConfigService.initLocalCache(); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java new file mode 100644 index 000000000..5ba526413 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java @@ -0,0 +1,4 @@ +/** + * 消息队列的消费者 + */ +package cn.iocoder.yudao.module.infra.mq.consumer; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java deleted file mode 100644 index db45856a3..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/file/FileConfigRefreshMessage.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.message.file; - -import lombok.Data; -import org.springframework.cloud.bus.event.RemoteApplicationEvent; - -/** - * 文件配置数据刷新 Message - */ -@Data -public class FileConfigRefreshMessage extends RemoteApplicationEvent { - - public FileConfigRefreshMessage() { - } - - public FileConfigRefreshMessage(Object source, String originService, String destinationService) { - super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java new file mode 100644 index 000000000..19edb0241 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java @@ -0,0 +1,4 @@ +/** + * 消息队列的消息 + */ +package cn.iocoder.yudao.module.infra.mq.message; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java deleted file mode 100644 index b06ab2d5c..000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/file/FileConfigProducer.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.infra.mq.producer.file; - -import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; -import cn.iocoder.yudao.module.infra.mq.message.file.FileConfigRefreshMessage; -import org.springframework.stereotype.Component; - -/** - * 文件配置相关消息的 Producer - */ -@Component -public class FileConfigProducer extends AbstractBusProducer { - - /** - * 发送 {@link FileConfigRefreshMessage} 消息 - */ - public void sendFileConfigRefreshMessage() { - publishEvent(new FileConfigRefreshMessage(this, selfDestinationService(), selfDestinationService())); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java new file mode 100644 index 000000000..343179cb8 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java @@ -0,0 +1,4 @@ +/** + * 消息队列的生产者 + */ +package cn.iocoder.yudao.module.infra.mq.producer; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java index 43ab5bc68..1b279391a 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java @@ -16,11 +16,6 @@ import javax.validation.Valid; */ public interface FileConfigService { - /** - * 初始化文件客户端 - */ - void initLocalCache(); - /** * 创建文件配置 * diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index 6236df6ff..7b5171a83 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.infra.service.file; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.file.core.client.FileClient; @@ -15,20 +17,20 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigU import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Validator; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER; @@ -46,6 +48,12 @@ public class FileConfigServiceImpl implements FileConfigService { @Resource private FileClientFactory fileClientFactory; + + /** + * 文件配置的缓存 + */ + @Getter + private List fileConfigCache; /** * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的 */ @@ -55,13 +63,9 @@ public class FileConfigServiceImpl implements FileConfigService { @Resource private FileConfigMapper fileConfigMapper; - @Resource - private FileConfigProducer fileConfigProducer; - @Resource private Validator validator; - @Override @PostConstruct public void initLocalCache() { // 第一步:查询数据 @@ -76,6 +80,27 @@ public class FileConfigServiceImpl implements FileConfigService { masterFileClient = fileClientFactory.getFileClient(config.getId()); } }); + this.fileConfigCache = configs; + } + + /** + * 通过定时任务轮询,刷新缓存 + * + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 + */ + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) + public void refreshLocalCache() { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(fileConfigCache)) { + initLocalCache(); + return; + } + + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = CollectionUtils.getMaxValue(fileConfigCache, FileConfigDO::getUpdateTime); + if (fileConfigMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } } @Override @@ -85,9 +110,9 @@ public class FileConfigServiceImpl implements FileConfigService { .setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig())) .setMaster(false); // 默认非 master fileConfigMapper.insert(fileConfig); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); - // 返回 + + // 刷新缓存 + initLocalCache(); return fileConfig.getId(); } @@ -99,8 +124,9 @@ public class FileConfigServiceImpl implements FileConfigService { FileConfigDO updateObj = FileConfigConvert.INSTANCE.convert(updateReqVO) .setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig())); fileConfigMapper.updateById(updateObj); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } @Override @@ -112,15 +138,9 @@ public class FileConfigServiceImpl implements FileConfigService { fileConfigMapper.updateBatch(new FileConfigDO().setMaster(false)); // 更新 fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true)); - // 发送刷新配置的消息 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCommit() { - fileConfigProducer.sendFileConfigRefreshMessage(); - } - - }); + // 刷新缓存 + initLocalCache(); } private FileClientConfig parseClientConfig(Integer storage, Map config) { @@ -139,12 +159,13 @@ public class FileConfigServiceImpl implements FileConfigService { // 校验存在 FileConfigDO config = validateFileConfigExists(id); if (Boolean.TRUE.equals(config.getMaster())) { - throw exception(FILE_CONFIG_DELETE_FAIL_MASTER); + throw exception(FILE_CONFIG_DELETE_FAIL_MASTER); } // 删除 fileConfigMapper.deleteById(id); - // 发送刷新配置的消息 - fileConfigProducer.sendFileConfigRefreshMessage(); + + // 刷新缓存 + initLocalCache(); } private FileConfigDO validateFileConfigExists(Long id) { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index 656955194..ae42cda38 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigP import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; -import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; import lombok.Data; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; @@ -42,10 +41,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; /** -* {@link FileConfigServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link FileConfigServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(FileConfigServiceImpl.class) public class FileConfigServiceImplTest extends BaseDbUnitTest { @@ -55,8 +54,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @Resource private FileConfigMapper fileConfigMapper; - @MockBean - private FileConfigProducer fileConfigProducer; @MockBean private Validator validator; @MockBean @@ -81,6 +78,10 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { verify(fileClientFactory).createOrUpdateFileClient(eq(2L), eq(configDO2.getStorage()), eq(configDO2.getConfig())); assertSame(masterFileClient, fileConfigService.getMasterFileClient()); + // 断言 fileConfigCache 缓存 + assertEquals(2, fileConfigService.getFileConfigCache().size()); + assertEquals(configDO1, fileConfigService.getFileConfigCache().get(0)); + assertEquals(configDO2, fileConfigService.getFileConfigCache().get(1)); } @Test @@ -101,8 +102,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertFalse(fileConfig.getMaster()); assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -126,8 +125,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, fileConfig, "config"); assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -152,8 +149,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { // 断言数据 assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster()); assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster()); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -172,10 +167,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { // 调用 fileConfigService.deleteFileConfig(id); - // 校验数据不存在了 - assertNull(fileConfigMapper.selectById(id)); - // verify 调用 - verify(fileConfigProducer).sendFileConfigRefreshMessage(); + // 校验数据不存在了 + assertNull(fileConfigMapper.selectById(id)); } @Test @@ -201,30 +194,30 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @Test public void testGetFileConfigPage() { - // mock 数据 - FileConfigDO dbFileConfig = randomFileConfigDO().setName("芋道源码") - .setStorage(FileStorageEnum.LOCAL.getStorage()); - dbFileConfig.setCreateTime(LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN));// 等会查询到 - fileConfigMapper.insert(dbFileConfig); - // 测试 name 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName("源码"))); - // 测试 storage 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(FileStorageEnum.DB.getStorage()))); - // 测试 createTime 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(LocalDateTimeUtil.parse("2020-11-23", DatePattern.NORM_DATE_PATTERN)))); - // 准备参数 - FileConfigPageReqVO reqVO = new FileConfigPageReqVO(); - reqVO.setName("芋道"); - reqVO.setStorage(FileStorageEnum.LOCAL.getStorage()); - reqVO.setCreateTime((new LocalDateTime[]{buildTime(2020, 1, 1), - buildTime(2020, 1, 24)})); + // mock 数据 + FileConfigDO dbFileConfig = randomFileConfigDO().setName("芋道源码") + .setStorage(FileStorageEnum.LOCAL.getStorage()); + dbFileConfig.setCreateTime(LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN));// 等会查询到 + fileConfigMapper.insert(dbFileConfig); + // 测试 name 不匹配 + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName("源码"))); + // 测试 storage 不匹配 + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(FileStorageEnum.DB.getStorage()))); + // 测试 createTime 不匹配 + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(LocalDateTimeUtil.parse("2020-11-23", DatePattern.NORM_DATE_PATTERN)))); + // 准备参数 + FileConfigPageReqVO reqVO = new FileConfigPageReqVO(); + reqVO.setName("芋道"); + reqVO.setStorage(FileStorageEnum.LOCAL.getStorage()); + reqVO.setCreateTime((new LocalDateTime[]{buildTime(2020, 1, 1), + buildTime(2020, 1, 24)})); - // 调用 - PageResult pageResult = fileConfigService.getFileConfigPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbFileConfig, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = fileConfigService.getFileConfigPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbFileConfig, pageResult.getList().get(0)); } @Test