1个不稳定版本
| 0.0.0 | 2022年10月5日 |
|---|
#22 在 #integration-testing
2KB
orchestra
orchestra模式是一种部分演员模式,其中全局协调器负责相关的工作项。
proc-macro
该宏提供了一种方便的生成器,采用构建器模式,其核心是创建并启动一组子系统,这些子系统完全是声明式的。
#[orchestra(signal=SigSigSig, event=Event, gen=AllMessages, error=OrchestraError)]
pub struct Opera {
#[subsystem(MsgA, sends: [MsgB])]
sub_a: AwesomeSubSysA,
#[cfg(any(feature = "feature1", feature = "feature2"))]
#[subsystem(MsgB, sends: [MsgA])]
sub_b: AwesomeSubSysB,
}
- 每个子系统都使用以下注解
#[subsystem(_)],其中MsgA分别MsgB是该子系统消费的消息。每个子系统都必须实现具有正确特例的子系统特例。通常,这是通过使用#[subsystem]和#[contextbounds]宏来实现的。#[contextbounds(Foo, error=Yikes, prefix=wherethetraitsat)]可以应用于impl-块和fn-块。它将为泛型Context添加额外的特例,例如Context: FooContextTrait,以及一些其他特例。注意,这里的Foo引用的是在#[orchestra(..)]宏中声明的子系统名称。#[subsystem(Foo, error=Yikes, prefix=wherethetraitsat)]是对上述内容的扩展,实现了trait Subsystem<Context, Yikes>。
error=告诉编曲器使用用户提供的错误类型,如果没有提供,则使用内置类型。请注意,这是在整个调用过程中使用的唯一错误类型,因此请确保为所有相关的应用程序错误类型E实现了From<E>。event=声明了一个外部事件类型,它将某些事件注入到编曲器中,而不参与子系统模式。signal=定义了一个信号类型,用于编曲器。这是所有子系统共享的“滴答”或“时钟”。gen=定义了一个包装enum类型,用于包装所有可以被 任何 子系统消费的消息。- 功能可以通过
#[cfg(feature = "feature")]属性宏表达式进行功能门控。目前支持的是any、all、not和feature。
/// Execution context, always required.
pub struct DummyCtx;
/// Task spawner, always required
/// and must implement `trait orchestra::Spawner`.
pub struct DummySpawner;
fn main() {
let _orchestra = Opera::builder()
.sub_a(AwesomeSubSysA::default())
.sub_b(AwesomeSubSysB::default())
.spawner(DummySpawner)
.build();
}
在显示的 main 中,通过生成的、编译时错误生成器模式创建了编曲器。
生成器要求在调用 build 方法之前,通过相应的设置器方法设置所有子系统、行李字段(额外的结构数据)和生成器。未能进行此类初始化将导致编译错误。这是通过将每个生成器字段编码为所谓的 state generics 来实现的,这意味着每个字段可以是 Init<T> 或 Missing<T>,因此每个设置器将特定结构字段的 Missing 状态转换为 Init 状态。因此,如果您在期望 Init 的位置看到了关于 Missing 的编译时错误,通常意味着在调用 build 之前没有设置某些子系统或行李字段。
要排除子系统进行检查,可以在尚未准备包含在 orchestra 中的某些子系统上设置 wip 属性。
#[orchestra(signal=SigSigSig, event=Event, gen=AllMessages, error=OrchestraError)]
pub struct Opera {
#[subsystem(MsgA, sends: MsgB)]
sub_a: AwesomeSubSysA,
#[subsystem(MsgB, sends: MsgA), wip]
sub_b: AwesomeSubSysB, // This subsystem will not be required nor allowed to be set
}
行李字段可以初始化多次,但对于子系统则不是这样:子系统必须只初始化一次(另一个编译时检查)或通过特殊的设置器方法(如 replace_<subsystem> 方法)进行替换。
需要一个任务生成器和子系统上下文,分别通过实现 Spawner 和 SubsystemContext 来定义。
调试
和往常一样,调试存在问题的 proc-macros 是一件非常令人烦恼的事情,请参阅功能 "expand"。
功能
功能 "expand"
expander 被用来生成更好的错误信息。通过 --features=orchestra/expand 来启用。
功能 "dotgraph"
根据声明的要发送和消费的消息生成一个有向图,显示连接性。通过 --features=orchestra/dotgraph 来启用。生成的文件路径将显示出来,形式为 ${OUT_DIR}/${orchestra|lowercase}-subsystem-messaging.dot。使用 dot -Tpng ${OUT_DIR}/${orchestra|lowercase}-subsystem-messaging.dot > connectivity.dot 将其转换为例如 png 图像或使用您喜欢的 dot 文件查看器。它还在旁边创建了一个 .svg 图,直接用于 .dot 图。
注意事项
没有工具是没有缺陷的,orchestra 也不例外。
大消息类型
不建议通过通道发送大消息,就像在其他通道实现中一样。如果您需要传输大于几百字节数据,请使用 Box<_> 包裹它或使用全局标识符访问持久化状态,例如数据库,具体取决于用例。
响应通道
在消息中包含响应通道似乎非常有吸引力,对于许多情况,这些通道是维护结构化数据流的一种非常方便的方式,但如果不小心使用,它们可能会让你陷入困境。需要的小心之处包括以下三个方面:
- 循环消息依赖导致单线程子系统的死锁
- 在多个子系统之间跨度过深的消息依赖
- 响应通道导致的延迟
每个问题都有各种解决方案,例如使用本地缓存来修复频繁查找相同信息的情况,或者在特殊情况下将子系统分割成多个部分以避免循环依赖,但具体取决于 orchestra 的使用上下文。
要找到这些问题,dotgraph 功能提供了子系统到子系统级别的所有交互的可视化(还不是消息级别),以便调查循环。在生成阶段请留意警告。
许可证
许可协议如下之一
- Apache License, Version 2.0, (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可协议 (LICENSE-MIT 或 https://open-source.org.cn/licenses/MIT)
由您选择。