#event-bus #events #pub-sub #lock-free #async-api #disruptor

eventador

支持同步和异步API的无锁pub/sub事件总线

18个版本

0.0.18 2021年2月15日
0.0.17 2021年2月11日

#1091 in 异步

Apache-2.0

52KB
928

eventador-rs

Crates.io Docs.rs

这个crate提供了一个受LMAX Disruptor启发的无锁Pub/Sub事件总线。

用户可以通过WaitStrategy策略来配置发布者如何处理缓慢的订阅者。

提供了同步和异步API。

示例

请使用提供的示例程序来更全面地了解如何使用此crate。

基本同步使用

use eventador::Eventador;

let eventbus = Eventador::new(4).unwrap();
let subscriber = eventbus.subscribe::<usize>();

let i: usize = 1234;
eventbus.publish(i);

let mut publisher = eventbus.publisher();
publisher.send(i + 1111);

let mut msg = subscriber.recv();
assert_eq!(i, *msg);

msg = subscriber.recv();
assert_eq!(i + 1111, *msg);

基本异步使用

use eventador::{Eventador, SinkExt, StreamExt};

let eventbus = Eventador::new(4).unwrap();

let mut subscriber = eventbus.async_subscriber::<usize>();
let mut publisher = eventbus.async_publisher::<usize>(4);

let i: usize = 1234;
publisher.send(i).await?;

let msg = subscriber.next().await?;
assert_eq!(i, *msg);

为什么使用?

事件总线通过允许并发应用程序子例程通过事件相互交互和影响其他子例程,从而减轻了并发程序的开发负担。

Eventador通过提供不同的策略来处理订阅者落后时发布,拥抱Rust的“Choose Your Guarantees ™”模式。这些策略表现为WaitStrategies,默认为等待所有订阅者读取事件后再覆盖。

功能标志

  • async:启用异步API的使用

设计考虑

可以在这里找到库架构的概述。

环形缓冲区

与Eventador一样,大多数事件总线实现都使用某种形式的环形缓冲区作为存储已发布事件的基本数据结构。因此,Eventador实例不能无限增长以容纳事件,这与Vec不同。发布者需要可配置的策略来决定如何在环形缓冲区中何时覆盖旧数据。

LMAX Disruptor

LMAX Disruptor是许多事件总线实现的基础,尽管Disruptor的当代架构与过时的LMAX白皮书中的架构大不相同。Eventador借鉴了当前Disruptor架构的原则,但相似之处到此为止。

在发布事件时,序列器原子地将事件分配给环形缓冲区中的索引。

订阅者在内部有自己的序列器,以确定环形缓冲区中最后读取的事件。在接收到订阅消息时,序列器原子地更新以反映它现在可以接收下一个事件。

无锁

Eventador 有可能成为一个高冲突(也称为瓶颈)结构,对于给定的并发程序,因此其实施需要尽可能地有效处理冲突。原子 CAS 操作通常比锁定更快,并且是处理冲突的首选方法。

TypeId

这个 crate 依赖于使用 TypeId 来确定事件的类型以及订阅者订阅的事件类型。

不幸的是,由于 Rust 反射工具的限制,枚举将与枚举变体的 TypeId 不同。这意味着订阅者必须订阅枚举类型,并忽略它不感兴趣的任何变体,即使它仍然收到。同样,发布者必须以枚举类型发布事件,而不是变体,以保持一致性。

功能状态

测试,尤其是基准测试,尚未完全实现。

功能 状态
Sync MPMC Pub/Sub
Async MPMC Pub/Sub
等待策略

依赖项

~0.5–15MB
~136K SLoC