写点什么

两个模块,从数据的角度带你深入了解 IPFS

2020 年 2 月 24 日

两个模块,从数据的角度带你深入了解IPFS


IPFS 和区块链有着非常紧密的联系, 随着区块链的不断发展,对数据的存储需求也越来越高。本文从 IPFS 的底层设计出发, 结合源代码, 分析了 IPFS 的一些技术细节。


一、概述

IPFS 和区块链有着非常紧密的联系, 随着区块链的不断发展,对数据的存储需求也越来越高, 由于性能和成本的限制,现有的区块链设计方案大部分都选择了把较大的数据存储在链外,通过对数据进行加密, 哈希运算等手段来防止数据被篡改, 在区块链上只引用所存数据的 hash 值, 从而满足业务对数据的存储需求。 本文从 IPFS 的底层设计出发, 结合源代码, 分析了 IPFS 的一些技术细节。 由于 IPFS 还在不断更新中, 文中引用的部分可能和最新代码有所出入。


阅读本文需要读者


了解网络编程


了解分布式存储


了解基本的区块链知识


二、什么是 IPFS?

维基百科上是这样解释的:是一个旨在创建持久且分布式存储和共享文件的网络传输协议。


上面的解释稍显晦涩, 我的理解是:


  1. 首先它是一个 FS(文件系统)

  2. 其次它支持点对点传输


既然是文件系统, 那它和普通的文件系统有什么区别呢? 有以下几点区别:


  • 存储方式: 它是分布式存储的, 为了方便传输,文件被切分成多个 block, 每个 block 通过 hash 运算得到唯一的 ID, 方便在网络中进行识别和去重。 考虑到传输效率, 同一个 block 可能有多个 copy, 分别存储在不同的网络节点上。

  • 内容寻址方式: 每个 block 都有唯一的 ID,我们只需要根据节点的 ID 就可以获取到它所对应的 block。


那么问题来了, 既然文件被切分成了多个 block,如何组织这些 block 数据,组成逻辑上的文件呢? 在 IFPS 中采用的 merkledag, 下面是 merkledag 的一个示意图:



简单来说, 就是 2 种数据结构 merkle 和 DAG(有向无环图)的结合, 通过这种逻辑结构, 可以满足:


  • 内容寻址: 使用 hash ID 来唯一识别一个数据块的内容

  • 防篡改: 可以方便的检查哈希值来确认数据是否被篡改

  • 去重: 由于内容相同的数据块哈希是相同的,可以很容去掉重复的数据,节省存储空间


确定了数据模型后, 接下来要做的事: 如何把数据分发到不同的网络节点上, 达到分布式存储和共享的目的? 我们先思考一下, 通过网络,比如 HTTP, 访问某个文件的步骤,首先我们要知道存储这个文件的服务器地址, 然后我们需要知道这个文件对应的 ID, 比如文件名。前者我们可以抽象成网络节点寻址, 后者我们抽象成文件对象寻址; 在 IPFS 中, 这两种寻址方式使用了相同的算法, KAD, 介绍 KAD 算法的文章很多,这里不赘述, 只简单说明一下核心思想:


KAD 最精妙之处就是使用 XOR 来计算 ID 之间的距离,并且统一了节点 ID 和 对象 ID 的寻址方式。 采用 XOR(按比特异或操作)算法计算 key 之间的“距离”。


这种做法使得它具备了类似于“几何距离”的某些特性(下面用 ⊕ 表示 XOR)


  • (A ⊕ B) == (B ⊕ A) XOR 符合“交换律”,具备对称性。

  • (A ⊕ A) == 0 反身性,自身距离为零

  • (A ⊕ B) > 0 【不同】的两个 key 之间的距离必大于零

  • (A ⊕ B) + (B ⊕ C) >= (A ⊕ C) 三角不等式


通过 KAD 算法,IPFS 把不同 ID 的数据块分发到与之距离较近的网络节点中,达到分布式存储的目的。


通过 IPFS 获取文件时,只需要根据 merkledag, 按图索骥,根据每个 block 的 ID, 通过 KAD 算法从相应网络节点中下载 block 数据, 最后验证是否数据完整, 完成拼接即可。


