科普 | IPFS数据之旅

IPFS星际比特
IPFS星际比特 机构得得号

Nov 25, 2018 IPFS分布式存储引领者

摘要: 我们自己的数据在互联网中到处都是,但是我们却没办法很好的管理这些数据,在中心化网络和大数据面前,我们可以做些什么呢?

 

被称为下一代HTTP协议的IPFS,热度仅次于EOS。但很多人对IPFS只有一些很抽象的概念。在此为大家简单解释一下IPFS网络:“可以把IPFS网络想象成一个大硬盘,全世界就这一个硬盘,这个硬盘背后是无数个对等的点组成,我们存的文件会被冗余并且分片的存到某些点上。”

IPFS是一个内容寻址,有版本管理的,点对点文件系统。本系列说的IPFS包括协议实验室在github.com上ipfs和libp2p两个组织的各种项目,不包括Filecoin。工作关系一直追踪分布式存储IPFS的各种动态,分享下自己对IPFS技术的理解,会涉及到IPFS各种工具和相关代码。

设想下面几个情景:

(可以发现我们自己的数据其实在互联网中到处都是,但是我们却没办法很好的管理这些数据。)

情景1

小A同学给小B同学传一个照片,直接能想到的都是需要通过中心化的服务,比如微信,网盘等等方式,我们给不同人分享,意味着我们会在不同app/服务商中保存同一份数据,时间长了,搞不清哪里有想找的照片。

这里面就可以用到ipfs的内容寻址(content addressing),同一份内容永远只有一个地址。对于ipfs这样的一个点对点的网络,其实没有传统的上传和下载的概念。每个人都可以把文件保存在自己的ipfs节点中,然后把文件的地址(哈希值)广播出去,如果别人需要,会根据这个哈希值在他自己的节点保存。

情景2

如果小B对照片做了修改,比如去除红眼后,分享给小C。现在问题来了,小A怎么知道有最新的修改呢?小A“沟通基本靠吼”,直接问下小B或小C。如果是更多人协作呢?

ipfs有ipns(InterPlanetary Naming System)和pubsub(订阅,发布)可以帮忙做这个事情。

情景3

如果小A希望网络中有三个备份,要怎么做呢?已有方法是放到不同服务提供商,某度网盘存一份,微信收藏一份,telegram存一份……如果要存10份呢?

ipfs的ipfs-cluster可以帮忙做指定副本数量的存储。

以上情景都可以通过IPFS解决,从设计上看,IPFS包括用DHT存储metadata,使用类似的BT的数据交换方式,使用类似git的object管理和SFS(Self-Certified Filesystems)。

上述情景都可以通过IPFS解决,那技术上到底是如何解决的呢,请关注下期的IPFS数据之旅(二)

IPFS-companion
 

顾名思义,IPFS companion(IPFS伴侣)就是方便大家在浏览器中使用IPFS的各种功能。 它使用Javascript语言,支持本地的IPFS daemon或浏览器内置的基于JS的IPFS daemon(如下图)。 

它使用mozilla的WebExtensions API开发,后者是一个跨浏览器的浏览器扩展/插件开发API。

IPFS companion功能包括:

  • 上传文件
  • pin,unpin文件
  • 地址转换

对地址的转换,举个栗子。

对于http的地址

