24、异步Rust编程与P2P网络开发

异步Rust编程与P2P网络开发

1. 理解异步Rust

在异步Rust编程中,程序不会出现挂起的情况,能够成功执行两个异步任务。为了进一步理解 futures 的工作原理,我们可以实现一个异步定时器。当时间到期时, Waker 会通知 Tokio 运行时,与之关联的任务已准备好再次被轮询。当 Tokio 运行时第二次轮询该函数时,将从函数中获取一个值。

1.1 实现自定义 future

我们创建一个新的 future 来表示异步定时器,它需要完成以下操作:
1. 定时器接受一个过期时间。
2. 每当运行时执行器对其进行轮询时,会进行如下检查:
- 如果当前时间大于过期时间,将返回带有 String 值的 Poll::Ready
- 如果当前时间小于过期时间,它将进入休眠状态,直到过期时间,然后触发 Waker 上的 wake() 调用,这将通知异步运行时执行器再次调度和执行该任务。

以下是具体的代码实现:

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::thread::sleep;
use std::time::{Duration, Instant};

struct AsyncTimer {
    expiration_time: Instant,
}

impl Future for AsyncTimer {
    type Output = String;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if Instant::now() >= self.expiration_time {
            println!("Hello, it's time for Future 1");
            Poll::Ready(String::from("Future 1 has completed"))
        } else {
            println!("Hello, it's not yet time for Future 1. Going to sleep");
            let waker = cx.waker().clone();
            let expiration_time = self.expiration_time;
            std::thread::spawn(move || {
                let current_time = Instant::now();
                if current_time < expiration_time {
                    std::thread::sleep(expiration_time - current_time);
                }
                waker.wake();
            });
            Poll::Pending
        }
    }
}

#[tokio::main]
async fn main() {
    let h1 = tokio::spawn(async {
        let future1 = AsyncTimer {
            expiration_time: Instant::now() + Duration::from_millis(4000),
        };
        println!("{:?}", future1.await);
    });
    let h2 = tokio::spawn(async {
        let file2_contents = read_from_file2().await;
        println!("{:?}", file2_contents);
    });
    let _ = tokio::join!(h1, h2);
}

// function that simulates reading from a file
fn read_from_file2() -> impl Future<Output = String> {
    async {
        sleep(Duration::new(2, 0));
        String::from("Future 2 has completed")
    }
}

上述代码的具体步骤如下:
1. 定义 AsyncTimer 类型,用于存储过期时间。
2. 在 AsyncTimer 上实现 Future 特性。
3. 指定 future 的输出值为 String 类型。
4. 实现 poll() 函数。
- 在 poll() 函数中,首先检查 current_time >= expiration_time 。如果是,则从函数中返回带有 String 值的 Poll::Ready
- 如果 current_time < expiration_time ,则启动线程休眠所需的时间。
- 调用 wake() 函数,通知异步执行器再次调度该任务进行执行。
5. 在 main() 函数中,使用定时器的过期时间初始化 future 类型。

运行该程序,终端将输出以下内容:

Hello, it's not yet time for Future 1. Going to sleep
"Future 2 has completed"
Hello, it's time for Future 1
"Future 1 has completed"

下面是程序执行的流程图:

graph TD;
    A[开始] --> B[创建异步任务1和2];
    B --> C[执行任务1,调用AsyncTimer];
    C --> D{过期时间是否到达};
    D -- 否 --> E[输出未到时间,休眠并返回Poll::Pending];
    E --> F[执行任务2,调用read_from_file2];
    F --> G[任务2休眠2秒后完成];
    D -- 是 --> H[输出时间已到,返回Poll::Ready];
    G --> I[等待任务1完成];
    H --> I;
    I --> J[程序结束];
2. 引入对等网络

传统的分布式系统采用客户端/服务器模式,而对等(P2P)网络是另一种分布式系统。在P2P网络中,一组节点(或对等体)直接相互交互,共同提供一项公共服务,无需中央协调器或管理员。

2.1 P2P网络与客户端/服务器网络的比较
特性 客户端/服务器网络 P2P网络
架构 有专门的服务器,客户端向服务器请求服务 节点既可以是客户端也可以是服务器,直接交互
权限 服务器可控制客户端访问 无服务器控制,数据和状态在多个节点复制
容错性 存在单点故障(服务器) 无单点故障
抗审查性 数据集中存储,易被审查 数据在多个节点复制,难被审查
资源利用 边缘客户端资源未充分利用 更好地利用边缘客户端资源
2.2 构建P2P系统的技术要求

构建P2P系统比构建传统的客户端/服务器系统更为复杂,涉及以下几个技术要求:
1. 传输 :P2P网络中的每个对等体可以使用不同的协议,如HTTP(s)、TCP、UDP等。
2. 对等体身份 :每个对等体需要知道它想要连接并发送消息的对等体的身份。
3. 安全 :每个对等体应该能够以安全的方式与其他对等体进行通信,避免第三方拦截或修改消息。
4. 对等体路由 :每个对等体可以通过多种路由接收来自其他对等体的消息,这意味着每个对等体应该能够将消息路由到其他对等体(如果消息不是发给自己的)。
5. 消息传递 :P2P网络应该能够发送点对点消息或组消息(采用发布/订阅模式)。
6. 流复用 :P2P网络应该支持在公共通信链路上传输多个信息流,以实现与多个节点的并发通信。

