v0.3.0于4.23发布,使用 AsyncRead & AsyncWrite来替换我们的 ReadEx & WriteEx & SplitEx;简化了 Kad/DHT 的实现逻辑。

修改

ReadEx & WriteEx & SplitEx:

最初我们尝试借助 async-trait 来定义自己 io 操作相关的 Trait,以便更纯粹的使用 async/await 的方式来编写代码。

ReadEx 为例大概是下面这样:

#[async_trait]
pub trait ReadEx {
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error>;
}

使用我们定义的 Trait 确实给我们带来了一些好处,无需再编写状态机式的代码。相同的逻辑可以用更容易理解的方式实现;同时也引入了一些问题:

  1. 很难进行读写分离 我们先来看看为什么需要读写分离,使用 async/await 方式相较于 poll 的方式,失去了一些控制能力,无法在一个 Future 中同时做多件事,比如我们这里的读写:
let mut socket = ...;

// poll 方式
fn poll(socket: Pin<&mut Socket>, cx: Context) {
    match socket.poll_read(cx) {
        Poll::Pending => {
            // 这里就有机会在 poll 中同时处理读写
            socket.poll_write(cx)
        }
        Poll::Ready => {...}
    }
}

// async/await
async fn handle_socket(socket: &mut Socket) {
    socket.read().await;
    // 这里就没办法在在 read 还没准备好的时候,继续去执行write操作
    // 由于 read & write 都是需要 &mut T的所以也没办法借助select来打到目的
    // let read_fut = socket.read();
    // let write_fut = socket.write();
    // select(read_fut, write_fut).await;
}

鉴于这样的原因,我们就需要将读写分开来处理。同时读写放在不同的协程中处理,代码逻辑也会更清晰。

要实现读写分离,当然是需要 Runtime 底层 io 提供支持的,两大 Runtime 阵营提供了不同的实现方式:

  • async-stdClone 的方式达到分离的目的
  • tokio 则借助 BiLock 实现读写分离

Clone 的方式当然很好,libp2p-rs 中是分了很多层的,下层给上层提供的 io 有自己的逻辑在里面,这样就会有各种原因让我们很难实现 Clone,即便我们付出一些代价实现了 Clone 那也意味着绑定了某个运行时。

借助 BiLock 的方式更通用,这时候 ReadEx 的弊端就显现了,获得锁之后没办法在底层 io 处于Pending 状态时释放锁,这将导致读写协程之间存在死锁的可能。

鉴于以上原因我们又定义了 SplitEx,各层向上提供的 io 抽象都需要实现 SplitEx,这样能解决问题,但不够优雅还增加了工作量。

2.无法很好的向后兼容 Rust 异步编程官方未定义标准的读写 Traitfutures 库中的 AsyncRead & AsyncWrite 可以说是事实上的标准。现存的应用/系统中都是基于 AsyncRead & AsyncWrite 去构建的,现有应用/系统想要切换到 libp2p-rs 就需要做一些修改了,Rust 代码中大多是以泛型参数加上 where 条件的形式限定的,而 Rust 对于这种限定条件的修改往往是牵一发动全身。

替代方案是我们也可以在 ReadEx 的基础上包装出 AsyncRead ,但这需要付出额外的性能开销,当然也不是我们愿意见到的。

3.无法复用 futures 库中的一些扩展功能

Kad:

Kad-DHT 协议的实现也做了一些修改。 更具体地说,从 Kad 中删除了 re-provide/publish 功能。我们认为 re-provide/publish 的逻辑属于使用 Kad 的应用程序,而不是 Kad 本身。 这样的话,相应地也能简化 Provider/Record 的数据结构,并且在 RecordStore Trait 中增加两个 gc_xxx 方法来分别对 Provider/Record 执行 GC,这是通过跟踪从网络接收到的 Provider/Record 的时间戳来完成的 。 请注意,仅对从网络接收到的 provider/record 执行GC。


Netwarps 由国内资深的云计算和分布式技术开发团队组成,该团队在金融、电力、通信及互联网行业有非常丰富的落地经验。Netwarps 目前在深圳、北京均设立了研发中心,团队规模30+,其中大部分为具备十年以上开发经验的技术人员,分别来自互联网、金融、云计算、区块链以及科研机构等专业领域。 Netwarps 专注于安全存储技术产品的研发与应用,主要产品有去中心化文件系统(DFS)、去中心化计算平台(DCP),致力于提供基于去中心化网络技术实现的分布式存储和分布式计算平台,具有高可用、低功耗和低网络的技术特点,适用于物联网、工业互联网等场景。 公众号:Netwarps