一种游戏排行榜的高效实现:改进的redis-like zset
游戏中经常有排行榜的需求,例如对玩家的战力进行全服排行,跨服排行,竞技场段位积分排行,副本伤害排行等。设计一个排行榜系统一般来说需要能提供以下接口:
游戏中经常有排行榜的需求,例如对玩家的战力进行全服排行,跨服排行,竞技场段位积分排行,副本伤害排行等。设计一个排行榜系统一般来说需要能提供以下接口:
现在的游戏服务器设计的时候为了提高吞吐量会考虑充分利用多核的性能,一种思路是偏向多进程并发模型,不同游戏系统可以独立拆分不同进程并行运行,进程之间通过RPC交互,另一种思路就是本文要谈到的多线程模型。
后端服务通常是多个进程各自提供部分功能联合起来作为整体对外提供服务,经常会有不同后端进程间互相通信的需求,有时还有进程间相互保活维持连接状态的需求。如果进程数比较少,相互建立网络连接也能简单解决,但如果服务扩展,进程数变多了,网络拓扑结构连接错综复杂,难以部署和动态扩容。
Hugo是golang实现的静态网址生成器,主题丰富,使用简单。Isso则是python实现的类似Disqus的开源评论服务器。本站就是使用Hugo的一个极简主题Even生成静态内容,Isso server提供评论服务,Nginx作为web server反向代理Isso并提供静态内容。以下是我在centos7上的详细安装部署步骤:
这里是按照源码方式安装的(官方文档有编译好的二进制下载安装方法说明),源码安装需要先安装golang(安装文档点我),按照文档安装完golang后,运行如下命令安装Hugo:
git clone https://github.com/gohugoio/hugo.git
cd hugo
go install --tags extended
一个比特币区块可以分为区块头和该区块包含的所有交易数据,区块头的大小是80字节,而一个交易的平均大小是250字节,平均一个区块包含超过500个交易,所以一个完整的区块大小几乎是区块头的1000倍。一个包含完整区块链数据的全节点占有可观的存储空间,但是许多比特币客户端(如智能手机等)不具备这样的条件,它们通常采用 SPV 技术只存储所有区块头组成的链表,这种设备被称为 SPV 客户端。
SPV 节点因为没有交易数据,没办法像全节点一样独立验证一个交易,它必须向周围的全节点查询交易是否存在某个区块中,如果在一个区块中,并且这个区块已经得到做够多的确认(已经有6个新块在其上生成了),从而这个交易得到认证。 SPV 节点与周围的全节点建立连接,通过发送 getheaders 消息给对方来获取区块头,并且在连接上设置过滤器,获取自己感兴趣的交易数据以及新生成的区块头。
SPV 节点没有完整的交易数据,是如何验证某个交易是否存在于区块中的呢?这里用到了一个数据结构 Merkle 树:
Merkle 树是一棵满二叉树,叶节点是这个区块中各个交易数据的Hash值(运行两次SHA256),非页节点是对两个子节点的连接(HA+HB)求Hash值:
为了生成一棵满二叉树,当交易个数是奇数的时候,会复制最后一个交易作最后一个叶节点。 Merkele 树就是按照以上描述的规则之下而上生成,生成的根节点最后会保存到区块头中。
现在SPV 节点想验证上图(Merkle Tree)中的交易A是否存在,只需要知道节点HB和HCD就可以算出来根节点,然后与区块头中保存的根节点作比较,如果相匹配就验证了A存在该区块中。在这里HB和HCD称作交易A的 Merkle 路径。
SPV 节点在和其他全节点的连接上设置了过滤器表示对A交易感兴趣,当有一个全节点遇到A交易后,就会通过 merkleblock 消息把该交易数据和其所在的区块头,以及 Mekle 路径数据传给它,SPV 节点可以验证A是否存在该区块中,并把区块头链接到自己的区块链中。
上文说SPV 节点可以设置过滤器过滤自己感兴趣的交易,而不用接收所有交易数据。这个过滤器最简单的实现是直接告诉对方想要什么样输出地址的交易等,从而可以在SPV 节点构建一个钱包。但是这个信息很可能被网络上的不怀好意的人监听,导致泄漏了自己的隐私。
Bloom 过滤器可以描述一个你想要的搜索模式而不用直接告诉具体的东西(就像在搜索引擎中搜索关键字或者设置一个正则表达式匹配字符串一样),它提供了一种高效的方式可以让你表达你想要的搜索模式的同时可以保护你的隐私。
使用者可以微调搜索模式,但是搜索模式越精确会带来越差的私密性,反之模式越不精确可能私密性越好。Bloom 过滤器的具体实现细节可以参考维基百科Bloom filter,类似对原始数据做哈希运算,然后把输出信息编码到一个二进制串里面。
SPV 节点会先把 bloom filter 初始化为空,意味着匹配不到任何数据,然后会把感兴趣的比特币地址,公钥,交易id,作为各个匹配模式集成到一个 bloom filter中,之后通过 filterload 消息发到全节点,在全节点这一方则会把每一个交易对这个 bloom filter做过滤,把匹配到的数据发回给 SPV 节点。
SPV 节点可以验证一个交易是否存在,但是不能证实一个交易不存在,结果就是容易被不诚实的节点攻击,例如双花同一笔UTXO(Unspent Transaction Output),SPV 节点不能证实这个UTXO没有被其他交易花掉,有可能这个已经被花费的交易被不诚实的节点隐藏起来了。
虽然bloom filter 过滤器在一定程度上保护了 SPV 节点的隐私,但是并不是完全私密的,只要在网络上进行长期监控收集到足够多的数据,还是能获取到有用信息的。
为了解决这些风险,可以对连接通道进行加密并对连接的节点进行认证。