自 napster 20 年前以来,p2p 的应用一直层出不穷,一开始主要集中在音视频的文件共享领域。早期的 bitTorrent,电驴,后来的pplive,迅雷,快播等,都是走内容分享的路子。在这里,p2p 主要充当的是点对点下载资源块的角色,至于播放体验的优化,是另一回事。在分光了一段时间后,互联网进入到高带宽 + 海量 CDN 时代,这类的 p2p 软件渐渐式微,越来越小众。原因很简单 —— 通过高速网络和遍布全球各地的 CDN 节点,流媒体音视频的播放体验已经非常优秀,普通用户又何必退而求其次使用体验一般,片源不稳定的 p2p 软件呢(除了特殊需求)?
p2p 的另一个战场是即时通讯。QQ / MSN / Skype 等虽然不能算作完全的 p2p 软件,但大量使用了 p2p 技术来提升通讯的效率,以及避免不必要的带宽消耗。比如 Alice 和 Bob 通过后台确认过对方的在线状态后,两者之间完全可以建立点对点连接,之后的通讯完全可以在两者之间完成,消息无需服务器进行一道转手。当然,这样「白嫖」用户的带宽是有代价的,用户间传输的数据很难被「监控」到(如果每个消息都要往服务器转发一份,那 p2p 也没有多大意义了),于是相关的服务也无法根据这些数据去做各种各样的深度学习和分析,进而更好地提升用(zhuan4)户(qian2)体(neng2)验(li4)。所以随着互联网厂商越来越不差钱,对 p2p 技术的渴求也没那么强烈了。
区块链技术爆红后,p2p 又逐渐走回主流技术界的视野。早期的区块链,最核心的技术是两个部分:p2p 和共识算法。人们都觉得 p2p 是已经解决了的问题,把目光更多投在了共识算法。所以尽管必不可少,相对于层出不穷的当红炸子鸡 —— 共识算法,p2p 技术就像一个被冷落在角落里的老黄牛,只是在需要的时候,就被拉出来抽两鞭子。
到了区块链走向更广阔的领域,尤其是 IPFS 兴起后,大家才渐渐意识到,之前的项目,p2p 和共识算法绑得太紧,为每一种解决方案都设(chao1)计(xi2)一套 p2p,不符合 DRY 的伟大精神,于是在 IPFS 项目的牵头下,libp2p 被定义出来 —— 顾名思义,这是用来解决 p2p 网络层核心问题的一套库。有了 libp2p,开发者要实现一个基于 p2p 的解决方案,变得无比简单:即便是一个对 p2p 核心技术没有深入了解的人,也可以很快开发出一个 p2p 系统。可以这么说:libp2p 未来在 p2p 领域的历史地位,就像 webkit 在浏览器世界的历史地位一样。
在具体介绍 libp2p 之前,我们先看看 p2p 网络和我们熟悉的 client/server 网络的区别:
(图片来源:Telecommunication systems and technology[1])
libp2p 包含一系列协议的实现,这些协议共同作用,完成了:
(图片来源:A network framework for decentralized P2P application development [2])
对于开发者而言,相对于掌握这些协议的细节而言,更重要的掌握如何使用 libp2p 构造一个 p2p 网络,所以,我们需要掌握以下概念和它们的使用。
对于一个 p2p 网络,一个节点想让别人认识它并接受它的一个前提是它要有可以被识别的节点身份。这个就是 network identity。libp2p 使用公钥/私钥对来产生 network identity:私钥用于数据的签名,公钥作为 PeerId
。一般来说,一个节点在初始化之后,应该产生一个配置文件,保存节点的公钥私钥,以便节点以后反复运行时能够使用相同的身份。
前面我们提到,在 p2p 网络中,节点间传输协议的选择需要非常多样,这是因为网络中有可能运行着各种版本,甚至不同实现的节点,因而,支持一个范围广泛的传输协议供节点连接时协商,便变得非常必要。此外,p2p 网络需要额外的安全性,不仅仅是消息的加密,还有消息发送者的身份验证 —— 你可以将其类比成使能了 mutual auth 的 TLS:客户端验证服务器的证书来确保连接的是合法的服务器,而服务器同时也验证客户端的证书确保访问的是合法的客户端。
为了实现这一目的,libp2p 抽象出了 Transport
层,它负责传输协议的协商,包括使用什么样的传输协议,使用什么样的安全机制,以及如果做多路复用(stream multiplexing)。基本上,这对应了 ISO/OSI 模型的:传输层(比如用 TCP)和会话层(比如用 Noise + Yamux)。如果你使用过 websocket,那么你对一个 HTTP 连接 "upgrade" 成 websocket 并不陌生,libp2p 在这些层之间也是一层层 upgrade 的。我们看个例子:
let noise_keys = noise::Keypair::<noise::X25519Spec>::new()
.into_authentic(&keypair)
.unwrap();
let transport_layer = {
let tcp = tcp::TcpConfig::new().nodelay(true);
let dns_tcp = dns::DnsConfig::system(tcp).await?;
let ws_dns_tcp = websocket::WsConfig::new(dns_tcp.clone());
dns_tcp.or_transport(ws_dns_tcp)
};
let transport = transport_layer
.upgrade(Version::V1)
.authenticate(NoiseConfig::xx(noise_keys).into_authenticated())
.multiplex(YamuxConfig::default())
.timeout(Duration::from_secs(20))
.boxed();
这段代码并不难懂,非常简洁干练,没有 Rust 经验的人也能猜出来在干什么。但里面包含的信息量巨大:
可以看到,短短几行代码,我们就构建了一个非常安全(noise protocol),非常高效(yamux),以及非常灵活(tcp / websocket)的传输协议。
如果说 Transport
定义了如何在网络中传输数据,那么 NetworkBehaviour
则定义了在网络中要传输什么样的数据,或者说,你的 p2p 协议本身。你只需要专注于数据本身而不必考虑最终数据是如何加密,使用什么协议发送出去。我觉得这个设计非常优雅,它清晰地像我们展示了什么是 Separation of Concerns。
libp2p 自带了一系列 Network behaviour —— 上图中大部分的协议,都通过 Network behaviour 实现,我举几个例子:
除了 libp2p 默认实现的一系列 Network behaviour 外,你也可以创建新的 Network behaviour,并且把你的协议和已有的 behaviour 组合在一起。
要实现你自己的 Network behaviour,你需要:
UpgradeInfo
:这告诉 libp2p 你的协议的唯一标识符,比如:/ipfs/ping/1.0.0
。InboundUpgrade
和 OutboundUpgrade
:这是协议输入输出数据的处理。ProtocolsHandler
和 NetworkBehaviour
:协议的主要处理流程。上文提到 Network
定义了如何发送数据,NetworkBehaviour
定义了什么时候发送什么样的数据,但我们还缺一个中间人,把 NetworkBehaviour
要发送的数据交给 Network
发送出去,并且把 Network
收到的数据交给 NetworkBehaviour
处理。这个中间人就是 Swarm
。
我们用一段代码看看 Swarm
是如何驱动 Network
和 NetworkBehaviour
前进的:
let transport = ...; // 参见上文代码
let behaviour = Ping::new(PingConfig::new().with_keep_alive(true));
let mut swarm = Swarm::new(transport, behaviour, local_peer_id);
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
loop {
let event = swarm.next().await?;
println!("event: {:?}", event);
}
这里可以看到,Swarm
需要 Network
和 NetworkBehaviour
才能创建。swarm 可以监听一个特殊的地址 —— 这里 /ip4/0.0.0.0/tcp/0
是一个 IPFS 定义的 multiaddr,可以很方便地表述多层协议的地址。之后,我们可以通过 swarm.next()
来驱动 Swarm
处理数据。一般而言,Swarm
获取到的数据,已经交给 NetworkBehaviour
处理了,只有未定义的数据,才会被收到上文中的流程进行进一步处理。
现在 libp2p 主要被用来构建狭义的区块链项目,比如 substrate,filecoin 等,我觉得有些暴殄天物。其实 libp2p 还可以有更广阔的天地,比如在社交软件,工具软件,通讯软件,甚至电子商务等领域找到自己的位置。接下来的几篇文章,我们会进一步讲解 libp2p,并使用 rust-libp2p[3] 构建一个有意思的小工具。
[1] Telecommunication systems and technology: https://www.slideserve.com/myrrh/telecomunication-systems-and-technology-powerpoint-ppt-presentation
[2] A network framework for decentralized P2P application development: https://ipfs.io/ipfs/QmdzerM4fsnNGVf5jnogxu6QpXQa5rHDK87oHStC5xGCS4/
[3] rust-libp2p: https://crates.io/crates/libp2p