Dapr 1.16.15 发布解析:Scheduler 中 Actor Reminder 与 Job 名称校验缺陷的修复
【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr
导读
Dapr 1.16.15 是一个针对 Scheduler 服务的关键缺陷修复版本。该版本解决了当 Actor Reminder 名称或 Actor ID 包含|、@等字符时,通过 Scheduler 服务注册 Reminder 或 Job 失败的问题。本文将深入剖析该缺陷的成因、影响范围、修复方案及其底层实现,帮助读者理解 Scheduler 内部名称拼接机制与 DNS-1123 校验规则的冲突,以及如何验证升级后的行为。
一、问题概述:合法的 Actor 却无法注册 Reminder
自 Dapr 1.15 起,Scheduler-backed Actor Reminder 成为默认实现。但在 1.16.15 之前的版本中,存在一个不一致的行为:当 Reminder 名称或它所属的 Actor ID 包含某些字符(如竖线|或 at 符号@)时,通过 Scheduler 服务注册 Reminder 会失败。
这一行为与 Dapr 的其他能力形成了鲜明对比——相同的字符在调用 Actor(actor invocation)和保存 Actor 状态(actor state)时都是被接受的。也就是说,一个在其他所有场景下都能正常工作的 Actor,却无法为其创建 Reminder。
更令人困惑的是错误信息本身具有误导性,报错如下:
a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9](https://link.gitcode.com/i/8acf0bd3631bbbad42ae253c756a8781)?(\.[a-z0-9](https://link.gitcode.com/i/8acf0bd3631bbbad42ae253c756a8781)?)*')该错误声称只允许小写名称,但实际上大写名称是可以注册成功的;同时它也没有准确描述究竟哪些字符被真正拒绝,给用户排障造成了极大的困扰。
二、影响范围:谁受到了影响
以下用户会受到影响:
- 使用 Scheduler-backed Actor Reminder(自 1.15 起的默认行为);
- Reminder 名称、Actor ID 或 scheduled job 名称中包含严格 DNS-1123 字符集之外的字符,例如
|、@或大写字母。
例如,一个 Actor ID 形如nexus-fire-safety-api||SmokeDetectorGroupMonitorActor的服务,或一个名称形如my@reminder|name的 Reminder,在 1.16.15 之前的版本中都无法通过 Scheduler 正常注册。
三、根因剖析:内部名称拼接与 DNS-1123 校验的冲突
3.1 Scheduler 如何组织 Reminder 与 Job 的键名
Scheduler 服务内部会将每个 Reminder 或 Job 组合成单个名称,并使用||作为内部字段分隔符:
- Actor Reminder:
actorreminder||<namespace>||<type>||<id>||<name> - Job:
app||<namespace>||<appID>||<name>
这一拼接逻辑可以在源码 pkg/scheduler/server/internal/serialize/names.go 中得到印证,其中PrefixesFromNamespace返回的键前缀正是"actorreminder||" + namespace + "||"与"app||" + namespace + "||"两种格式:
// PrefixesFromNamespace returns key prefixes for all jobs types for a given // namespace. func PrefixesFromNamespace(namespace string) []string { return []string{ "actorreminder||" + namespace + "||", "app||" + namespace + "||", } }3.2 DNS-1123 校验规则的严格程度远超预期
问题出在校验环节:拼接完成后,每个由||分隔的字段段都会单独使用 Kubernetes 的 DNS-1123 subdomain 规则进行校验。而 DNS-1123 规则只允许:
- 小写字母数字字符(
a-z、0-9) - 连字符
- - 点
.
这一字符集远比 Dapr 在 API 边缘(API edge)实际接受的字符集要严格得多,从而产生了 Actor 调用与 Reminder 注册之间的不一致。
仓库中 pkg/validation/validation.go 保留了该 DNS-1123 校验规则的定义与正则:
const ( dns1123LabelFmt string = "[a-z0-9](https://link.gitcode.com/i/8acf0bd3631bbbad42ae253c756a8781)?" dns1123LabelErrMsg string = "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character" dns1123LabelMaxLength int = 63 ) var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")而regexError辅助函数(见 pkg/validation/validation.go)生成的错误信息正是发布说明中出现的 "a lowercase RFC 1123 subdomain must consist of..." 格式。
3.3 大写名称为何"实际可用但报错说不行"
根因中还有一个隐蔽的细节:校验器在检查每个字段段之前会先将其小写化。因此大写名称在实践中是可以注册成功的,但错误提示依然引用只允许小写 RFC 1123 subdomain 的说明——这就是错误信息与真实行为脱节的原因:它既不反映真实拒绝的字符集合,也掩盖了规则本身的错位。
四、解决方案:统一使用 Dapr API 边缘的校验策略
4.1 新的字符允许规则
修复后,Scheduler 改用Dapr 在 API 边缘使用的同一套校验策略进行名称校验。这样,任何能用于 Actor 调用的名称,都能同样用于 Reminder 或 Job。具体规则如下:
- 允许:大写字母以及
|、@等此前被拒绝的字符; - 仍然禁止:
/、\、#、?、控制字符(含 NUL 字节),以及精确路径序列.和..; ||作为字段分隔符,在 Actor ID 和名称内部依然被接受。
4.2 源码级别的印证
这一"API 边缘校验策略"的实现可在 pkg/messaging/method/normalize.go 的ValidateName函数中找到,其注释明确指出它校验的是 Reminder 或 Timer 名称,且禁止的字符集合与发布说明完全一致:
// ValidateName checks that a name (e.g. reminder or timer name) does not // contain characters that could cause path traversal or injection when the // name is embedded in a URL path. Unlike NormalizeMethod, this rejects any // name containing '/' or '\' since names are identifiers, not paths. func ValidateName(name string) error { if strings.ContainsAny(name, "#?\x00/\\") { return fmt.Errorf("name contains forbidden character: %q", name) } for i := range name { b := name[i] if b < 0x20 || b == 0x7f { return fmt.Errorf("name contains control character at position %d: %q", i, name) } } if name == "." || name == ".." { return fmt.Errorf("name is a path traversal sequence: %q", name) } return nil }该函数拒绝的字符集合(#、?、NUL、/、\、控制字符、.与..)与发布说明中描述的修复后规则完全吻合,而错误信息现在也会准确描述实际被禁止的字符(如name contains forbidden character、name contains control character at position N、name is a path traversal sequence),不再抛出令人困惑的 DNS-1123 报错。
4.3 附带修复:包含||的 Actor ID 的列表元数据
本次修复还顺带解决了一个相关问题:对于 Actor ID 中包含||的 Actor,现在列出其 Reminder 时也能正确报告 Actor 元数据。这是因为||既是字段分隔符又是合法字符,在解析拼接键时需要能够无歧义地区分二者。
五、测试证据:键拼接的 Round-Trip 正确性
仓库中的单元测试对修复后的行为提供了完整验证,见 pkg/scheduler/server/internal/serialize/names_test.go。
1. Actor ID 包含||分隔符与单竖线的场景(names_test.go#L61-L64):
"actor id containing the || delimiter and single pipes": { meta: actorMeta("myns", "SmokeDetectorActor", "nexus-fire-safety-api||SmokeDetectorGroupMonitorActor||Nexus|6671cfbe7f48af247700a24b||SmokeDetectorGroupInformation"), expPrefix: "actorreminder||myns||SmokeDetectorActor||nexus-fire-safety-api||SmokeDetectorGroupMonitorActor||Nexus|6671cfbe7f48af247700a24b||SmokeDetectorGroupInformation||", },2. Round-Trip 往返测试(names_test.go#L86-L118)验证:将完整 Job 键减去前缀后,可以无歧义地恢复出原始的 Reminder 名称,即使 Actor ID 和 Reminder 名称本身包含|和||(字段分隔符)也是如此。测试用例覆盖了:
- 名称含
@与|的 Reminder:"my@reminder|name"; - Actor ID 含多段
||的复杂场景; - 名称含
|与@的 App Job:"my|job@name"。
同时,pkg/scheduler/server/internal/cron/cron_test.go 中的测试键actorreminder||myns||mytype||my||id||myreminder也验证了在 Actor ID 中嵌入||时键格式依然正确可解析。
六、升级与验证建议
- 升级到 Dapr 1.16.15(或包含此修复的更新版本)以解除注册限制。升级前,如果业务中已存在名称含
|、@或大写字母的 Reminder/Job 注册失败的情况,升级后即可正常注册。 - 升级后建议做回归验证:为包含特殊字符(如
|、@)的 Actor ID 创建 Reminder,并执行列出(list)、触发(fire)与删除(delete)操作,确认全链路正常。 - 仍须遵守的边界:请记住修复后并非所有字符都被放行——
/、\、#、?、控制字符(含 NUL)以及精确的.和..仍然会被拒绝。这些限制是为了防止名称被嵌入 URL 路径时引发路径穿越或注入风险(见 pkg/messaging/method/normalize.go 中的设计说明)。 - 错误信息解读:升级后若仍遇到校验错误,错误信息将直接指明被禁止的具体字符或字符位置(例如
name contains forbidden character),不再出现误导性的 DNS-1123 小写规则提示。
七、总结
Dapr 1.16.15 通过将 Scheduler 的名称校验统一到 Dapr API 边缘策略,消除了"Actor 可用但 Reminder 不可注册"的不一致问题。从 pkg/scheduler/server/internal/serialize/names.go 的键拼接设计,到 pkg/validation/validation.go 中 DNS-1123 规则的来源,再到 pkg/messaging/method/normalize.go 中新的校验实现与 pkg/scheduler/server/internal/serialize/names_test.go 的 Round-Trip 测试,本次修复体现了 Dapr 在 API 边界一致性上的工程细节:内部存储格式(||分隔)与对外可接受字符集必须协同演进,才能既保证键名可解析,又保证用户命名自由度不受无谓限制。
【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考