WebFlux
wow-webflux 根据 wow-openapi 的运行时 route contracts 创建命令、状态、快照、事件和查询 Handler。需要 Wow 声明式 HTTP API 时使用;已有自定义 controller 且不需要自动路由时无需引入。
模块负责响应式 handler、请求提取、等待策略和错误映射;Spring WebFlux/Netty 负责监听端口、连接、codec、资源和网络超时,应用负责认证授权与网关策略。
安装
implementation("me.ahoo.wow:wow-spring-boot-starter") {
capabilities { requireCapability("me.ahoo.wow:webflux-support") }
}直接依赖 wow-webflux 只获得 API/实现类,不会获得 Starter 的 WebFluxAutoConfiguration 和属性绑定。
自动路由注册
自动配置合并 ordered WebFluxRouteModule 与额外 HttpRouteHandlerFunctionFactory,再用 RouterSpecs 构建一个 RouterFunction。只有已加载元数据和实际注册的 contributor 会产生路由;模块存在不等于某个业务端点存在。
路由模式
路径由 aggregate/command route metadata 决定,并按 tenant、owner、space 需要增加作用域。只有应用另行安装与当前 Spring WebFlux 匹配的 Springdoc WebFlux starter 时,才可用候选 runtime /v3/api-docs 验证;仅有 webflux-support 时应检查 RouterSpecs.toRouteCatalog() 或等价 route catalog 诊断,不手写猜测生产路径。
聚合路由模式
@AggregateOwner(OwnerPolicy.AGGREGATE_ID) 等 metadata 决定 owner 与 aggregate ID 是否合并;命令 handler 仍从 path/header/body 创建 CommandMessage。
拥有者路由模式
owner/tenant/space 路由只表达资源作用域,不自动认证请求者。安全层必须把主体与这些值绑定并拒绝越权。
HTTP 方法映射
@CreateAggregate 和 @CommandRoute 生成的 method/path 来自编译元数据;WebFlux 只执行合同。修改注解或生成元数据后必须重新构建并检查 OpenAPI。
配置
最小配置只有 capability;默认 wow.webflux.enabled=true。常用默认值:
wow:
webflux:
global-error:
enabled: true
batch:
concurrency: 128
prefetch: 4
query:
default-list-size: 100
idle-timeout: 10s
strict-count-filter: false
query:
http:
max-list-size: 1000
max-page-size: 100
max-page-window: 10000
max-filter-nodes: 128
max-filter-values: 1000
allow-expensive-operators: true批量并发度按请求生效,并由快照重建与 StateEvent 重发共享;多个并发请求会叠加下游负载,应按应用与存储容量下调。
数值上限为 0 时关闭对应 HTTP guard;idle-timeout=0s 关闭 idle timeout。不要复制后端已负责的字段类型、mapping 或唯一性校验。入口为 HTTP 的查询(所有内置查询路由都会写入)由 Gateway 在准入时按 wow.query.http 预算检查;HttpQueryGuard 只保留 HTTP 适配器自己的职责(响应行数上限、limit=0 默认值、空闲超时、缓冲)。程序内的 QueryGateway 调用按进程内执行,默认不受预算约束,见查询网关。
count 请求体就是一个过滤表达式;根对象缺少 op 时按旧版条件读取,其操作符默认为 ALL,于是 {"field":"state.name","value":"x"} 会统计全部行。设 strict-count-filter: true 改为拒绝这样的请求体,客户端统计全部时显式发送 {"op":"MATCH_ALL"}。
聚合查询路由
Snapshot 与 EventStream 都接收 AggregationQuery:
.../snapshot/aggregation聚合快照模型;.../event/aggregation聚合事件流模型,并与 event/list、event/paged、event/count 使用相同的基础、tenant、owner 路由规则。
普通 JSON 会先收集动态行数组,Accept: text/event-stream 则逐行流式返回。Gateway 的 HTTP 入口预算(wow.query.http.*,在准入第 0 步检查)同样限制 condition、values、limit、Elements、metric sort 和高成本表达式;路由守卫只负责返回行数上限、默认列表大小、空闲超时与缓冲。
EventStream 的根 filter 作用于事件流文档;使用 elements = [{"path":"body"}] 展开事件数组后,group 和 metric 字段相对事件项。Elasticsearch 当前不索引 body.body payload,因此跨后端聚合范围是事件流 envelope 与 body 事件元数据;需要 payload 聚合时必须先单独设计 mapping 与历史数据重建。
命令 HTTP 合同
WebFlux 物化命令路由并把请求交给命令运行时。命令路由、媒体类型与请求构造见发送命令;等待阶段、函数匹配、timeout 和未知结果见完成语义。本页不复制命令 Header 或等待状态机。
错误处理
默认 RequestExceptionHandler 把框架错误转换为 ErrorInfo 响应,并写 Wow-Error-Code;全局 WebExceptionHandler 默认启用。已验证映射包括参数/状态错误 400、not found 404、wait timeout 408、未知错误 500。自定义 error strategy 时必须保留原异常失败路径,避免空响应吞掉错误。
路由报告的所有错误都由同一个 WebFluxErrorStrategy 映射,包括状态码已是 200、只能写进响应体的错误:中途失败的 SSE 流的最后一个事件,以及批量结果(快照重建、StateEvent 重发)的 errorCode/errorMsg。自 9.3.0 起,策略未归类的异常在这里也和 JSON 路由一样是 InternalServerError,消息为 "Unexpected server error";9.3.0 之前 SSE 事件和批量结果是 BadRequest 加异常的原始消息。改了错误码或消息的自定义策略,除 toServerResponse 外还要覆盖 toErrorInfo(throwable),SSE 事件和批量结果才会随之改变。反过来也一样:通过委托构建的策略(object : WebFluxErrorStrategy by DefaultWebFluxErrorStrategy)若只覆盖 toErrorInfo,改变的只是 SSE 事件和批量结果,JSON 响应仍由 DefaultWebFluxErrorStrategy 按它自己的映射渲染;两者都要覆盖。自定义 RequestExceptionHandler 通过 handleInBody(request, throwable) 报告这类错误;默认实现用 DefaultWebFluxErrorStrategy 映射,不再调用 handle。
OpenAPI 集成
OpenAPI 由运行时 metadata 和 route contracts 组装。每个实例按 wow.query.schema.revalidate-interval 定期重新校验自己的 schema,wowQuerySchema actuator 端点列出本实例的 schema 版本并可按需立即重新校验。9.1 的 POST /{aggregate}/snapshot/schema/refresh 与 POST /{aggregate}/event/schema/refresh 作为弃用别名保留到 10.0.0:在作答的实例上只重新加载该聚合对应的模型(Snapshot 或 EventStream),并发请求共用同一次进行中的加载,再像 GET …/schema 那样返回能力描述。重新校验只影响执行它的实例,失败时保留上一个 schema;它不广播、不修改后端 mapping。actuator 端点只在管理面暴露。
性能优化
先观察 event-loop 阻塞、序列化、handler latency、等待连接数和 query guard rejection,再调整 batch 或 server 设置。
响应式处理
内置路径返回 Mono/Flux,不在核心 handler 中阻塞。自定义 controller 也必须保持这一边界;阻塞 cache source 或驱动调用应隔离到合适 scheduler,并用负载测试验证。
监控和调试
记录 route ID、request ID、error code、HTTP status、latency 和 cancellation。需要框架 span 时加入 opentelemetry-support;WebFlux capability 不配置 exporter。
请求日志
可临时开启 me.ahoo.wow.webflux=DEBUG,但不要记录命令机密、认证 header 或完整敏感 payload。诊断后恢复生产日志级别。
最佳实践
- 已安装匹配 Springdoc WebFlux starter 时用候选 runtime OpenAPI 验证路由,否则检查
RouterSpecsroute catalog; - 在 WebFlux 之前完成认证,在 query/command route 上执行授权;
- 保持响应式链路非阻塞,显式测试取消和 timeout;
- 只按风险调整 HTTP guard,不用
0作为默认逃生开关。
聚焦检查:
./gradlew :wow-webflux:check