1 个不稳定版本

0.3.0 2024 年 5 月 5 日

#322WebSocket


用于 aper-yew

MIT 许可证

77KB
1.5K SLoC

Aper

GitHub Repo stars crates.io docs.rs wokflow state

Cartoonized face of an ape.

Aper 是一个用于使用 状态机 进行数据同步的 Rust 库。Aper 提供了用状态机表示常见数据结构的机制,以及一个与传输无关的协议,用于在网络上保持多个状态机的多个实例同步。

应用场景包括在共享状态下运行的实时多人应用程序,希望增量且双向共享状态更新的客户端/服务器应用程序,以及多人回合制游戏。

什么是状态机?

对于 Aper 来说,状态机只是一个实现了 StateMachinestructenum,它具有以下属性

  • 它定义了一个 StateMachine::Transition 类型,通过它可以描述对状态的每一次可能的更改。这通常是一个有用的枚举类型,但不是必需的。
  • 它定义了一个 StateMachine::Conflict 类型,它描述了在应用一个在应用时无效的转换时可能发生的冲突。对于不可能发生冲突的简单类型,您可以使用 NeverConflict
  • 所有状态更新都是确定性的:如果您克隆了一个 StateMachine 和一个 Transition,将克隆的转换应用到克隆的状态的结果必须与将原始转换应用到原始状态的结果相同。

以下是一个实现计数的 StateMachine 的示例

use aper::{StateMachine, NeverConflict};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
struct Counter { value: i64 }

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
enum CounterTransition {
    Reset,
    Increment(i64),
    Decrement(i64),
}

impl StateMachine for Counter {
    type Transition = CounterTransition;
    type Conflict = NeverConflict;

    fn apply(&self, event: &CounterTransition) -> Result<Counter, NeverConflict> {
        match event {
            CounterTransition::Reset => Ok(Counter {value: 0}),
            CounterTransition::Increment(amount) => Ok(Counter {value: self.value + amount}),
            CounterTransition::Decrement(amount) => Ok(Counter {value: self.value - amount}),
        }
    }
}

为什么不使用 CRDT?

无冲突复制数据类型 是一种非常巧妙的表示在节点间共享数据的手段。为了避免需要一个中心“事实来源”,CRDTs 要求更新操作(即状态转换)必须是 交换律。这使得它们可以表示许多常见的数据结构,但不允许表示任意复杂的更新逻辑。通过依赖于中心权威,状态机方法允许你实现具有任意更新逻辑的数据结构,例如在两个数据结构之间原子性地移动值,或棋盘游戏的规则。


Aper 正在快速演进。请将此视为技术预览。 查看版本 1.0 的 待解决的问题列表

依赖项

~9–13MB
~246K SLoC