广义的区块链是指实现了数据公开、透明、可追溯的产品的架构设计方法。必须包含点对点网络设计、加密技术应用、分布式算法的实现、数据存储技术的使用等4个方面,其他的可能涉及到分布式存储、机器学习、VR、物联网、大数据等。狭义的区块链仅仅涉及到数据存储技术,数据库或文件操作等。本文的区块链,指的是广义的区块链,不会涉及数字币的任何机制和实现。
本文所阐述的区块链实现主要面向的是私有链及联盟链。例如数家公司组个联盟,来共同见证、记录一些不可篡改的交互信息,如A公司给B公司发了一个xxx请求,B公司响应了什么什么。其实要的就是一个分布式数据库,而且性能要好,不是像比特币那种10分钟才生成一个区块,同时还需要确保每条信息被准确记录,不允许丢失(即技术上不允许分叉也不允许丢失)。这个平台的核心是数据库的性能,和区块链的一些特性以及实现高可用和选择性能更优的共识算法。
整个系统分为接入层、池化层、指令处理层、网络层以及存储层,下面我们分层逐个介绍,并就各层可能的性能及高可用优化点做一个说明。
接入层主要是区块链创建及查询的API接口,业务逻辑很简单,例如区块链的创建,先看一下指令集的定义:
池化层主要是基于ConcurrentLinkedQueue实现的一个队列服务,它主要的功能逻辑如下:
生产者第1个功能在前面已经介绍,这里介绍下第2个生产者场景,当服务端接收到来自共识结果是指令已存在,例如冲突,分叉的时候就进行重新打包:
接下来blockService会调用addBlock发起区块添加请求,会调用节点共识算法进行共识:
网络层,各节点采用的是长连接,断线后重连,维持心跳包。网络框架使用的是t-io,oschina的知名开源项目。t-io采用了AIO的方式,区块链平台在大量长连接情况下性能优异,资源占用也很少,并且具备group功能,特别适合于做多个联盟链的SaaS平台。并且包含了心跳包、断线重连、retry等优秀功能。
在项目中,每个节点即是server,又是client,作为server则被其他的N-1个节点连接,作为client则去连接其他N-1个节点的server。同一个联盟,设定一个Group,每次发消息,直接调用sendGroup方法即可。
分布式共识算法是分布式系统的核心,常见的有Paxos、pbft、bft、raft、pow等。区块链中常见的是POW、POS、DPOS、pbft等。
比特币采用了POW工作量证明,需要耗费大量的资源进行hash运算(挖矿),由矿工来完成生成Block的权利。其他多是采用选举投票的方式来决定谁来生成Block。共同的特点就是只能特定的节点来生成区块,然后广播给其他人。
由于私有链是封闭生态的存储系统,因此采用Paxos类共识算法(过半同意)可以达到最优的性能;联盟链有半公开半开放特性,因此拜占庭容错是适合选择之一,例如IBM超级账本项目;对于公有链来说,这种共识算法的要求已经超出了普通分布式系统构建的范畴,再加上交易的特性,因此需要引入更多的安全考虑。所以比特币的POW是个非常好的选择。
我们这里可选的是raft和pbft,分别做私链和联盟链,项目中我使用了修改过的pbft共识算法。
每个节点把客户端发来的交易向全网广播,主节点将从网络收集到需放在新区块内的多个交易排序后存入列表,并将该列表向全网广播;
每个节点接收到交易列表后,根据排序模拟执行这些交易。所有交易执行完后,基于交易结果计算新区块的哈希摘要,并向全网广播;
如果一个节点收到的2f(f为可容忍的拜占庭节点数)个其它节点发来的摘要都和自己相等,就向全网广播一条commit消息;
如果一个节点收到2f+1条(包括自己)commit消息,即可提交新区块到本地的区块链和状态数据库。
可以看到,传统的pbft是需要先选举出leader的,然后由leader来搜集交易,并打包,然后广播出去。之后各个节点开始对新Block进行校验、投票、累积commit数量,最后落地。
为了提升性能及可用性,这里对pbft做了修改,这是一个联盟,各个节点是平等的,所以不需要让每个节点都生成一个指令后,发给其他节点,再大家选举出一个节点来搜集网络上的指令组合再生成Block,太复杂了,而且又存在了leader节点的故障隐患。
我们对pbft的修改是,不需要选择leader,任何节点都可以构建Block,然后全网广播。其他节点收到该Block请求时即进入Pre-Prepare状态,校验格式、hash、签名等权限,校验通过后,进入Prepare状态,并全网广播状态。待自己累积的各节点Prepare的数量大于2f+1时,进入commit状态,并全网广播该状态。待自己累积的各节点Commit的数量大于2f+1时,认为已达成共识,将Block加入区块链中。
很明显,和有leader时相比,提升了处理性能及解决了leader的节点故障问题,但是缺少了顺序的概念。有leader时能保证Block的顺序,当有并发生成Block的需求时,leader能按照顺序进行广播。譬如大家都已经到number=5的区块了,然后需要再生成2个,有leader时,则会按照6、7的顺序来生成。而没有leader时,则可能发生多节点同时生成6的情况。为了避免分叉,可以优化的方案如下:
对比PBFT的共识流程可以发现RAFT的共识流程复杂度要低很多,事实上RAFT的共识流程复杂度是o(n),而PBFT的复杂度是o(n*n),RAFT只是容忍故障节点,但不容忍作恶节点(即虚假返回状态)。由于其共识性能更优,我们在系统中也实现了RAFT共识模块,采取的是git上的一款轻量级的raft-java,它核心实现的共识这里不做过多说明,感兴趣的朋友可以查找相应资料了解下。这里只是解释下共识后的区块添加逻辑,如下:
我们存储层采取的是rocksDB和levelDB,他们本质上都是一致的,rocksDB是基于levelDB优化而来的,比特币采取的是levelDB实现,他们都非常适合这种变长KV小规模的元数据存取。
我们这里封装了一个DbStore接口,定义了常见的put、get及delete操作,rocksDB及levelDB分别实现这些接口即可,涉及到的区块落盘的逻辑如下:
由于篇幅所限,本文讨论了一个区块链的几个核心层的主要实现逻辑及优化,对于P2P网络的Merkle Tree实现以及网络节点之间的数据同步和一致性机制没有涉及,大家如果感兴趣后续我们可以继续接着聊。
附注:此平台在os china开源md_blockchain项目上优化而来,同时也感谢jasperwang参与对源码优化的贡献。
评论