BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

在拥有50w用户的实际项目中使用以太坊

| 作者 Nikita Savchenko 关注 0 他的粉丝 ,译者 冬雨 关注 4 他的粉丝 发布于 2018年7月16日. 估计阅读时间: 15 分钟 | BCCon2018全球区块链生态技术大会,将区块链技术的创新和早期落地案例带回您的企业

这个信息技术故事讲的是以太坊,讲讲我们将以太坊区块链与产品DreamTeam结合时遇到的缺陷,DreamTeam是第一个电子竞技和游戏招聘和管理网络。在撰写本文时,我们平台上的注册用户总数已超过50万。

在我们的平台上,区块链负责所有与支付相关的操作。所有“金钱相关”的操作都是使用以太坊智能合约来处理的。当用户创建团队、添加或删除参与者、接收补偿金、彼此之间的交易等时,就需要用到智能合约了。在这种情况下使用区块链和智能合约,以保证支付和避免欺诈,而不幸的是在电子竞技中会经常见到这种欺诈现象。

数字

3月30日星期五,我们在以太坊网上为我们的用户开放了区块链解决方案。截止到大约上线后的45天内,我们已经达成了来自于主账户(管理团队和支付平台的支付)的5万笔挖矿交易。这大约即为每小时42笔交易,占以太坊网络总能力的0.25%(以消耗的gas来计算),考虑到时间段,这个数量相当令人惊讶。



DreamTeam区块链交易的间隔为30分钟

一旦我们迁移到实时以太坊网络(mainnet),这些数字就会发生变化,因为大多数交易不会在那里发生。这背后有几个原因:

目前的大部分交易只是初始奖金,队员和队伍一旦“激活”他们的区块链钱包就会得到这笔奖金。我们不会在实时网络上发放奖金,因此交易的数量在一开始就会急剧减少(目前超过63%的交易是初始奖金)。

在我们的平台上,只有一定比例的用户预期会使用“区块链”团队和付费服务,一旦这些转为真钱,会再次减少交易的数量。

考虑到这两点,我们预计只有10%左右的交易发生在真正的以太坊网络上,顶多,理论上我们的业务会占用以太坊网络的总能力的0.025%,大约每小时发生4笔交易。

然而,这一统计数据并没有考虑到几乎80%的交易发生在高峰期(见上图),这使得:

a)在高峰时期合理时间内被挖掘的以太坊交易的价格高昂(每笔转让交易的费用在0.25美元以上);

b)确认更便宜的交易要等待很长的时间(约0.15美元的费用交易要超过10小时)。

也就是说,当前“传统”区块链解决方案背后的真相是,它们的可扩展性不如常规数据平台。一旦公链流行起来,它最终将变得用起来很是昂贵。因此,作为最受欢迎的交易账簿,以太坊公司现在的价格就是相当昂贵。在我们的案例以及其他商业案例中,不断增长的用户数量和即将到来的特性一定会使以太坊的成交率更高,这可能会致使以太坊仅仅成为我们“代币制”的一个短期解决方案。

我在之前的文章中已经提到,我们都知道以太坊区块链尚未实现可扩展性。以太坊中即将出现的分片特性可能会极大地增加网络吞吐量,但是,它来得有些太晚了,在这种情况下,我们可能会决定迁移到另一个区块链平台。以太坊之外还有很多选择,只是,还没有任何一个像以太坊那样受欢迎和可靠。

所以,这篇文章主要内容还是以以太坊为主。

以太坊集成和后端体系结构

一旦我们开始处理实际价值,事情总会变得复杂起来。至于区块链,重要的是构建一个系统,来确保在通往以太坊网络的途中不会丢失任何交易。我想分享一些我们在项目开发过程中遇到的陷阱。

技术栈

我们使用微服务体系结构和持续集成,其中区块链解决方案由四个逻辑部分组成:

让我们简单地浏览一下所有技术内幕,并讨论为什么我们决定以这种方式构建平台。

后端

以太坊是为去中心化的web开发的,该平台的主要工具是基于JavaScript编写的。因此,在后端方面我们直接决定用NodeJS。我们还决定使用MongoDB,它是一个针对NodeJS的“传统”面向文档的数据库,能与JavaScript无缝集成。

我们的后端架构最重要的部分之一是使用RabbitMQ消息代理。对于那些不熟悉RabbitMQ的人,我建议您阅读此文此文。简而言之,RabbitMQ是一个专用服务,其他服务(如区块链服务)向其推送“消息”,然后这些“消息”被其他服务(或相同的服务)使用。

