17 个版本
0.4.0 | 2024 年 6 月 12 日 |
---|---|
0.3.6 | 2024 年 5 月 8 日 |
0.3.5 | 2024 年 1 月 29 日 |
0.3.3 | 2023 年 7 月 28 日 |
0.0.0 | 2016 年 2 月 15 日 |
#62 在 Rust 模式
129,002 每月下载量
用于 56 个crate (2 个直接使用)
105KB
2K SLoC
orchestra
Orchestra 模式是一种部分演员模式,全局指挥者负责相关工作项。
proc-macro
该 proc 宏提供了一个带有构建器模式的便捷生成器,其核心创建并启动一组子系统,这些子系统是纯声明性的。
#[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
,以及<Context as FooContextTrait>::Sender: FooSenderTrait
等。注意,这里的Foo
指的是在#[orchestra(..)]
宏中声明的子系统名称。#[subsystem(Foo, error=Yikes, prefix=wherethetraitsat)]
是上述功能的扩展,实现了trait Subsystem<Context, Yikes>
。
error=
通知乐团使用用户提供的错误类型,如果没有提供,则使用内置类型。请注意,这是在整个调用中使用的唯一错误类型,因此请确保为所有相关的应用错误类型E
实现了From<E>
。event=
声明一个外部事件类型,该类型会将某些事件注入到乐团中,而不参与子系统模式。signal=
定义一个用于乐团的信号类型。这是所有子系统的共享“tick”或“时钟”。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_<子系统>
方法进行替换。
任务生成器和子系统上下文需要使用 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文件查看器。它还会在.dot
图旁边创建一个.svg
,可以直接使用。
注意事项
没有工具是没有缺点的,orchestra
也不例外。
大消息类型
不建议通过通道发送大消息,就像其他通道实现一样。如果您需要传输大于几百字节的数据,请使用Box<_>
包裹它,或者使用全局标识符来访问持久状态,例如数据库,具体取决于用例。
响应通道
将响应通道作为消息的一部分似乎非常吸引人,并且在许多情况下,这些都是维护结构化数据流的一种非常方便的方式,但如果不谨慎使用,它们可能会给您带来麻烦。所需的谨慎包括三个方面:
- 循环消息依赖导致单线程子系统死锁
- 跨多个子系统太深的消息依赖
- 由于响应通道导致的延迟
每个问题都有各种解决方案,例如使用本地缓存来纠正频繁查找相同信息的操作,或者在某些异常情况下将子系统拆分为多个以避免循环依赖,或者将拓扑上紧密相关的子系统合并,但这强烈取决于orchestra使用的具体环境。
要查找这些,功能dotgraph
提供了子系统到子系统级别的所有交互的可视化(目前还不是消息级别),以研究循环。在生成阶段注意警告。
许可证
根据您的要求,许可协议为以下之一:
- Apache License,版本2.0,(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选。
依赖关系
~4.5–6.5MB
~111K SLoC