下面我们将详细介绍这些技术要求:
- 传输 :TCP/IP和UDP协议非常普遍,常用于编写网络应用程序。此外,还有一些更高级的协议,如基于TCP的HTTP和基于UDP的QUIC。由于网络中对等体的多样性,P2P网络中的每个对等体应该能够发起与其他节点的连接,并能够监听多个协议的传入连接。
- 对等体身份 :与Web开发领域不同,在Web开发中,服务器通过唯一的域名(如 www.rust-lang.org )来标识,然后通过域名服务解析为服务器的IP地址。而P2P网络中的节点需要一个唯一的身份,以便其他节点能够找到它们。P2P网络中的节点使用公钥和私钥对(非对称公钥加密)来与其他节点建立安全通信。节点在P2P网络中的身份称为 PeerId ,它是节点公钥的加密哈希值。
- 安全 :加密密钥对和 PeerId 使节点能够与其对等体建立安全、经过身份验证的通信通道。但这只是安全的一个方面。节点还需要实现授权框架,以建立哪些节点可以执行哪些操作的规则。此外,还需要解决网络级别的安全威胁,如Sybil攻击(其中一个节点操作员创建大量具有不同身份的节点,以在网络中获得有利地位)或日食攻击(一组恶意节点勾结以针对特定节点,使该节点无法访问任何合法节点)。
- 对等体路由 :P2P网络中的节点首先需要找到其他对等体才能进行通信。这通过维护一个对等体路由表来实现,该表包含网络中其他对等体的引用。但在一个拥有数千个节点且节点动态变化(即节点频繁加入和离开网络)的P2P网络中,任何单个节点都很难维护一个完整且准确的所有节点路由表。对等体路由使节点能够将不是发给自己的消息路由到目标节点。
- 消息传递 :P2P网络中的节点可以向特定节点发送消息,也可以参与广播消息协议。例如,发布/订阅模式中,节点注册对特定主题的兴趣(订阅),任何节点都可以在该主题上发送消息(发布),这些消息将被所有订阅该主题的节点接收。这种技术常用于将消息内容传输到整个网络。发布/订阅是分布式系统中消息传递的一种著名架构模式。
- 流复用 :前面提到P2P网络中的节点可以支持多种传输方式。流复用是一种在公共通信链路上发送多个信息流的方法。在P2P网络中,它允许多个独立的“逻辑”流共享一个公共的P2P传输层。当一个节点可能与不同的对等体有多个通信流,或者两个远程节点之间可能有许多并发连接时,这一点变得尤为重要。流复用有助于优化对等体之间建立连接的开销。在后端服务开发中,复用也很常见,客户端可以与服务器建立底层网络连接,然后在该底层网络连接上复用不同的流(每个流具有唯一的端口号)。

综上所述,我们对异步Rust编程中的自定义 future 和P2P网络的基础知识有了一定的了解。在后续内容中,我们将继续深入探讨如何使用 libp2p 库来构建P2P网络应用程序。

3. 理解libp2p网络的核心架构

编写自己的P2P应用程序网络层是一项艰巨的任务,因此我们可以使用一个名为 libp2p 的低级P2P网络库,它能让构建P2P应用程序变得更加容易。

libp2p 库是一个由协议、规范和库组成的模块化系统,可用于开发对等网络应用程序。截至目前, libp2p 支持三种编程语言:Go、JavaScript和Rust。它被许多流行项目使用,如IPFS、Filecoin和Polkadot。

libp2p 构建强大对等网络的关键模块如下表所示:
| 模块 | 功能 |
| ---- | ---- |
| 传输 | 负责数据从一个对等节点到另一个对等节点的实际传输和接收 |
| 身份 | 使用公钥加密(PKI)作为对等节点身份的基础,为每个节点生成唯一的对等ID |
| 安全 | 节点使用私钥对消息进行签名,传输连接可升级为安全加密通道,确保远程对等体相互信任,防止第三方拦截通信 |
| 对等发现 | 使对等节点能够在 libp2p 网络中找到并相互通信 |

下面是这些模块协作的流程图:

graph LR;
    A[传输] --> B[身份];
    B --> C[安全];
    C --> D[对等发现];
    D --> A;

总结

  • 异步Rust编程
    • 并发和并行是不同的概念,多线程和异步是两种并发编程模型。多线程使用操作系统原生线程,由操作系统进行任务调度;而异步使用异步运行时(如 Tokio ),通过轻量级的“绿色线程”在操作系统线程上调度多个任务。
    • 未来( Future )是一种异步计算,可在未来某个时间点返回值,它有 Poll::Pending Poll::Ready(future_value) 两种状态。当任务未准备好返回值时,会与 Waker 注册, Waker wake() 方法可通知异步执行器再次轮询该任务。
    • 通过实现自定义 Future (如 AsyncTimer ),我们可以更深入地理解异步编程的工作原理。
  • P2P网络
    • P2P网络与传统的客户端/服务器网络不同,它允许节点直接相互交互,具有无权限限制、容错和抗审查等优点。
    • 构建P2P系统涉及传输、对等体身份、安全、对等体路由、消息传递和流复用等技术要求。
    • libp2p 库为构建P2P应用程序提供了便利,其核心架构包含传输、身份、安全和对等发现等关键模块。

通过对异步Rust编程和P2P网络的学习,我们已经具备了构建各种异步应用程序或组件的基础。可以利用这些知识,以标准且易读(因此易于维护)的方式开发出高效、强大的分布式应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值