使用RabbitMQ有许多优点。但特别地,在向网络发布交易之前,我们首先向消息代理发送“交易发布请求消息”,然后由实际将交易发布到以太坊网络的工作者(worker)接收。这使我们能够实现以下套路:

  1. 把消息放到消息代理队列中,直到它得到处理,这将最小化由于各种原因(网络故障、中断、人为错误等)而导致交易无法发布的风险。
  2. 将交易发布到队列让我们可以容易让区块链交易的处理先停止一会儿(暂时杀掉消息消费者),进行一些维护之后再恢复交易处理(将消费者带回)。所有消息都将简单地堆栈在队列中,直到它们在任何平台定期维护之后得以全部发布和处理。

我们的一个智能合约是可升级的,我们成功地使用#2在生产环境中升级了合约,感知不到任何停机时间。

用于以太坊的Truffle框架

截止到撰写本文时,Truffle仍是以太坊开发最受欢迎的框架。它有一个很好的API,它将开发人员从底层以太坊的工作中解放出来(比如组装和签署原始交易、编译可靠代码、使用智能合约ABI等等),而且也引入了一些模式,它们可能适合某些项目工作流程,也可能不适合。

在Truffle中,为了将智能合约部署到网络上(或对智能合约进行任何初始设置),您得要编写JavaScript的迁移脚本,其中每次迁移都只运行一次,针对每个网络设定一个一个地按顺序执行。根据这个思想,第一次迁移向网络部署了一个小的智能合约(migrations .sol),然后Truffle使用该契约跟踪哪些迁移已经在网络上运行。一旦部署了这个智能合约,它的地址将被记录到一个工件文件(Migrations.json)中。Truffle读取这个文件是为了了解哪些迁移已经运行了,哪些还没有运行。

现在,假设您想部署一个新的智能合约,然后(可能稍后)调用其中的一些“初始化”方法,您需要编写三个迁移脚本,它们将:

  1. 部署迁移智能合约。
  2. 部署您的智能合约。
  3. 调用您的智能合约中的初始化函数。

注意,最后两个操作可以合并到一个迁移脚本中;我使用了3个脚本,为的是说明迁移是一个接一个开发、然后一个接一个运行的原子操作。

这些迁移脚本一旦由Truffle运行,就会在网络上产生以下交易:

  1. 迁移智能合约部署。
  2. 保存迁移1到网络上(Truffle将调用迁移智能合约)。
  3. 您的智能合约部署。
  4. 保存迁移2到网络。
  5. 智能合约函数调用。
  6. 保存迁移3到网络。

如您所见,为了在网络上做2个交易,Truffle需要再做4个交易,最终在网络上一共做了6个交易。

所以如果您的目标只是将一个简单的智能合约部署到网络中,可能希望避免使用Truffle迁移工作流。使用Truffle(例如,手工部署智能合约)实现此目的的一种方法是使用单个迁移脚本或围绕Truffle迁移脚本进行手动自动化。在任何场景中,您都可以使用truffle migration -reset在migrations目录中按顺序运行所有脚本(这也不需要Migrations.sol)。

Geth节点同步

Geth客户端节点只有在正确设置之后才能正常运行,不幸的是,这个过程可不快。今天(2018年5月18日)运行Geth节点需要:

  1. testnet至少需要66 Gb的SSD磁盘空间,而mainnet至少需要92 Gb(一些志愿者把统计数据发布在了你可以追溯到它的地方)。
  2. 至少8.3 Gb内存。
  3. 引导时需要良好的带宽,但它占用的互联网流量很少(平均25kb每分钟)。
  4. 同步时的平均处理能力。
  5. 磁盘I/O越好,Geth节点同步状态就越快。

它需要大约0.5-2天的时间来同步testnet的节点,而将其同步到mainnet则需要1-3天的时间。

如果您尝试同步Geth节点,可能想知道为什么Geth同步在最后100-200块上,同时不断地下载“已知状态”。我建议您阅读这篇很棒的文章,它详细解释了为什么会发生这种情况,但简而言之,要做到“同步”,您的节点需要在处理所有请求之前下载所有状态项。如果超过1.4亿,这需要1-3天下载(取决于磁盘I/O、网络和处理器)。

以下是我们监控得到的一些与Geth相关的图表(绘制间隔为1分钟):



