Commit be3fac75420232887eaad05c66ee028ec8baf52e
1 parent
f5331ce6
1. 简单 redis stream 的 StreamMessage 和对应的消费者
2. 跑通 Redis Stream 的流程
Showing
13 changed files
with
458 additions
and
3 deletions
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
| 1 | 1 | package cn.iocoder.dashboard.framework.redis.config; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.net.NetUtil; | |
| 3 | 4 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 5 | +import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | |
| 4 | 6 | import lombok.extern.slf4j.Slf4j; |
| 5 | 7 | import org.springframework.context.annotation.Bean; |
| 6 | 8 | import org.springframework.context.annotation.Configuration; |
| 7 | 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; |
| 10 | +import org.springframework.data.redis.connection.stream.*; | |
| 8 | 11 | import org.springframework.data.redis.core.RedisTemplate; |
| 9 | 12 | import org.springframework.data.redis.listener.ChannelTopic; |
| 10 | 13 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 11 | 14 | import org.springframework.data.redis.serializer.RedisSerializer; |
| 15 | +import org.springframework.data.redis.stream.StreamMessageListenerContainer; | |
| 16 | +import org.springframework.util.ErrorHandler; | |
| 12 | 17 | |
| 18 | +import java.time.Duration; | |
| 13 | 19 | import java.util.List; |
| 14 | 20 | |
| 15 | 21 | /** |
| ... | ... | @@ -48,4 +54,52 @@ public class RedisConfig { |
| 48 | 54 | return container; |
| 49 | 55 | } |
| 50 | 56 | |
| 57 | + @Bean(initMethod = "start", destroyMethod = "stop") | |
| 58 | + public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( | |
| 59 | + RedisConnectionFactory factory, List<AbstractStreamMessageListener<?>> listeners) { | |
| 60 | + // 创建配置对象 | |
| 61 | + StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> | |
| 62 | + streamMessageListenerContainerOptions = StreamMessageListenerContainer.StreamMessageListenerContainerOptions | |
| 63 | + .builder() | |
| 64 | + // 一次性最多拉取多少条消息 | |
| 65 | + .batchSize(10) | |
| 66 | + // 执行消息轮询的执行器 | |
| 67 | + // .executor(this.threadPoolTaskExecutor) | |
| 68 | + // 消息消费异常的handler | |
| 69 | + .errorHandler(new ErrorHandler() { | |
| 70 | + @Override | |
| 71 | + public void handleError(Throwable t) { | |
| 72 | + // throw new RuntimeException(t); | |
| 73 | + t.printStackTrace(); | |
| 74 | + } | |
| 75 | + }) | |
| 76 | + // 超时时间,设置为0,表示不超时(超时后会抛出异常) | |
| 77 | + .pollTimeout(Duration.ZERO) | |
| 78 | + // 序列化器 | |
| 79 | + .serializer(RedisSerializer.string()) | |
| 80 | + .targetType(String.class) | |
| 81 | + .build(); | |
| 82 | + | |
| 83 | + // 根据配置对象创建监听容器对象 | |
| 84 | + StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = StreamMessageListenerContainer | |
| 85 | + .create(factory, streamMessageListenerContainerOptions); | |
| 86 | + | |
| 87 | + RedisTemplate<String, Object> redisTemplate = redisTemplate(factory); | |
| 88 | + | |
| 89 | + // 使用监听容器对象开始监听消费(使用的是手动确认方式) | |
| 90 | + String consumerName = NetUtil.getLocalHostName(); // TODO 需要优化下,晚点参考下 rocketmq consumer 的 | |
| 91 | + for (AbstractStreamMessageListener<?> listener : listeners) { | |
| 92 | + try { | |
| 93 | + redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); | |
| 94 | + } catch (Exception ignore) { | |
| 95 | +// ignore.printStackTrace(); | |
| 96 | + } | |
| 97 | + | |
| 98 | + container.receive(Consumer.from(listener.getGroup(), consumerName), | |
| 99 | + StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed()), listener); | |
| 100 | + } | |
| 101 | + | |
| 102 | + return container; | |
| 103 | + } | |
| 104 | + | |
| 51 | 105 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/pubsub/ChannelMessage.java
| ... | ... | @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * Redis Channel Message 接口 |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 7 | 9 | */ |
| 8 | 10 | public interface ChannelMessage { |
| 9 | 11 | |
| ... | ... | @@ -12,7 +14,7 @@ public interface ChannelMessage { |
| 12 | 14 | * |
| 13 | 15 | * @return Channel |
| 14 | 16 | */ |
| 15 | - @JsonIgnore // 必须序列化 | |
| 17 | + @JsonIgnore // 避免序列化 | |
| 16 | 18 | String getChannel(); |
| 17 | 19 | |
| 18 | 20 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/stream/AbstractStreamMessageListener.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.stream; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ArrayUtil; | |
| 4 | +import lombok.Getter; | |
| 5 | +import lombok.SneakyThrows; | |
| 6 | +import org.springframework.beans.factory.annotation.Value; | |
| 7 | +import org.springframework.data.redis.connection.stream.ObjectRecord; | |
| 8 | +import org.springframework.data.redis.stream.StreamListener; | |
| 9 | +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; | |
| 10 | + | |
| 11 | +import java.lang.reflect.Type; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * Redis Stream 监听器抽象类,用于实现集群消费 | |
| 15 | + * | |
| 16 | + * @param <T> 消息类型。一定要填写噢,不然会报错 | |
| 17 | + * | |
| 18 | + * @author 芋道源码 | |
| 19 | + */ | |
| 20 | +public abstract class AbstractStreamMessageListener<T extends StreamMessage> | |
| 21 | + implements StreamListener<String, ObjectRecord<String, String>> { | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 消息类型 | |
| 25 | + */ | |
| 26 | + private final Class<T> messageType; | |
| 27 | + /** | |
| 28 | + * Redis Channel | |
| 29 | + */ | |
| 30 | + @Getter | |
| 31 | + private final String streamKey; | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * Redis 消费者分组,默认使用 spring.application.name 名字 | |
| 35 | + */ | |
| 36 | + @Value("spring.application.name") | |
| 37 | + @Getter | |
| 38 | + private String group; | |
| 39 | + | |
| 40 | + @SneakyThrows | |
| 41 | + protected AbstractStreamMessageListener() { | |
| 42 | + this.messageType = getMessageClass(); | |
| 43 | + this.streamKey = messageType.newInstance().getStreamKey(); | |
| 44 | + } | |
| 45 | + | |
| 46 | + @Override | |
| 47 | + public void onMessage(ObjectRecord<String, String> message) { | |
| 48 | + System.out.println(message); | |
| 49 | + } | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 处理消息 | |
| 53 | + * | |
| 54 | + * @param message 消息 | |
| 55 | + */ | |
| 56 | + public abstract void onMessage(T message); | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * 通过解析类上的泛型,获得消息类型 | |
| 60 | + * | |
| 61 | + * @return 消息类型 | |
| 62 | + */ | |
| 63 | + @SuppressWarnings("unchecked") | |
| 64 | + // TODO 芋艿:稍后重构 | |
| 65 | + private Class<T> getMessageClass() { | |
| 66 | + Class<?> targetClass = getClass(); | |
| 67 | + while (targetClass.getSuperclass() != null) { | |
| 68 | + // 如果不是 AbstractMessageListener 父类,继续向上查找 | |
| 69 | + if (targetClass.getSuperclass() != AbstractStreamMessageListener.class) { | |
| 70 | + targetClass = targetClass.getSuperclass(); | |
| 71 | + continue; | |
| 72 | + } | |
| 73 | + // 如果是 AbstractMessageListener 父类,则解析泛型 | |
| 74 | + Type[] types = ((ParameterizedTypeImpl) targetClass.getGenericSuperclass()).getActualTypeArguments(); | |
| 75 | + if (ArrayUtil.isEmpty(types)) { | |
| 76 | + throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName())); | |
| 77 | + } | |
| 78 | + return (Class<T>) types[0]; | |
| 79 | + } | |
| 80 | + throw new IllegalStateException(String.format("类型(%s) 找不到 AbstractStreamMessageListener 父类", getClass().getName())); | |
| 81 | + } | |
| 82 | + | |
| 83 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/stream/StreamMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.stream; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Redis Stream Message 接口 | |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 9 | + */ | |
| 10 | +public interface StreamMessage { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 获得 Redis Stream Key | |
| 14 | + * | |
| 15 | + * @return Channel | |
| 16 | + */ | |
| 17 | + @JsonIgnore // 避免序列化 | |
| 18 | + String getStreamKey(); | |
| 19 | + | |
| 20 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/util/RedisMessageUtils.java
| 1 | 1 | package cn.iocoder.dashboard.framework.redis.core.util; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; |
| 4 | +import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; | |
| 4 | 5 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 6 | +import org.springframework.data.redis.connection.stream.RecordId; | |
| 7 | +import org.springframework.data.redis.connection.stream.StreamRecords; | |
| 5 | 8 | import org.springframework.data.redis.core.RedisTemplate; |
| 6 | 9 | |
| 7 | 10 | /** |
| ... | ... | @@ -17,8 +20,21 @@ public class RedisMessageUtils { |
| 17 | 20 | * @param redisTemplate Redis 操作模板 |
| 18 | 21 | * @param message 消息 |
| 19 | 22 | */ |
| 20 | - public static <T extends ChannelMessage> void sendChannelMessage(RedisTemplate<?, ?> redisTemplate, T message) { | |
| 23 | + public static <T extends ChannelMessage> void sendChannelMessage(RedisTemplate<?, ?> redisTemplate, T message) { | |
| 21 | 24 | redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message)); |
| 22 | 25 | } |
| 23 | 26 | |
| 27 | + /** | |
| 28 | + * 发送 Redis 消息,基于 Redis Stream 实现 | |
| 29 | + * | |
| 30 | + * @param redisTemplate Redis 操作模板 | |
| 31 | + * @param message 消息 | |
| 32 | + * @return 消息记录的编号对象 | |
| 33 | + */ | |
| 34 | + public static <T extends StreamMessage> RecordId sendStreamMessage(RedisTemplate<String, String> redisTemplate, T message) { | |
| 35 | + return redisTemplate.opsForStream().add(StreamRecords.newRecord() | |
| 36 | + .ofObject(JsonUtils.toJsonString(message)) // 设置内容 | |
| 37 | + .withStreamKey(message.getStreamKey())); // 设置 stream key | |
| 38 | + } | |
| 39 | + | |
| 24 | 40 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/mail/SysMailSendConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.mail; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | |
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.mail.SysMailSendMessage; | |
| 5 | +import org.springframework.stereotype.Component; | |
| 6 | + | |
| 7 | +@Component | |
| 8 | +public class SysMailSendConsumer extends AbstractStreamMessageListener<SysMailSendMessage> { | |
| 9 | + | |
| 10 | + @Override | |
| 11 | + public void onMessage(SysMailSendMessage message) { | |
| 12 | + | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsSendConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | |
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | |
| 5 | +import org.springframework.stereotype.Component; | |
| 6 | + | |
| 7 | +@Component | |
| 8 | +public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { | |
| 9 | + | |
| 10 | + @Override | |
| 11 | + public void onMessage(SysSmsSendMessage message) { | |
| 12 | + System.out.println(message); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/mail/SysMailSendMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.mail; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 邮箱发送消息 | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +public class SysMailSendMessage implements StreamMessage { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 邮箱地址 | |
| 19 | + */ | |
| 20 | + @NotNull(message = "邮箱地址不能为空") | |
| 21 | + private String address; | |
| 22 | + /** | |
| 23 | + * 短信模板编号 | |
| 24 | + */ | |
| 25 | + @NotNull(message = "短信模板编号不能为空") | |
| 26 | + private String templateCode; | |
| 27 | + /** | |
| 28 | + * 短信模板参数 | |
| 29 | + */ | |
| 30 | + private Map<String, Object> templateParams; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 用户编号,允许空 | |
| 34 | + */ | |
| 35 | + private Integer userId; | |
| 36 | + /** | |
| 37 | + * 用户类型,允许空 | |
| 38 | + */ | |
| 39 | + private Integer userType; | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + public String getStreamKey() { | |
| 43 | + return "system.mail.send"; | |
| 44 | + } | |
| 45 | + | |
| 46 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 短信发送消息 | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +public class SysSmsSendMessage implements StreamMessage { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 手机号 | |
| 19 | + */ | |
| 20 | + @NotNull(message = "手机号不能为空") | |
| 21 | + private String mobile; | |
| 22 | + /** | |
| 23 | + * 短信模板编号 | |
| 24 | + */ | |
| 25 | + @NotNull(message = "短信模板编号不能为空") | |
| 26 | + private String templateCode; | |
| 27 | + /** | |
| 28 | + * 短信模板参数 | |
| 29 | + */ | |
| 30 | + private Map<String, Object> templateParams; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 用户编号,允许空 | |
| 34 | + */ | |
| 35 | + private Integer userId; | |
| 36 | + /** | |
| 37 | + * 用户类型,允许空 | |
| 38 | + */ | |
| 39 | + private Integer userType; | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + public String getStreamKey() { | |
| 43 | + return "system.sms.send"; | |
| 44 | + } | |
| 45 | + | |
| 46 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/BaseRedisIntegrationTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.config.RedisConfig; | |
| 4 | +import org.redisson.spring.starter.RedissonAutoConfiguration; | |
| 5 | +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; | |
| 6 | +import org.springframework.boot.test.context.SpringBootTest; | |
| 7 | +import org.springframework.context.annotation.Import; | |
| 8 | +import org.springframework.test.context.ActiveProfiles; | |
| 9 | + | |
| 10 | +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisIntegrationTest.Application.class) | |
| 11 | +@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件 | |
| 12 | +public class BaseRedisIntegrationTest { | |
| 13 | + | |
| 14 | + @Import({ | |
| 15 | + // Redis 配置类 | |
| 16 | + RedisAutoConfiguration.class, // Spring Redis 自动配置类 | |
| 17 | + RedisConfig.class, // 自己的 Redis 配置类 | |
| 18 | + RedissonAutoConfiguration.class, // Redisson 自动高配置类 | |
| 19 | + }) | |
| 20 | + public static class Application { | |
| 21 | + } | |
| 22 | + | |
| 23 | +} | ... | ... |
src/test-integration/java/cn/iocoder/dashboard/framework/redis/core/stream/RedisStreamTest.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.stream; | |
| 2 | + | |
| 3 | +import cn.hutool.core.thread.ThreadUtil; | |
| 4 | +import cn.iocoder.dashboard.BaseRedisIntegrationTest; | |
| 5 | +import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; | |
| 6 | +import cn.iocoder.dashboard.modules.system.mq.consumer.mail.SysMailSendConsumer; | |
| 7 | +import cn.iocoder.dashboard.modules.system.mq.consumer.sms.SysSmsSendConsumer; | |
| 8 | +import cn.iocoder.dashboard.modules.system.mq.message.mail.SysMailSendMessage; | |
| 9 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | |
| 10 | +import org.junit.jupiter.api.Test; | |
| 11 | +import org.springframework.context.annotation.Import; | |
| 12 | +import org.springframework.data.redis.core.StringRedisTemplate; | |
| 13 | + | |
| 14 | +import javax.annotation.Resource; | |
| 15 | +import java.util.concurrent.TimeUnit; | |
| 16 | + | |
| 17 | +public class RedisStreamTest { | |
| 18 | + | |
| 19 | + @Import({SysSmsSendConsumer.class, SysMailSendConsumer.class}) | |
| 20 | + public static class ConsumerTest extends BaseRedisIntegrationTest { | |
| 21 | + | |
| 22 | + @Test | |
| 23 | + public void testConsumer() { | |
| 24 | + ThreadUtil.sleep(1, TimeUnit.DAYS); | |
| 25 | + } | |
| 26 | + | |
| 27 | + } | |
| 28 | + | |
| 29 | + public static class ProducerTest extends BaseRedisIntegrationTest { | |
| 30 | + | |
| 31 | + @Resource | |
| 32 | + private StringRedisTemplate stringRedisTemplate; | |
| 33 | + | |
| 34 | + @Test | |
| 35 | + public void testProducer01() { | |
| 36 | + // 创建消息 | |
| 37 | + SysSmsSendMessage message = new SysSmsSendMessage(); | |
| 38 | + message.setMobile("15601691300").setTemplateCode("test"); | |
| 39 | + // 发送消息 | |
| 40 | + RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Test | |
| 44 | + public void testProducer02() { | |
| 45 | + // 创建消息 | |
| 46 | + SysMailSendMessage message = new SysMailSendMessage(); | |
| 47 | + message.setAddress("fangfang@mihayou.com").setTemplateCode("test"); | |
| 48 | + // 发送消息 | |
| 49 | + RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message); | |
| 50 | + } | |
| 51 | + | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | ... | ... |
src/test-integration/resources/application-integration-test.yaml
0 → 100644
| 1 | +spring: | |
| 2 | + main: | |
| 3 | + lazy-initialization: true # 开启懒加载,加快速度 | |
| 4 | + banner-mode: off # 单元测试,禁用 Banner | |
| 5 | + | |
| 6 | +--- #################### 数据库相关配置 #################### | |
| 7 | + | |
| 8 | +spring: | |
| 9 | + # 数据源配置项 | |
| 10 | + datasource: | |
| 11 | + name: ruoyi-vue-pro | |
| 12 | + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 | |
| 13 | + driver-class-name: org.h2.Driver | |
| 14 | + username: sa | |
| 15 | + password: | |
| 16 | + schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具 | |
| 17 | + druid: | |
| 18 | + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 | |
| 19 | + initial-size: 1 # 单元测试,配置为 1,提升启动速度 | |
| 20 | + | |
| 21 | + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 | |
| 22 | + redis: | |
| 23 | + host: 127.0.0.1 # 地址 | |
| 24 | + port: 6379 # 端口(单元测试,使用 16379 端口) | |
| 25 | + database: 0 # 数据库索引 | |
| 26 | + | |
| 27 | +mybatis: | |
| 28 | + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 | |
| 29 | + | |
| 30 | +--- #################### 定时任务相关配置 #################### | |
| 31 | + | |
| 32 | +--- #################### 配置中心相关配置 #################### | |
| 33 | + | |
| 34 | +--- #################### 服务保障相关配置 #################### | |
| 35 | + | |
| 36 | +# Lock4j 配置项(单元测试,禁用 Lock4j) | |
| 37 | + | |
| 38 | +# Resilience4j 配置项 | |
| 39 | +resilience4j: | |
| 40 | + ratelimiter: | |
| 41 | + instances: | |
| 42 | + backendA: | |
| 43 | + limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50 | |
| 44 | + limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500 | |
| 45 | + timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s | |
| 46 | + register-health-indicator: true # 是否注册到健康监测 | |
| 47 | + | |
| 48 | +--- #################### 监控相关配置 #################### | |
| 49 | + | |
| 50 | +--- #################### 芋道相关配置 #################### | |
| 51 | + | |
| 52 | +# 芋道配置项,设置当前项目所有自定义的配置 | |
| 53 | +yudao: | |
| 54 | + info: | |
| 55 | + version: 1.0.0 | |
| 56 | + base-package: cn.iocoder.dashboard | |
| 57 | + web: | |
| 58 | + api-prefix: /api | |
| 59 | + controller-package: ${yudao.info.base-package} | |
| 60 | + security: | |
| 61 | + token-header: Authorization | |
| 62 | + token-secret: abcdefghijklmnopqrstuvwxyz | |
| 63 | + token-timeout: 1d | |
| 64 | + session-timeout: 30m | |
| 65 | + mock-enable: true | |
| 66 | + mock-secret: test | |
| 67 | + swagger: | |
| 68 | + enable: false # 单元测试,禁用 Swagger | |
| 69 | + captcha: | |
| 70 | + timeout: 5m | |
| 71 | + width: 160 | |
| 72 | + height: 60 | |
| 73 | + file: | |
| 74 | + base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/ | |
| 75 | + codegen: | |
| 76 | + base-package: ${yudao.info.base-package}.modules | |
| 77 | + db-schemas: ${spring.datasource.name} | |
| 78 | + xss: | |
| 79 | + enable: false | |
| 80 | + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 | |
| 81 | + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 | |
| 82 | + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 | ... | ... |
src/test/java/cn/iocoder/dashboard/modules/system/service/dept/SysDeptServiceTest.java
| ... | ... | @@ -72,7 +72,6 @@ class SysDeptServiceTest extends BaseDbUnitTest { |
| 72 | 72 | // 断言 maxUpdateTime 缓存 |
| 73 | 73 | Date maxUpdateTime = (Date) getFieldValue(deptService, "maxUpdateTime"); |
| 74 | 74 | assertEquals(ObjectUtils.max(deptDO1.getUpdateTime(), deptDO2.getUpdateTime()), maxUpdateTime); |
| 75 | - | |
| 76 | 75 | } |
| 77 | 76 | |
| 78 | 77 | @Test | ... | ... |