soda docs v17.0.1

配置参考

soda-core 的配置分成两块:

前缀类作用
soda.event.*com.hibuka.soda.bus.configuration.EventProperties事件总线、Redis Stream、序列化
scoda.ddd.cqrs.async.*com.hibuka.soda.bus.configuration.AsyncConfigCQRS 异步线程池
注意

线程池的前缀是 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
redisRedis 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-handlerCircularReferenceHandler 枚举IGNORE@NotNull
soda.event.serialization.fail-on-self-referencesbooleanfalse无

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
校验无

是否指数退避。开启时延迟计算是:

java
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

死信流名。重试耗尽后,消息连同失败原因写进这个流:

java
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-sizeint8@PositiveOrZero
scoda.ddd.cqrs.async.max-pool-sizeint16@Positive
scoda.ddd.cqrs.async.queue-capacityint100@PositiveOrZero
scoda.ddd.cqrs.async.thread-name-prefixStringcqrs-async-@NotBlank
键含义
core-pool-size核心线程数
max-pool-size最大线程数
queue-capacity队列容量
thread-name-prefix线程名前缀

线程池的其它参数是硬编码的(不在配置里):

项值
keepAliveSeconds60
waitForTasksToCompleteOnShutdowntrue
awaitTerminationSeconds60
拒绝策略在调用者线程执行(CallerRunsPolicy 思路)

完整示例

yaml
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-typeStringspring
soda.event.serialization.circular-reference-handler枚举IGNORE
soda.event.serialization.fail-on-self-referencesbooleanfalse
soda.event.redis.topicStringsoda-events
soda.event.redis.hostStringlocalhost
soda.event.redis.portint6379
soda.event.redis.passwordString无
soda.event.redis.databaseint0
soda.event.redis.stream.group-nameStringsoda-events-group
soda.event.redis.stream.consumer-nameStringsoda-events-consumer-{server.port}
soda.event.redis.stream.concurrencyint1
soda.event.redis.stream.batch-sizeint10
soda.event.redis.stream.maxlenlong10000
soda.event.redis.stream.poll-timeoutlong1000
soda.event.redis.stream.acknowledge-timeoutlong30000
soda.event.redis.stream.max-retriesint3
soda.event.redis.stream.initial-retry-delaylong1000
soda.event.redis.stream.exponential-backoffbooleantrue
soda.event.redis.stream.dead-letter-streamStringsoda-events-dead-letter
soda.event.redis.stream.idempotency.enabledbooleanfalse
soda.event.redis.stream.idempotency.redis-key-prefixStringsoda-events-idempotency
soda.event.redis.stream.idempotency.expire-timelong86400
soda.event.redis.stream.idempotency.retry-intervallong100
scoda.ddd.cqrs.async.core-pool-sizeint8
scoda.ddd.cqrs.async.max-pool-sizeint16
scoda.ddd.cqrs.async.queue-capacityint100
scoda.ddd.cqrs.async.thread-name-prefixStringcqrs-async-

下一步