(https://ipfs.io/ipfs/QmUgmeDNfbP61hynZAQjvy81sGoQmLQRzEaG51knUS9rjk),

可以生成IPFS地址:

/ipfs/QmUgmeDNfbP61hynZAQjvy81sGoQmLQRzEaG51knUS9rjk,(这个命令可以通过ipfs get下载。笔者后续会详细介绍ipfs的数据流,敬请期待。)和普通浏览器可以访问的带ipfs 网关的地址:https://ipfs.io/ipfs/QmUgmeDNfbP61hynZAQjvy81sGoQmLQRzEaG51knUS9rjk。

笔者最喜欢的功能其实是直接在浏览器使用如下四种格式IPFS链接举例: 

例如下面图片 

 

可以用

ipfs://QmUgmeDNfbP61hynZAQjvy81sGoQmLQRzEaG51knUS9rjk访问。

使用浏览器内置的js daemon时,浏览器地址栏会显示:  

使用本地的ipfs daemon时,浏览器地址栏会显示:  

二者的含义都是把内容

/ipfs/QmUgmeDNfbP61hynZAQjvy81sGoQmLQRzEaG51knUS9rjk 

请求通过http api转发给ipfs.io或本地的127.0.0.1:8080 ipfs网关。ipfs网关把http请求转为ipfs的点对点协议。从上述ipfs网关开始,后面就是ipfs协议,而不是传统http协议了。

明正才能传顺

情景一中,小A想给小B传一个文件,大致过程如下:

  • 本地添加文件;
  • 把文件的hash给小B;
  • 小B根据文件hash在IPFS网络中得到这个文件。

这里有意不提上传和下载,点对点的系统并不是上传文件到服务器,再由别人下载。在IPFS中我们只能把文件上传到自己的IPFS节点或public gateway(公共网关),别人通过哈希值从IPFS网络中下载。

回忆一下前面提到的IPFS Stack,这次主要涉及到IPLD对象是如何管理,在每个节点是如何存储的。

添加文件

我们向自己的IPFS添加文件的过程如下:

1. 文件拆分并写入IPFS基础数据结构dag(有向无环图),dag不仅用在IPFS中也用在Filecoin中,参考下文:深度解读IPFS/Filecoin 2018 update

2. 把dag数据根据系统配置写入IPFS底层blocks存储;

3. Flush mfs(Mutable Filesystem)的root目录,MFS是一个建立在IPFS上面的虚拟文件系统,提供类似unix文件系统的API;

4. 递归Pin上面flush过的root目录及其所有文件,并把状态写入底层blockstore存储。

flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。 一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。

做存储的小伙伴肯定这里会问了,我存1M的数据,肯定要比1M多啊,浪费的空间怎么办?IPFS社区有实验特性Filestore,可以避免浪费多余的硬盘空间。 

文件拆分并生成dag

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ export IPFS_PATH=~/.ipfs-private

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ export LIBP2P_FORCE_PNET=1

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ ipfs add --cid-version 1 --wrap-with-directory test_data

added zdj7WkayJ4Y7QmFsHg6pQQ528J7bqSZpGfUk8WfivrNFJyUr6 test_data

added zdj7WXi97o1NcWTq9bVw9BhJLMxEVKjUVbXP1U3V2TBHswPvr

 512.00 KiB / 512.00 KiB [======================================================================================================================] 100.00%

graphmd可以把dag用图形化方式显示,例如

https://ipfs.io/docs/examples/example-viewer/example#../graphmd/README.md的

 

文件拆分策略

文件拆分方式可以通过--chunker指定。有固定大小和rabin两种方式。如果不指定,默认是256k一个chunk。

dag (有向无环图)的类型

ipfs的object来自git。ipfs支持两种dag,一种是balanced dag,适合随机访问;另一个种是trickledag,适合顺序访问。默认情况下ipfs使用balanced dag。ipfs添加文件时可以用--trickle或-t表示生成trickledag。下面是balanced dag插入数据的过程。

 

trickledag是这样的:

 

添加新的子树:

 

需要注意的是使用不同dag或不同的拆分方式都会生成不同的哈希值,也就是对ipfs来说,是不同的文件。

CID版本

CID包括multibase,multicodec和multihash。

默认是cidv0,社区正在做cidv1的工作。有个小工具可以将cid解析成人类可读的数据。

dag写入底层存储:

ipfs默认使用两种存储,在~/.ipfs/blocks使用flat fs,在~/.ipfs/datastore使用leveldb。后者在后面提到pin的时候仍然会提到,这里先跳过。在上面的默认配置中,ipfs会使用base32哈希值的倒数第二和倒数第三位索引。

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ hexdump  ~/.ipfs-private/blocks/RX/AFKREIHCUSVW2OVNVS7VX35P33XIYNO6AF4TXB7R7CNAF5YZSGULUB3RXQ.data | head

0000000 38b9 a7c4 061f b138 6dca b81a 5aeb a304

0000010 99e6 2ce7 600b d190 f5da 0695 7436 efd3

0000020 97bd 357a 9fa6 e8ab 422d a7d6 051c 4ba1

0000030 6be3 ad89 f3d4 f6d4 72a4 0b94 5a1e 8f61

0000040 2953 1ad3 a5f4 3f30 477a a907 bd65 b6fa

0000050 9658 4883 54fd 318b 6e51 85c3 1772 65d5

0000060 7c6a 3202 2e4b bc48 54b6 1aca 927e 6d7c

0000070 c34d 522e faa1 3883 9bda f5bf d6e7 e1d1

0000080 5d90 4548 7cf2 5c4d e501 9a8e a747 0bdd

0000090 fa7d a186 1565 5839 93b7 0ad3 2c18 bd48

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ hexdump test_data | head

0000000 38b9 a7c4 061f b138 6dca b81a 5aeb a304

0000010 99e6 2ce7 600b d190 f5da 0695 7436 efd3

0000020 97bd 357a 9fa6 e8ab 422d a7d6 051c 4ba1

0000030 6be3 ad89 f3d4 f6d4 72a4 0b94 5a1e 8f61

0000040 2953 1ad3 a5f4 3f30 477a a907 bd65 b6fa

0000050 9658 4883 54fd 318b 6e51 85c3 1772 65d5

0000060 7c6a 3202 2e4b bc48 54b6 1aca 927e 6d7c

0000070 c34d 522e faa1 3883 9bda f5bf d6e7 e1d1

0000080 5d90 4548 7cf2 5c4d e501 9a8e a747 0bdd

0000090 fa7d a186 1565 5839 93b7 0ad3 2c18 bd48

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ cat  ~/.ipfs-private/blocks/KE/AFKREIBOBL5SRHO6FXSR2KPV7TCNEQFPIL2CHLI7226YE2446Q5A7KEKEY.data

welcom to ipfsbit.com

前面我们通过IPFS add添加了”test_data“文件

上文是拿“welcom to ipfsbit.com“这一句话举个栗子,通过flat fs方式存储这句话返回的哈希值是”bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ cat  ~/.ipfs-private/blocks/KE/AFKREIBOBL5SRHO6FXSR2KPV7TCNEQFPIL2CHLI7226YE2446Q5A7KEKEY.data“而图中的KE就是倒数第二和倒数第三位的索引。

Append(追加数据)

IPFS做为内容寻址的文件系统。Append数据之后,只有被修改的chunk会被生成新的hash,原有chunk仍然可以用原有hash访问。例如我们在上面的测试数据test_data后面追加"welcom to ipfsbit.com"字符串。由于原本的test_data是512k,ipfs默认是256k一个chunk,正好是两个chunk。新增的数据会在一个新建的chunk里面。我们可以用ipfs object get看到前面两个chunk数据没有变化。只是最后一个chunk包含我们新增的字符串。

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ echo "welcom to ipfsbit.com" >> test_data

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ ipfs add --cid-version 1 --wrap-with-directory test_data

added zdj7We9xdpUKDjQxCXYJ2DMg2YJD87NmuybjoTDxptbitB2cd test_data

added zdj7WfuHAfPSdyS2XL7avfzYudTaYT83ckG1ku1gdv8H96sg7

 512.02 KiB / 512.02 KiB [======================================================================================================================] 100.00%

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ ipfs object get zdj7WkayJ4Y7QmFsHg6pQQ528J7bqSZpGfUk8WfivrNFJyUr6

{"Links":[{"Name":"","Hash":"zb2rhmtzy5H4y7UQxZ3pzf3FE6BAE5PAeLT3yjTrKX5RCcBvj","Size":262144},{"Name":"","Hash":"zb2rhaMcpAWL6rvYay5HX7wsuj6gKnZTPk2ToF5xnf7vtnf6G","Size":262144}],"Data":"\u0008\u0002\u0018\ufffd\ufffd  \ufffd\ufffd\u0010 \ufffd\ufffd\u0010"}

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ ipfs object get zdj7We9xdpUKDjQxCXYJ2DMg2YJD87NmuybjoTDxptbitB2cd

{"Links":[{"Name":"","Hash":"zb2rhmtzy5H4y7UQxZ3pzf3FE6BAE5PAeLT3yjTrKX5RCcBvj","Size":262144},{"Name":"","Hash":"zb2rhaMcpAWL6rvYay5HX7wsuj6gKnZTPk2ToF5xnf7vtnf6G","Size":262144},{"Name":"","Hash":"zb2rhZk1fBPjs9ho7beGQ2itVcETXJ7RYkJmsWpeyXGhvLu8y","Size":22}],"Data":"\u0008\u0002\u0018\ufffd\ufffd  \ufffd\ufffd\u0010 \ufffd\ufffd\u0010 \u0016"}

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ ipfs get zb2rhZk1fBPjs9ho7beGQ2itVcETXJ7RYkJmsWpeyXGhvLu8y

Saving file(s) to zb2rhZk1fBPjs9ho7beGQ2itVcETXJ7RYkJmsWpeyXGhvLu8y

 22 B / 22 B [===============================================================================================================================] 100.00% 0s

bamvor@ipfs-ci-and-test:~/works/ipfs_demo$ cat zb2rhZk1fBPjs9ho7beGQ2itVcETXJ7RYkJmsWpeyXGhvLu8y

welcom to ipfsbit.com

备注

这里面没有提到cache,默认是ARCCache。如果配置了BloomFilter,也会开启BloomFilter。

上面流程只考虑了单独添加一个文件的情况,没有考虑符号链接,目录的情况。

(作者:IPFS星际比特,内容来自链得得内容开放平台“得得号”;本文仅代表作者观点,不代表链得得官方立场)

链得得仅提供相关信息展示,不构成任何投资建议
本文系作者 IPFS星际比特 授权链得得发表,并经链得得编辑,转载请注明出处、作者和本文链接

更多精彩内容,关注链得得微信号(ID:ChainDD),或者下载链得得App

分享到:

相关推荐

    评论(0

    Oh! no

    您是否确认要删除该条评论吗?

    分享到微信