简介
Bitswap是IPFS的数据交易模型。它的目的是用来从其他节点请求或发送数据。Bitswap有两个主要的功能
- 尝试从网络中获取已被客户端请求的数据。
- 发送自己所拥有的数据给需要的节点。
介绍
Bitswap是IPFS的中心块交换协议。它处理IPFS用户、普通用户以及应用的请求,从网络中获取数据。它用来跟其他节点的Bitswap相互通信来交换彼此所需的数据。
Bitswap是基于消息的协议,而不是回复。所有的消息包含需求清单、数据块。在接收到需求清单之后,IPFS节点或发出其拥有的数据给需求方。当接收到数据块之后,节点应该发送一个Cancel通知表明它不再需求相应的数据块。在协议层Bitswap是相当简单。
尽管Bitswap是一个相当简单的协议,考虑到时间和内存性能,也有很多值得关注的点。我们的目的是在这里尽量详细的记录这些,为了将来实现的时候可以不断迭代。
子系统
在Bitswap管理中两个主要的流程: 从其他节点请求数据和发送数据给其他节点。 数据块请求主要由需求管理员来调节,它告诉节点是否我们需要新的数据块。发送数据块主要由决策引擎处理,它决定了如何在节点之间进行资源分配。
类型
如下描述的类型是Bitswap子系统中描述的。
- CID: 使用特定数据块的内容寻址标识符
- Peer: 链接的另外的Bitswap实例
- Block: 二进制blob
- Message: Bitswap 消息
- Entry:在需求列表中,添加/删除特定的CID时可能包含在消息中的需求列表,其中包括
- CID 一个特定的块
- Priority 用户想要的CID的优先级
- Cancel 一个bool值,旨在从我们的需求列表中删除CID。
Ledger:两个节点之间数据交换的聚合值。每个节点为其他节点存储一个Ledger。
Bitswap消息
单个Bitswap消息可能包含以下任意内容
- 发件人的需求清单。此需求列表可能是发件人完整的需求列表,也可能仅仅是接收者需要知道的需求列表。
- 数据块。这些是接收者请求的数据块。
消息格式
message Message{
message Wantlist {
message Entry{
optional string block = 1; // the block key
optional int32 priority = 2; // the priority (normalized). default to 1
optional bool cancel = 3; // whether this revokes an entry
}
repeated Entry entries = 1; // a list of wantlist entries.
optional bool full = 2; // whether this is the full wantlist. default to false.
}
optional Wantlist wanlist = 1;
repeated bytes blocks = 2;
}
需求管理者
需求管理者处理块请求。对于请求的块,通过cid认证,Bitswap.GetBlock(cid)方法被调用。Bitswap.GetBlock(cid)从网络中请求cid,如果收到相应的块,则返回。更具体来讲,Bitswap.GetBlock(cid)添加cid到需求列表中。需求管理者通过向每个节点的消息队列中添加条目来更新所有节点。
决策引擎
决策引擎如何在节点之间分配资源。当接收到一个需求列表时,消息会发送给决策引擎。对每一个需求列表中的CID,都有相应的块,块有一个任务添加到任务队列。一旦块被发送到节点的消息队列中,我们认为任务已经完成了。
在决策引擎中主要的数据结构是节点请求队列(PRQ)。PRQ添加节点到加权队列中,其中权重基于对一个或者多个节点的度量。这也是Bitswap策略的来源。当前,策略是一个函数,它的输入是ledger,输出是节点的权重。然后节点在各自的任务队列中提供任务。在给定的循环轮次中,每个节点的数据量由它们在队列中的秀昂队权重确定。
消息队列
每一个激活的节点都有一个相应的消息队列。消息队列保存要发送给节点的下一条消息。消息队列从其他的子系统中接收更新。
- 需求管理者:当CID从需求列表添加或删除时,我们必须更新节点。这些需求列表的更新被发送到其他所有节点的消息队列中。
- 决策引擎:当我们拥有节点需要的块,并且决策引擎决定发送这个块,我们广播这个块到其他节点的消息队列。
任务工作者观察消息队列,将消息从队列中出列,并且发送给相应的接收者。
网络
网络是表示通过一个或多个链接到我们的所有Bitswap节点的抽象。Bitswap消息流流入或流出的网络。在一个任意的网络中,我们必须假设我们所有的同伴都是理性的和自私的。
实现细节
消息合并
当已包含Bitswap消息的消息队列收到另一个消息队列时,新的消息应该与原消息合并,以减少发送单独数据包的开销。
Bitswap会话
Bitswap会话是尝试优化发送到其他Bitswap客户端的块请求。当从网络中请求块图时,我们发送一个需求清单去更新包含根块给所有的节点。对于每个发送根块的节点,我们将此节点添加到图的激活集中。然后我们将图中的其他节点的所有请求发送到活动集中的节点。拥有图根节点的节点可能也会拥有其子节点,同时没有根节点的节点可能没有子节点。
实施建议
- 维护一组“在线合作”节点
- 协议侦听器接受伙伴接受消息流
- 协议发送方向合作伙伴打开流发送消息
- 分离出一个决策引擎,选择将哪些块、在何时、发送给哪些节点。
Sender
- 打开bitswap流
- 发送一个或多个bitswap消息
- 关闭bitswap流
Listener
- 接受bitswap流
- 接受一个或多个bitswap流
- 关闭bitswap流
Events
- bitswap.addedBlock(block)
- 观察是否有任何节点需要此块,并发送
- bitswap.getBlock(key, cb)
- 添加到需求列表
- 可能会发送需求列表更新到其他节点
- bitswap.cancelGet(key)
- 需求列表取消
- bitswap.receivedMessage(msg)
- 处理需求列表变更
- 处理块
- bitswap.peerConnected(peer)
- 添加节点
- bitswap.peerDisconnected(peer)
- 移除节点
Tricky Bits
- 客户端bitswap可能会调用getBlock 然后cancelBlock
- 合作伙伴可能会发送垃圾邮件
- 针对每个用户的标准化优先级
Modules
- bitswap-decision-engine
- bitswap-message
- bitswap-net
- bitswap-wantlist
Notes
var bs = new BlockService(repo, bitswap)
bs.getBlock(multihash, (err, block)=>{
// try to fetch from repo
// if not -> ask bitswap
// bitswap will cb() once the block is back, once.
// bitswap will write to the repo as well.
})