编者按:本文来自 DfinityFun(ID:DfinityFun),Odaily星球日报经授权转载。
整理:Blockpunk网络神经元系统(NNS)
互联网计算机是基于称为互联网计算机协议(ICP)的区块链计算协议。网络本身是根据分层结构设计的。底层是托管专用硬件节点(Nodes)的独立数据中心。这些节点机器组合在一起以创建子网(Subnets)。子网主运行着软件容器(Canisters),它们是可互操作的基础计算单元,由用户上传,容器中包含了代码和状态。
ICP 的独特之处在于它的网络神经元系统(NNS),它负责控制、配置和管理网络。
数据中心通过向 NNS 申请加入网络( NNS 负责管理入网)。NNS 本身是一个开放的治理系统,它监督着网络的准入条件。从某种意义上说,它的职责与互联网上的 ICANN 很像(ICANN 为想要运行 BGP 路由的机器分配系统号)。NNS 承担着广泛的网络管理员的角色,包括监控节点计算机以查找互联网计算机网络上的统计偏差,从而辨识系统的性能需求并检查处理错误。
NNS 在代币经济学中也起着关键作用。NNS 生成新的 ICP 代币(以前称为 DFN 代币)以奖励节点,节点分为两类,一种是数据中心运行的专用硬件节点,一种是在 NNS 中抵押 ICP 创建的投票神经元节点,神经元节点治理着网络中的提案。当 NNS 发行新的 ICP 代币来奖励数据中心和神经元时,这是通货膨胀的部分。
最终,数据中心所有者和神经元所有者可以获取 ICP 代币,并与容器所有者和管理者进行交易。容器所有者和管理者将这些代币转换为 cycles ,然后使用这些 cycles 来为其容器充电(即 gas 费)。例如,当这些容器执行计算或存储内存时,整个流程中需要消耗 cycles,cycles 消耗殆尽后,必须为它们补充更多的 cycles 才能继续运行,这是通货紧缩的部分。
子网
要了解互联网计算机,你必须先了解子网的概念,子网是整个网络的基本组成部分。子网负责托管着互联网计算机网络的软件容器的不同子集。在 NNS 控制下,将从不同数据中心提取的节点机器聚集在一起来创建子网。这些节点通过 ICP 协作,以对称地复制与它们所托管的软件容器有关的数据和计算。
NNS 在构建子网时会合并来自独立数据中心的节点。通过使用 DFINITY 开发的拜占庭容错技术和密码学技术,ICP 协议可以确保子网防篡改且永不宕机。尽管子网是整个互联网计算机网络的基本组成部分,但它们对用户和软件是无感的。用户和容器软件仅需要知道容器的身份即可调用其共享的函数。
这种无感性是互联网基本设计原则的延伸。在(传统)互联网上,如果用户要连接到某些软件,需要知道运行该软件的计算机的 IP 地址和该软件正在侦听的 TCP 端口。在互联网计算机上,如果用户希望调用函数,则只需要知道容器的身份和函数签名即可。与互联网创建无缝连接的方式相同,DFINITY 也为软件创建了一个无缝的世界,其中任何获得许可的软件都可以直接调用任何其他软件,而无需了解网络的基础运行情况。
互联网计算机还以其他方式确保子网的无感性。NNS 可以拆分和合并子网,以平衡整个网络的负载。当然,这对于托管容器也是不受影响的。
在此示例中,我们有一个虚拟的子网 ABC,该子网承载了 11 个软件容器。NNS 表示需要拆分网络扩展性能,这时子网 ABC 将继续托管容器 1–6,但同时生成一个新的子网 XYZ 来托管容器 7–11。在扩展中,涉及的所有容器都不会遇到服务中断的情况。
在将容器上传到互联网计算机时,必须指定特定的子网类型。实际上,有一个专门为 NNS 服务的特殊子网,你无法将容器上传到该子网。相反,你必须指定子网类型,例如“data(数据)”,“system(系统)”或“fiduciary(受信任)”。
每种子网类型都会为你的容器提供某些属性和功能。例如,如果你的容器托管在数据子网中,则它可以处理调用,但不能调用其他容器。要调用其他容器,你需要一个系统子网。如果希望你的容器能够保持 ICP 代币的余额或将 cycles 发送到其他容器,则需要一个受信任的子网。由于这些原因,治理容器只能托管在受信任的子网中。
子网的功能部分源自底层的容错能力。这是基础科学领域中令人兴奋的领域,我们希望很快与公众分享,其中包括允许 NNS 修复损坏的子网的新的加密技术。
容器
子网的目的是托管容器。容器在专用的管理程序中运行,并通过公共指定的 API 与其他的容器交互。容器包含了可在 WebAssembly 虚拟机上运行的 WebAssembly 字节码及其中运行的内存数据页。通常,可以通过编译诸如 Rust 或 Motoko 之类的编程语言来创建该 WebAssembly 字节码。该字节码将包含一个运行时(runtime),使开发人员可以轻松地与 API 进行交互。
在互联网计算机上,调用容器共享的函数有两种方式:更新调用或查询调用。本质区别在于,当你将函数作为更新调用进行调用时,其对容器内存中的数据所做的任何更改都将保留,而如果将函数作为查询调用进行调用时,则其对内存的任何更改在运行后都将被丢弃。
更新调用的更改是持久化的,并且它们是防篡改的,因为 ICP 区块链计算机协议在子网的每个节点上都运行它们。如你所预期的,这些调用在一致的全局调用顺序中,允许在完全确定的执行环境中并发执行。更新调用在两秒内就可完成。
在此示例中,用户向容器内托管的交易所应用提交购买订单。
相反,查询调用不会保留更改。它们对内存所做的任何更改在运行后都会被丢弃。它们非常高效且便宜,并且仅需几毫秒即可完成。这是因为它们未在子网中的所有节点上运行,也意味着它们提供了较低的安全级别。
在此示例中,用户正在请求自定义的新闻页面,立即就能获得新生成的内容。
自动存储,正交持久性
开发人员保留数据的方式时互联网计算机最酷的功能情之一。开发人员不必考虑持久性问题,他们只需编写代码,持久性就会自动发生。这称为正交持久性。这是因为互联网计算机会保留容器中的内存数据页。
你可能想知道这一切如何工作。对于可以使内存数据页发生变化的更新调用来说,容器是软件执行者。这意味着在任何给定时间,容器内只能有一个执行线程。
尽管容器内只有一个执行线程,但默认情况下可以交错进行跨容器更新调用。当更新调用进行跨容器更新调用时会发生这种情况,该调用会阻塞,从而允许将执行线程移至新的更新调用。
相比之下,查询调用不会对内存进行永久更改。这样一来,在任何给定时间,容器内可以有任意数量的并发线程来处理查询调用。这些查询调用针对最后确定的状态根中记录的内存快照运行。
最后,容器可以创建新的容器以及容器可以也 fork 自身。你只需指定 WebAssembly 字节码即可创建一个新的容器,并且内存数据页开始为空。当容器 fork 时,将创建一个新生成的副本,该副本与内部的内存页面相同。创建可扩展的互联网服务时,fork 功能非常强大。
可扩展性
现在来谈谈互联网计算机服务的可扩展性。不同类型的容器都有各自的容量上限。例如,由于 WebAssembly 实现的限制,一个容器只能存储 4GB 的内存。因此,当我们要创建可扩展到数十亿用户的互联网服务时,我们必须使用多容器架构。
我们可能希望创建一个特殊的容器来创建容器的许多副本,然后将用户内容分片到不同的容器以创建可扩展的互联网服务。但是,由于很多种原因,这个架构过来简单了。
的确,每个额外的容器都会增加整体内存容量,以及增加容器数量都会增加整体更新和查询调用的吞吐量,但是我们无法扩展针对特定用户内容的查询调用请求。每当我们通过添加更多的容器分片来增加系统容量时,我们都需要重新负载均衡用户内容,这并不是真正的边缘可扩展架构。也没有好用的方法,在查询时把数据从最接近的副本中提供给终端用户。我们需要一个两层架构,前端容器和后端容器。
互联网计算机提供了一些有趣的功能,可以将终端用户锚定到前端容器,比如允许通过 NNS 将域名映射到多个前端容器上(类DNS)。当终端用户希望解析这样的域名时,互联网计算机将查看托管前端容器的所有子网中所有副本节点的总数,并返回最接近的副本节点的 IP 地址。这导致终端用户会在距离最近的副本上执行查询调用,从而减少了固有的网络延迟并改善了用户体验,在没有内容分发网络(CDN)的情况下提供了边缘计算的优势。
为了充分利用这个功能,我们需要一个涉及前端容器和后端数据桶容器的经典两层结构。在此示例中,Web 浏览器希望加载个人资料图片。
Step1,Web 浏览器将映射到一个前端容器,它运行在具有附近节点的子网。然后,Web 浏览器将提交查询调用请求到附近的节点以检索照片。
Step2,前端容器将向保存照片的数据容器发出跨容器查询调用请求。
Step3,如果数据存储容器返回的查询调用响应涉及静态内容(例如照片),则可以将数据存储在缓存中。在此案例中,运行前端容器查询调用的副本节点,可以将查询调用的响应(即数据)存储到其查询缓存中。
Step4,查询调用缓存机制对于前端容器代码是完全无感的。一旦用户调用的前端容器收集了所有必要的信息,它就可以通过查询调用响应或 HTTP 请求返回内容。
随着时间的流逝,节点的查询缓存会累积静态内容并生成附近用户感兴趣的数据,从而为他们提供更快,更好的用户体验。这样,互联网计算机的原生边缘体系结构提供了内容分发网络的优势,而无需开发人员做任何特殊的事情,也无需争取单独的专有服务的帮助。
对于更新调用,经典两层结构采用了不同的方法。必须序列化对用户内容和数据的更新,以防止诸如更新丢失之类的问题。通常,会通过计算用户hash值,将用户映射到特定的前端容器来实现的。
一旦在 Web 浏览器或智能手机上运行的 UX/UI 确定了哪个前端容器负责协调对某些内容或数据的更改,便可以通过标准接口提交更新调用来修改内容或数据。
然后,此前端容器通常会进行更多的跨容器更新调用,以实现所需的更改。
开放式互联网服务
总结一下,让我们讨论一下设计使用前端容器和后端数据容器的两层体系结构的开放互联网服务。首先,当你编写前端容器代码时,将通过使用称为 BigMap 的现有库类来简化工作。
BigMap 可以存储 EB 级(十亿字节,约为百万TB)数据,仅使用一行代码就可以完成对象的写入。通过让前端容器和数据桶容器复制,在两个容器之间分担责任,让体系结构可以透明,动态地横向扩展。
最后,要创建真正的开放互联网服务,你需要将所有容器的责任分配给一个开放的代币化治理容器。如果你是企业家,则可以通过在早期出售一些治理代币来筹集发展资金。你可能会设计一些方案,通过给他们治理代币提供激励来激发互联网服务的早期参与者,从而获得更好的网络效果,并赢得竞争。