配置参考
soda-core 的配置分成两块:
| 前缀 | 类 | 作用 |
|---|---|---|
soda.event.* | com.hibuka.soda.bus.configuration.EventProperties | 事件总线、Redis Stream、序列化 |
scoda.ddd.cqrs.async.* | com.hibuka.soda.bus.configuration.AsyncConfig | CQRS 异步线程池 |
线程池的前缀是 scoda.ddd.cqrs.async,不是 soda.ddd.cqrs.async。README-CN 里写错了。
soda.event
EventProperties 标注了 @ConfigurationProperties(prefix = "soda.event") 和 @Validated,所以配置写错会在启动时抛异常,而不是运行时才暴露。
soda.event.bus-type
| 项 | 值 |
|---|---|
| 键 | soda.event.bus-type |
| 类型 | String |
| 默认值 | spring |
| 校验 | @NotBlank(不能为空) |
可选值:
| 值 | 效果 |
|---|---|
simple | 进程内事件总线,SimpleEventBus |
redis | Redis Streams 事件总线,RedisStreamEventBus |
spring | 当前版本没有实现,不要用 |
EventProperties 的默认值是 "spring",但 17.0.1 里 soda-event-spring 模块已被删除,没有任何 EventBus 实现类的条件是 havingValue = "spring"。
实际行为是:SimpleEventBusAutoConfiguration 的条件是 havingValue = "simple", matchIfMissing = true,所以不配置时走 Simple;配成 spring 时反而一个 EventBus Bean 都没有 —— 连带 RepositoryEventAutoConfiguration(@ConditionalOnBean(EventBus.class))不注册,领域事件永远发不出去。
生产环境请显式写 soda.event.bus-type: simple 或 redis。
soda.event.spring
| 项 | 值 |
|---|---|
| 键 | soda.event.spring |
| 类型 | SpringProperties |
| 默认值 | 空对象 |
| 校验 | @NotNull |
SpringProperties 是一个空类 —— 没有任何可配置项。保留它只是为了配置结构的完整性。
soda.event.serialization
序列化配置。目前只有两个键,而且它们在 17.0.1 里实际上没有被任何代码读取 —— 序列化已经完全交给 EventSerializer 抽象处理了。
| 键 | 类型 | 默认值 | 校验 |
|---|---|---|---|
soda.event.serialization.circular-reference-handler | CircularReferenceHandler 枚举 | IGNORE | @NotNull |
soda.event.serialization.fail-on-self-references | boolean | false | 无 |
CircularReferenceHandler 的三个取值:
| 值 | 含义 |
|---|---|
IGNORE | 忽略循环引用(默认值) |
ERROR | 检测到循环引用时抛错 |
RETAIN | 用 JSON 引用保留循环引用 |
保留这两个键是为了向后兼容。17.0.1 移除了 SmartCircularReferenceSerializerModifier,循环引用的处理现在由你使用的 EventSerializer 实现自己负责。
soda.event.redis
Redis 相关配置。只有在 soda.event.bus-type=redis 时才生效。
soda.event.redis.topic
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.topic |
| 类型 | String |
| 默认值 | soda-events |
| 校验 | @NotBlank |
Redis Stream 的 key 名。发布的事件写进这个 Stream,消费组从这里拉取。
soda.event.redis.host
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.host |
| 类型 | String |
| 默认值 | localhost |
| 校验 | @NotBlank |
soda.event.redis.port
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.port |
| 类型 | int |
| 默认值 | 6379 |
| 校验 | @Positive |
soda.event.redis.password
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.password |
| 类型 | String |
| 默认值 | 无(null) |
| 校验 | 无 |
soda.event.redis.database
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.database |
| 类型 | int |
| 默认值 | 0 |
| 校验 | @PositiveOrZero |
soda.event.redis.stream
Redis Stream 的消费参数。
soda.event.redis.stream.group-name
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.group-name |
| 类型 | String |
| 默认值 | soda-events-group |
| 校验 | @NotBlank |
消费者组名。同一组内的多个消费者分摊消息。启动时用 XGROUP CREATE 创建,已存在则忽略。
soda.event.redis.stream.consumer-name
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.consumer-name |
| 类型 | String |
| 默认值 | soda-events-consumer- + System.getProperty("server.port", "0") |
| 校验 | @NotBlank |
消费者名。默认带上 server.port,这样同一台机器上跑多个实例不会撞名。
concurrency > 1 时,实际的消费者名会加后缀:consumerName-0、consumerName-1……
soda.event.redis.stream.concurrency
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.concurrency |
| 类型 | int |
| 默认值 | 1 |
| 校验 | @Positive |
并发消费线程数。每个线程一个 Redis Stream 订阅。
soda.event.redis.stream.batch-size
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.batch-size |
| 类型 | int |
| 默认值 | 10 |
| 校验 | @Positive |
单次 XREADGROUP 拉取的最大条数。
soda.event.redis.stream.maxlen
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.maxlen |
| 类型 | long |
| 默认值 | 10000 |
| 校验 | @Positive |
Stream 的最大长度。超过后旧消息被裁剪。
maxlen 裁剪的是未被消费组确认的消息之外的历史。如果消费速度跟不上生产速度,消息可能在处理前就被裁掉。maxlen 不是安全网 —— 需要可靠投递时,把它调大,并监控消费延迟。
soda.event.redis.stream.poll-timeout
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.poll-timeout |
| 类型 | long(毫秒) |
| 默认值 | 1000 |
| 校验 | @Positive |
拉取阻塞的超时时间。
soda.event.redis.stream.acknowledge-timeout
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.acknowledge-timeout |
| 类型 | long(毫秒) |
| 默认值 | 30000 |
| 校验 | @Positive |
确认超时。
这个参数在 EventProperties 里定义了并有校验,但 RedisEventBusAutoConfiguration 构造 RedisStreamEventBus 时没有把它传进去(Builder 里没有对应的 setter)。目前它是一个不生效的配置项。
soda.event.redis.stream.max-retries
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.max-retries |
| 类型 | int |
| 默认值 | 3 |
| 校验 | @PositiveOrZero |
处理失败时的最大重试次数。重试循环是 for (int retryCount = 0; retryCount <= maxRetries; retryCount++),所以总共尝试 maxRetries + 1 次。
soda.event.redis.stream.initial-retry-delay
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.initial-retry-delay |
| 类型 | long(毫秒) |
| 默认值 | 1000 |
| 校验 | @Positive |
首次重试的延迟。
soda.event.redis.stream.exponential-backoff
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.exponential-backoff |
| 类型 | boolean |
| 默认值 | true |
| 校验 | 无 |
是否指数退避。开启时延迟计算是:
private long calculateRetryDelay(int retryCount) {
if (exponentialBackoff) {
return (long) (initialRetryDelay * Math.pow(2, retryCount));
}
return initialRetryDelay;
}默认配置下,重试延迟依次是 1s、2s、4s。
soda.event.redis.stream.dead-letter-stream
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.dead-letter-stream |
| 类型 | String |
| 默认值 | soda-events-dead-letter |
| 校验 | @NotBlank |
死信流名。重试耗尽后,消息连同失败原因写进这个流:
Map<String, String> deadLetterEntry = new HashMap<>(message.getValue().size() + 4);
deadLetterEntry.putAll(message.getValue());
deadLetterEntry.put("deadLetterReason", reason);
deadLetterEntry.put("deadLetterTimestamp", String.valueOf(System.currentTimeMillis()));
deadLetterEntry.put("originalStream", message.getStream());
deadLetterEntry.put("originalId", message.getId().getValue());soda.event.redis.stream.idempotency.enabled
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.idempotency.enabled |
| 类型 | boolean |
| 默认值 | false |
| 校验 | 无 |
是否启用幂等。跨服务场景建议开启。
soda.event.redis.stream.idempotency.redis-key-prefix
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.idempotency.redis-key-prefix |
| 类型 | String |
| 默认值 | soda-events-idempotency |
| 校验 | @NotBlank |
幂等状态的 Redis key 前缀。完整 key 是 前缀:eventId。
soda.event.redis.stream.idempotency.expire-time
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.idempotency.expire-time |
| 类型 | long(秒) |
| 默认值 | 86400(24 小时) |
| 校验 | @Positive |
幂等状态的过期时间。
soda.event.redis.stream.idempotency.retry-interval
| 项 | 值 |
|---|---|
| 键 | soda.event.redis.stream.idempotency.retry-interval |
| 类型 | long(毫秒) |
| 默认值 | 100 |
| 校验 | @Positive |
幂等状态检查的重试间隔。
scoda.ddd.cqrs.async
AsyncConfig 用的前缀是 scoda.ddd.cqrs.async。它由 SpringBusAutoConfiguration 的 @EnableConfigurationProperties(AsyncConfig.class) 启用。
| 键 | 类型 | 默认值 | 校验 |
|---|---|---|---|
scoda.ddd.cqrs.async.core-pool-size | int | 8 | @PositiveOrZero |
scoda.ddd.cqrs.async.max-pool-size | int | 16 | @Positive |
scoda.ddd.cqrs.async.queue-capacity | int | 100 | @PositiveOrZero |
scoda.ddd.cqrs.async.thread-name-prefix | String | cqrs-async- | @NotBlank |
| 键 | 含义 |
|---|---|
core-pool-size | 核心线程数 |
max-pool-size | 最大线程数 |
queue-capacity | 队列容量 |
thread-name-prefix | 线程名前缀 |
线程池的其它参数是硬编码的(不在配置里):
| 项 | 值 |
|---|---|
| keepAliveSeconds | 60 |
| waitForTasksToCompleteOnShutdown | true |
| awaitTerminationSeconds | 60 |
| 拒绝策略 | 在调用者线程执行(CallerRunsPolicy 思路) |
完整示例
soda:
event:
bus-type: redis
redis:
topic: soda-events
host: 127.0.0.1
port: 6379
password: ${REDIS_PASSWORD:}
database: 0
stream:
group-name: soda-events-group
consumer-name: soda-events-consumer
concurrency: 2
batch-size: 20
maxlen: 50000
poll-timeout: 1000
acknowledge-timeout: 30000
max-retries: 3
initial-retry-delay: 1000
exponential-backoff: true
dead-letter-stream: soda-events-dead-letter
idempotency:
enabled: true
redis-key-prefix: soda-events-idempotency
expire-time: 86400
retry-interval: 100
scoda:
ddd:
cqrs:
async:
core-pool-size: 16
max-pool-size: 32
queue-capacity: 200
thread-name-prefix: "cqrs-async-"配置项速查表
| 键 | 类型 | 默认值 |
|---|---|---|
soda.event.bus-type | String | spring |
soda.event.serialization.circular-reference-handler | 枚举 | IGNORE |
soda.event.serialization.fail-on-self-references | boolean | false |
soda.event.redis.topic | String | soda-events |
soda.event.redis.host | String | localhost |
soda.event.redis.port | int | 6379 |
soda.event.redis.password | String | 无 |
soda.event.redis.database | int | 0 |
soda.event.redis.stream.group-name | String | soda-events-group |
soda.event.redis.stream.consumer-name | String | soda-events-consumer-{server.port} |
soda.event.redis.stream.concurrency | int | 1 |
soda.event.redis.stream.batch-size | int | 10 |
soda.event.redis.stream.maxlen | long | 10000 |
soda.event.redis.stream.poll-timeout | long | 1000 |
soda.event.redis.stream.acknowledge-timeout | long | 30000 |
soda.event.redis.stream.max-retries | int | 3 |
soda.event.redis.stream.initial-retry-delay | long | 1000 |
soda.event.redis.stream.exponential-backoff | boolean | true |
soda.event.redis.stream.dead-letter-stream | String | soda-events-dead-letter |
soda.event.redis.stream.idempotency.enabled | boolean | false |
soda.event.redis.stream.idempotency.redis-key-prefix | String | soda-events-idempotency |
soda.event.redis.stream.idempotency.expire-time | long | 86400 |
soda.event.redis.stream.idempotency.retry-interval | long | 100 |
scoda.ddd.cqrs.async.core-pool-size | int | 8 |
scoda.ddd.cqrs.async.max-pool-size | int | 16 |
scoda.ddd.cqrs.async.queue-capacity | int | 100 |
scoda.ddd.cqrs.async.thread-name-prefix | String | cqrs-async- |