下面我们再从技术实现的角度做更深入的介绍。


三、IPFS 的系统架构


我们先看一下 IPFS 的系统架构图, 分为 5 层:


  • 一层为 naming, 基于 PKI 的一个命名空间;

  • 第二层为 merkledag, IPFS 内部的逻辑数据结构;

  • 第三层为 exchange, 节点之间 block data 的交换协议;

  • 第四层为 routing, 主要实现节点寻址和对象寻址;

  • 第五层为 network, 封装了 P2P 通讯的连接和传输部分。


站在数据的角度来看, 又可以分为 2 个大的模块:


IPLD( InterPlanetary Linked Data) 主要用来定义数据, 给数据建模;


libp2p 解决的是数据如何传输的问题。


下面分别介绍 IFPS 中的 2 个主要部分 IPLD 和 libP2P。


IPLD

通过 hash 值来实现内容寻址的方式在分布式计算领域得到了广泛的应用, 比如区块链, 再比如 git repo。 虽然使用 hash 连接数据的方式有相似之处, 但是底层数据结构并不能通用, IPFS 是个极具野心的项目, 为了让这些不同领域之间的数据可互操作, 它定义了统一的数据模型 IPLD, 通过它, 可以方便地访问来自不同领域的数据。



前面已经介绍数据的逻辑结构是用 merkledag 表示的, 那么它是如何实现的呢? 围绕 merkledag 作为核心, 它定义了以下几个概念:


  • merkle link 代表 dag 中的边

  • merkel-dag 有向无环图

  • merkle-path 访问 dag 节点的类似 unix path 的路径

  • IPLD data model 基于 json 的数据模型

  • IPLD serialized format 序列化格式

  • canonical 格式: 为了保证同样的 logic object 总是序列化为一个同样的输出, 而制定的确定性规则


围绕这些定义它实现了下面几个 components


  • CID 内容 ID

  • data model 数据模型

  • serialization format 序列化格式

  • tools & libraries 工具和库

  • IPLD selector 类似 CSS 选择器, 方便选取 dag 中的节点

  • IPLD transformation 对 dag 进行转换计算


我们知道,数据是多样性的,为了给不同的数据建模, 我们需要一种通用的数据格式, 通过它可以最大程度地兼容不同的数据, IPFS 中定义了一个抽象的集合, multiformat, 包含 multihash、multiaddr、multibase、multicodec、multistream 几个部分。


multihash


自识别 hash, 由 3 个部分组成,分别是:hash 函数编码、hash 值的长度和 hash 内容, 下面是个简单的例子:



这种设计的最大好处是非常方便升级,一旦有一天我们使用的 hash 函数不再安全了, 或者发现了更好的 hash 函数,我们可以很方便的升级系统。


multiaddr


自描述地址格式,可以描述各种不同的地址



multibase


multibase 代表的是一种编码格式, 方便把 CID 编码成不同的格式, 比如这里定义了 2 进制、8 进制、10 进制、16 进制、也有我们熟悉的 base58btc 和 base64 编码。



multicodec


mulcodec 代表的是自描述的编解码, 其实是个 table, 用 1 到 2 个字节定了数据内容的格式, 比如用字母 z 表示 base58btc 编码, 0x50 表示 protobuf 等等。



multistream


multistream 首先是个 stream, 它利用 multicodec,实现了自描述的功能, 下面是基于一个 javascript 的例子; 先 new 一个 buffer 对象, 里面是 json 对象, 然后给它加一个前缀 protobuf, 这样这个 multistream 就构造好了, 可以通过网络传输。在解析时可以先取 codec 前缀,然后移除前缀, 得到具体的数据内容。



结合上面的部分, 我们重点介绍一下 CID。


CID 是 IPFS 分布式文件系统中标准的文件寻址格式,它集合了内容寻址、加密散列算法和自我描述的格式, 是 IPLD 内部核心的识别符。目前有 2 个版本,CIDv0 和 CIDv1。