Geth节点每日内存使用情况(同步后)



Geth节点CPU使用情况(同步后)



Geth节点网络使用情况(同步后)

交易发布

在以太坊中一个非常奇怪但又非常必要的事情是,从特定帐户发送的每个交易必须具有唯一的顺序整数,这称为nonce。在以太坊中,使用nonce数字来防止双花攻击(double spend attacks)。这个整数引入了两个重要的影响:

  1. 从一个帐户提交的所有交易都按顺序处理(挖掘)。例如,如果您向网络提交了2个nonce整数各为1和2的交易(每个交易的序列唯一整数都是必须的),那么在第一个交易处理(被挖掘)之前,第二次交易是根本就不会被挖掘到的。但是在你提交多个具有顺序nonce数的交易到网络,有可能这些交易(或者全部)将在同一块得以挖掘(如果你分别提供良好的gas价格的话,但它也取决于矿工是否选择这些交易)。
  2. 您需要额外跟踪提交的每个nonce数字,或者只使用一个节点以防止nonce冲突。

第一点是特别痛苦的,如果你做了这样一个需授权的智能合约,只有一个地址被授权“管理”某件事,你需要经常“管理”这个智能合约。设定较低或普通的gas价格并形成大量交易,将导致为了等新的交易被挖掘可能会需要几乎无穷无尽的等待时间。

解决第一个问题的方法是将交易发布拆分为多个以太坊账户。然而,在这种情况下,您永远不会知道将在哪个交易之前挖掘哪个交易。在我们的智能合约下,甚至可以指定哪个客户对应于哪个团队ID,以避免打乱我们的团队对智能合约的管理。

Nonce碰撞

为考虑一个节点崩溃的情况,我们尝试了扩展Geth节点数量,此时我们遇到了另一件神秘的事件,那就是Nonce碰撞。

事实证明,不幸的是,您不能只是仅仅扩展Geth节点的数量。

我们在三个Geth节点之前使用了一个简单的负载均衡器,该节点将每个交易发送到三个节点中的一个。问题是每次我们一次提交许多交易,其中一些交易就神秘地消失了。我们花了一两天的时间才最终发现这是一个nonce碰撞的问题。

当您向网络提交原始交易时,是没问题的,因为您自己跟踪nonce数字。在这种情况下,您只需要一个节点将原始交易发布到网络。但是,如果您正在使用节点中内置的帐户解锁机制,并且在发布交易(使用web3)时不指定nonce,那么该节点将尝试自己选择适当的nonce值,然后签署交易。

由于网络延迟,如果两个节点接收相同的交易发布请求,它们可能会生成相同的nonce值。在接收交易发布请求时,他们不知道他们都收到了具有相同nonce的交易。因此,当通过网络传播这些交易时,其中一个将最终被删除,因为它的“交易nonce太低了”。

为了修复由于向系统添加负载均衡器而引入的nonce冲突,我们需要创建一种不同的负载均衡器。例如,一个总是使用一个特定节点的负载均衡器,并且只在第一个节点宕机时才切换到另一个节点。

gas限值

gas限值也是一个非常重要的数值。您可以以两种方式提交gas限值方面的交易:

#1 只需要在代码中硬编码gasLimit = blockGasLimit(或另一个您认为对您的交易来说足够的数字)。

#2 使用诸如estimateGas之类的东西来估计交易所需的gas限值。

我一直都建议使用选项#2,因为它切实影响了交易挖掘时间。在这种情况下,交易的处理时间要快得多。

另外,请注意,选项#2只是对在网络当前状态下运行此交易将使用的gas用量的严格估计。在实际处理交易之前,网络的状态可能会发生变化,并最终导致gas超量异常。我建议在gas估值中再额外增加1-5%,并在将交易发布到网络之前,进一步研究如何以及什么会实际影响您的gas估计值。

结论

毫无疑问,以太坊会是世界上最伟大的未来塑造者之一。但在以太坊中,以及在任何其他“革命性”的事物中,有许多地方并不理想,还有待改进。现在,有很多与太坊类似的东西,现在就有EOS、恒星、卡达诺、Lisk和其他许多正在积极开发的有前途的平台,多到令你很难做出选择。但是以太坊仍然是一个领导者,因为社区和大家长久以来对它的信任才能真正使平台走下去。

查看英文原文:Ethereum Blockchain in a Real Project with 500k+ Users

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT