题记
之前一直关于web 3.0一直有个疑问,2.0时代的痛点之一是”数据为平台所有,而非用户所有”,所以用户被平台绑架,只能被迫接受平台的规则,比如微信数据不能迁移到支付宝,直到看到了IPFS,让我找到了这个问题的答案。
概念背景
IPFS又称星际文件系统,web 3.0底层存储重构解决方案组成之一,通过结合类似FileCoin区块链奖励的方式激励每个用户的计算机参与到分布式存储网络中,最终构建成世界上最大的分布式存储系统。与传统网络不同的是,不存在中心化单点存储带来的风险,能够最大化的利用全球网络的带宽,提升传输的效率。
再谈谈ipfs所在公司Protocol Labs 目前包括五个项目,项目彼此独立又联系,就目前来说,这个家族成员都在为了更安全、高效、开放的网络而努力。它们分别是:
● IPFS:星际文件系统,一个旨在创建持久且分布式存储和共享文件的网络传输协议。
● FileCoin:IPFS 的激励项目,一个加密货币驱动的存储网络。矿工通过为分布式存储网络提供开放的硬盘空间,赚取 Filecoin;用户则花费 Filecoin,将文件加密存储于去中心化网络。
● Libp2p: 模块化的网络堆栈项目。libp2p 汇集了各种传输和点对点协议,使开发人员可轻松构建大型,稳定的p2p网络。
● IPLD:分布式网络数据模型. 它通过加密哈希算法连接数据,以更好实现数据交换和链接。
● Multiformats:安全系统的协议集合,自我描述格式可以让系统互相协作和升级。
IPFS 系统架构
IPFS 采取分层模块化的设计方案,每个模块都可以有多种实现。IPFS 一共分为5层:
● naming 基于 PKI 的一个命名空间(IPNS)。
● merkledag IPFS 数据结构格式
● exchange 节点之间数据的交换和复制
● routing 用于节点和对象寻址
● network 用于节点的连接和数据的传输
1.3.1 IPFS 网络层
网络层负责在IPFS节点之间提供点对点传输(可靠或者不可靠)。 它的任务主要是:
● NAT穿透: UDP和TCP打洞、端口映射或者 TURN的方式。
● 支持多种网络协议: TCP, SCTP, UTP,
● 支持数据加密,签名和信道释放
● 支持多路复用:复用连接,流,协议,节点
1.3.2 IPFS 路由层
IPFS 路由层有两个重要目的:
● 节点寻址:寻找其他节点
● 内容寻址:寻找发布在网络中的内容
路由层可以针对不同的场景采用不同的实现,例如:
● DHT:在网络中采用分布式的方式缓存路由记录
● mdns:用于本地查找
● snr:采用委派寻址,委托给超级节点,让它来帮助寻址
● dns:IPFS 可以在 dns 之上进行寻址
1.3.3 IPFS 交换层
IPFS 交换层负责数据的传输,一旦节点互相连接上,交换协议就会开始对数据的传输进行控制。同样的交换层也可以有多种实现方式:
● Bitswap:IPFS 的主要交换协议
● HTTP:基于 http 协议实现的简单数据交换方式
1.3.4 IPFS merkledag
Merkle DAG拥有如下的功能:
● 内容寻址:使用多重哈希来唯一识别一个数据块的内容
● 防篡改:可以方便的检查哈希值来确认数据是否被篡改
● 去重:由于内容相同的数据块哈希是相同的,可以很容去掉重复的数据,节省存储空间
在 IPFS 网络中,存储文件时,首先会将文件切片,切割成 256KB 大小的文件。之后循环调用MerkleDAG.Add方法构建文件 MerkleDAG。 文件 hash 值创建流程:
● 将切片之后的文件进行sha-256运算
● 将运算结果选取0~31位
● 将选取结果根据base58编码,运算结果前追加 Qm 即为最后结果,作为文件的 Hash 值
1.3.5 IPFS naming
IPFS 基于内容寻址,文件被切分成多个 block, 每个 block 通过 hash 运算得到唯一的 ID, 方便在网络中进行识别和去重。考虑到传输效率, 同一个 block 可能有多个 copy, 分别存储在不同的网络节点上。 每个 block 都有唯一的 ID,我们只需要根据节点的 ID 就可以获取到它所对应的 block。
在 IPFS 中,一个文件的 Hash 值完全取决于其内容,修改它的内容,其相应的 Hash 值也会发生改变。如果我们把修改前后的文件都添加到 IPFS 网络中,那么我们将可以通过这两个 Hash 值访问到前后两个版本的内容。这种静态特性有利于提高数据的安全,比如 张三可以将一份自己签名(私钥加密)的文件放到 IPFS 中,那么即使她后来对文件进行了修改并重新签名和发布,那么之前的文件依然存在,她不能抵赖曾经发布过老版本的文件。但对于一些需要保持动态性的文件来说,比如网页,在新版本出现后,旧版本的内容将毫无意义。并且,总不能要求网页访问者每次要在浏览器中输入不同的 IPFS 地址来访问不同时期的网页吧。
IPNS(Inter-Planetary Naming System)提供了一种为文件增加动态性的解决方案。它允许节点的 PeerID 限定的命名空间提供一个指向具体 ipfs 文件(目录) Hash 的指针,通过改变这个指针每次都指向最新的文件内容,可以使得其他人始终访问最新的内容。
数据路由检索数据结构(DHT)
传统散列表主要作用在单机的某个应用系统,分布式散列表主要是作用在分布式应用系统中,每个节点可以类比成传统散列表的bucket。在实际使用场景中,直接对所存储的每一个业务数据计算散列值,然后用散列值作为key,业务数据本身是value。针对ipfs的场景来说,散列值就是文件拆分后的block的哈希值。
想了解DHT的出现原因,需要先回顾下P2P文件检索发展史的三个阶段。
第一代:
采用中央服务器模式,每个节点都需要跟中央服务器进行连接,再进行数据检索,弊端是单点服务器压力十分庞大,风险高,单点故障会导致整个业务系统不可用。
第二代:
采用广播的模式,要找文件的时候,每个节点都向自己建连的所有节点进行询问;被询问的节点如果不知道这个文件在哪里,就再次进行广播,直至找到所需文件。这种检索模式会引发广播风暴并严重占用网络带宽,也会严重消耗节点的系统资源,同时同个节点会受到多个相同文件的检索请求。即使在协议层面通过设置 TTL,限制查询过程只递归N轮,依然无法彻底解决此弊端。
第三代:DHT
DHT的诞生核心是为了解决两个问题:单点问题和检索效率。通过分布式散列表将数据拆分同步至互联网多个存储节点(同一份数据会备份到多个节点),通过距离算法来提升路由效率。
DHT的协议有很多种,如chord、kademlia等,各种协议的核心都是围绕着下面几个点展开设计:
1.哈希算法(数据 -> DHT的key)
2.路由机制(如何更高效的通过哈希key找到数据所在的存储节点)
3.距离算法(计算不同key之间的距离,路由机制中会使用)
下面以chord和kademlia为例深入讲解一下上面三个点(感兴趣可以看一下)
chord
拓扑结构采用的是一致性哈希的设计。
Chord 主要是为了解决节点动态变化的难点。为了解决这个难点,一致性哈希把哈希值空间(keyspace)构成一个环。对于m比特的散列值,其范围是 [0, 2^m-1]。把这个区间头尾相接就变成一个环,其周长是 2^m。然后对这个环规定了一个移动方向(比如顺时针)。
假设有某个节点A,距离它最近的是节点B(以顺时针方向衡量距离)。那么称 B 是 A 的继任(successor),A 是 B 的前任(predecessor)。
数据隶属于距离最小的节点。以 m = 6 的环形空间为例:
数据区间 [5,8] 隶属于节点8
数据区间 [9,15] 隶属于节点15
…
数据区间 [59,4] 隶属于节点4(注:6 比特的环形空间,63之后是0)
哈希算法: SHA-1
路由算法,简单遍历,收到请求时查看数据是否在当前节点,不在的话请求转发给后继者节点进行检索,以此类推。复杂度是O(n),相对低效,也就不涉及距离算法了。
kademlia
同chord不同的是,kad算法将散列值(SHA1)映射到一颗二叉树上,流程如下:
先把key以二进制形式表示,然后从高位到低位依次处理。
二进制的第n个 bit 就对应了二叉树的第n层
如果该位是 1,进入左子树,是 0 则进入右子树(这只是人为约定,反过来处理也可以)
全部数位都处理完后,这个key就对应了二叉树上的某个叶子
距离算法:异或
路由机制
核心流程:数据哈希 -> 转二进制 -> 提交任一节点查找 -> 节点内通过K-桶及二叉树递归遍历找到该节点 -> 转发 -> 数据返回
K-桶(K-bucket)是什么?
每个节点在完成子树拆分后,只需要知道每个子树里面的一个节点,就足以实现全遍历。但是考虑到健壮性(分布式系统的节点随时动态变化),只知道一个显然是不够,需要知道多个才比较保险。
Kad 论文中给出了一个K-桶(K-bucket)的概念。也就是说:每个节点在完成子树拆分后,要记录每个子树里面的K个节点。这里所说的K值是一个系统级的常量。由使用 Kad 的软件系统自己设定(比如 BT 下载使用的 Kad 网络,K 设定为 8)。
K-桶其实就是路由表对于某个节点而言,如果以它自己为视角拆分了n个子树,那么它就需要维护 n个路由表,并且每个路由表的上限是K。
下面演示了节点0011如何通过连续查询来找到节点1110的。节点0011通过在逐步底层的子树间不断学习并查询最佳节点,获得了越来越接近的节点,最终收敛到目标节点上:
首先,需要知道请求发起节点 0011 和 目标节点 1110 的 距离 n ,然后从 请求节点 0011 的本地 k-bucket 的 n 桶中返回 k个 节点信息,然后 0011 再给这几个节点广播,如果 这几个节点中刚好有距离是 n 的节点,那么就会用 0011 ,否则,就从这些节点的k-bucket 中继续上述操作,直到找到 1110 节点,所以这个过程是个递归的过程。
对整个文件进行分片(按照256KB大小),分片后对每片数据进行哈希,哈希后将数据通过
路由寻址,类似redis,通过libp2p不止两次,通过距离算法不断在一致性哈希环递归找到下一个最近的node查找文件
http之前基于文件位置路由寻址,ipfs基于文件哈希寻址,相当于通过内容找内容
bitswap文件交换
解决什么问题?
由于ipfs对文件分成多个block处理,block可能是存在一个节点,也可能是散布在多个节点,bitswap主要是解决如何从多个节点高效获取全部数据的问题。是对DHT的功能进行接口封装,
FileCoin
filecoin是ipfs的补充协议,ipfs缺乏对用户的激励,IPFS 作为一个开源项目已经被很多系统在使用, 它允许节点之间相互请求,传输,和存储可验证的数据,但是节点之间并没有形成一个统一的网络,各个节点都是各自存储自己认为重要的数据, 没有简单的方法可以激励他人加入网络或存储特定数据。
一个很重要的问题:ipfs如何保证数据不丢?
与filecoin激励机制相结合,用的是Erasure coding的模式,即M+N的模式,M是原文件的份数,N是备份的份数。IPFS会将文件切割发到不同的矿工手里,防止局部网络的瘫痪,对全局文件安全性的影响。
使用过程(ipfs上传和下载的demo)
1.下载解压安装
2.ipfs init
PS D:\software\kubo> ./ipfs init
generating ED25519 keypair…done
peer identity: 12D3KooWL9Lm16KXgEKNSSzsHpMnsMKTDBreaowjYZTjJ88vtH5a
initializing IPFS node at C:\Users\Administrator.ipfs
to get started, enter:
ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
ipfs deamon
启动ipfs客户端
启动之后可本地通过客户端进行数据上传,节点查看等
http://127.0.0.1:5001/ipfs/bafybeibozpulxtpv5nhfa2ue3dcjx23ndh3gwr5vwllk7ptoyfwnfjjr4q/#/
ipfs add 文件路径
文件上传
ipfs get cid
文件下载
文件hash:QmU6QpUqfsoFmLDdnkDSMs7VZGyCFYAUUqieTLWXmNRxMq
CID:QmU6QpUqfsoFmLDdnkDSMs7VZGyCFYAUUqieTLWXmNRxMq
总结
其实整个分布式存储网络的核心流程很简单:
存储:文件拆分 -> 哈希 -> bitswap分发到p2p网络中 -> 服务器节点本地数据库存储索引和文件
只不过ipfs在各个环节上尽力做到了极致,比如通过二叉树取代原有的遍历检索,和将node id 和 data key同构,提升检索效率。
类似ipfs的p2p文件传输系统其实早在多年前就已经存在了,比如BitTorrent协议,个人认为让整个p2p传输爆火的原因是2020年诞生的FileCoin,它是ipfs的激励层,用户因为有了激励而主动参与到生态建设中,让整个类似P2P的分布式存储网络更加完善。
另一点体会是Protocol Labs 旗下包括ipfs在内的五个项目,五个项目将整个系统进行模块划分,系统的低耦合性质让子模块可以很好的自我进化,最终五个项目组合在一起形成庞大的分布式存储网络。
未来整个系统优化的方向还有很多,比如如何选择更合理的切割范围以便节省存储以及提升传输效率,filecoin通过什么方式可以更好的平衡惩罚和激励等等。
个人猜想
ifps是对标http的文件传输系统,个人有个大胆猜想,未来可能不会再有(或弱化)集中化的阿里云、腾讯云、华为云这些集中的云服务器厂商,每个用户的计算机都是云的一部分,通过激励机制让每个用户都参与到云的共建中来。
参考文档
https://www.hyperchain.cn/news/262.html
https://zhuanlan.zhihu.com/p/132514928
https://zhuanlan.zhihu.com/p/149738588
https://www.zhihu.com/people/yin-qing-kuang-ji/posts?page=43