CIDv0 是一个向后兼容的版本,其中:


  • multibase 一直为 base58btc

  • multicodec 一直为 protobuf-mdag

  • version 一直为 CIDv0

  • multihash 表示为 cidv0 ::=


为了更灵活的表述 ID 数据, 支持更多的格式, IPLD 定义了 CIDv1,CIDv1 由 4 个部分组成:



  • multibase

  • version

  • multicodec

  • multihash


IPLD 是 IPFS 的数据描述格式, 解决了如何定义数据的问题, 下面这张图是结合源代码整理的一份逻辑图,我们可以看到上面是一些高级的接口, 比如 file, mfs, fuse 等。 下面是数据结构的持久化部分,节点之间交换的内容是以 block 为基础的, 最下面就是物理存储了。比如 block 存储在 blocks 目录, 其他节点之间的信息存储在 leveldb, 还有 keystore, config 等。



数据如何传输呢?

接下来我们介绍 libP2P, 看看数据是如何传输的。libP2P 是个模块化的网络协议栈。



做过 socket 编程的小伙伴应该都知道, 使用 raw socket 编程传输数据的过程,无非就是以下几个步骤:


1.获取目标服务器地址


2.和目标服务器建立连接


3.握手协议


4.传输数据


5.关闭连接


libP2P 也是这样,不过区别在于它把各个部分都模块化了, 定义了通用的接口, 可以很方便的进行扩展。


架构图


由以下几个部分组成,分别是:


  • Peer Routing

  • Swarm (传输和连接)

  • Distributed Record Store

  • Discovery


下面我们对它们做分别介绍, 我们先看关键的路由部分。


Peer Routing


libP2P 定义了 routing 接口,目前有 2 个实现,分别是 KAD routing 和 MDNS routing, 扩展很容易, 只要按照接口实现相应的方法即可。


ipfs 中的节点路由表是通过维护多个 K-BUCKET 来实现的, 每次新增节点, 会计算节点 ID 和自身节点 ID 之间的 common prefix, 根据这个公共前缀把节点加到对应的 KBUCKET 中, KBUCKET 最大值为 20, 当超出时,再进行拆分。



更新路由表的流程如下:



除了 KAD routing 之外, IPFS 也实现了 MDNS routing, 主要用来在局域网内发现节点, 这个功能相对比较独立, 由于用到了多播地址, 在一些公有云部署环境中可能无法工作。


Swarm(传输和连接)


swarm 定义了以下接口:


  • transport 网络传输层的接口

  • connection 处理网络连接的接口

  • stream multiplex 同一 connection 复用多个 stream 的接口


下面我们重点看下是如何动态协商 stream protocol 的,整个流程如下:



默认先通过 multistream-select 完成握手


发起方尝试使用某个协议, 接收方如果不接受, 再尝试其他协议, 直到找到双方都支持的协议或者协商失败。


另外为了提高协商效率, 也提供了一个 ls 消息, 用来查询目标节点支持的全部协议。


Distributed Record Store


record 表示一个记录, 可以用来存储一个键值对,比如 ipns name publish 就是发布一个 objectId 绑定指定 node id 的 record 到 ipfs 网络中, 这样通过 ipns 寻址时就会查找对应的 record, 再解析到 objectId, 实现寻址的功能。


Discovery


目前系统支持 3 种发现方式, 分别是:


  • bootstrap 通过配置的启动节点发现其他的节点

  • random walk 通过查询随机生成的 peerID, 从而发现新的节点

  • mdns 通过 multicast 发现局域网内的节点


最后总结一下源代码中的逻辑模块:



从下到上分为 5 个层次:


最底层为传输层, 主要封装各种协议, 比如 TCP,SCTP, BLE, TOR 等网络协议


传输层上面封装了连接层,实现连接管理和通知等功能


连接层上面是 stream 层, 实现了 stream 的多路复用


stream 层上面是路由层


最上层是 discovery, messaging 以及 record store 等


四、总结

本文从定义数据和传输数据的角度分别介绍了 IPFS 的 2 个主要模块 IPLD 和 libP2P:


IPLD 主要用来定义数据, 给数据建模


libP2P 解决数据传输问题


这两部分相辅相成, 虽然都源自于 IPFS 项目,但是也可以独立使用在其他项目中。


IPFS 的远景目标就是替换现在浏览器使用的 HTTP 协议, 目前项目还在迭代开发中, 一些功能也在不断完善。为了解决数据的持久化问题, 引入了 filecoin 激励机制, 通过 token 激励,让更多的节点加入到网络中来,从而提供更稳定的服务。


本文转载自美图技术公众号。


原文链接:https://mp.weixin.qq.com/s/WrSqkR5jdWVMFcLG5OCREQ


2020 年 2 月 24 日 19:18216

评论

发布
暂无评论
发现更多内容

比曲婉婷云尽孝更可怕的是:2020年,低收入家庭仍然在被收割

成周

心理学 教育 培训 维权 曲婉婷

奈学:Java 和 JavaScript 是什么关系?

古月木易

Java

数据挖掘技术在轨迹数据上的应用实践

滴滴技术

人工智能 数据挖掘 滴滴技术 轨道技术 创新公交

不一样的面向对象(三)

书旅

php 面向对象 面向对象编程

信息公交服务在滴滴的应用实践

滴滴技术

滴滴技术 人工只能 信息公交 路径优化

看看别人是怎么面试蚂蚁金服的!社招Java面经分享

Java架构师迁哥

Java 阿里巴巴 面试 蚂蚁金服

Go编程(一) 怎么写Go代码

dongfanger

go 编程 开发

JAVA集合之ConcurrentHashMap

彭阿三

Java JAVA集合

Go编程(二) 多线程简单斗地主

dongfanger

go 编程 开发

国庆假期快来了,打开8天长假的正确方式是...

老胡爱分享

读书 书籍推荐 随笔杂谈

一次注定失败的裸面

escray

ruby 面试题 面经 大龄程序员 面试经历

打造一个全功能的浏览器

Daniel

架构师训练营第三周学习总结

邓昀垚

极客大学架构师训练营

图解 K8S 源码 - Deployment Controller 篇

郭旭东

Kubernetes Kubernetes源码

倒计时!Pulsar Summit Asia 2020 演讲征集

Apache Pulsar

开源 云原生 pulsar Apache Pulsar 消息中间件

2020面试阿里字节跳动90%被问到的JVM面试题附答案

Java架构师迁哥

为什么推广ipv6以及网站ipv6改造基本步骤

MySQL从删库到跑路

Web IP 网络

Java源码系列1——ArrayList

超超不会飞

Java

永续合约系统开发app源码,合约交易所平台搭建

WX13823153201

永续合约系统开发

作业帮基于Apache Doris的数仓实践

DorisDB

数据库 数据仓库 数据分析 OLAP 大数据架构

浅谈滴滴需求响应式公交背后的技术

滴滴技术

滴滴技术 创新公交 路径优化

10多家公司的Java开发面试常见问题合集

Java架构师迁哥

奈学:Java 和 JavaScript 是什么关系?

奈学教育

Java

聊一下《技术力量-一线技术团队成功启示录》

Man

中台 研发管理

初学源码之——Spring IOC 应用

Java架构师迁哥

StreamNative 宣布开源 MoP:Apache Pulsar 支持原生 MQTT 协议

Apache Pulsar

开源 云原生 mqtt Apache Pulsar 消息中间件

2020年行摄回忆录(上)

穿过生命散发芬芳

生活 摄影

java安全编码指南之:异常处理

程序那些事

java安全编码 java安全 java安全编码指南

DàYé玩转数据战略Step By Step

曲水流觞TechRill

数据中台 数字化

点对点音视频应用场景及优势

anyRTC开发者

音视频 WebRTC 直播 RTC 安卓

一篇文章搞定 Nginx 反向代理与负载均衡

哈喽沃德先生

nginx 负载均衡 反向代理 服务器 正向代理与反向代理

「中国技术开放日·长沙站」现场直播

「中国技术开放日·长沙站」现场直播

两个模块,从数据的角度带你深入了解IPFS-InfoQ