HTML5技术

游戏服务端究竟解决了什么问题? - fingerpass(10)

字号+ 作者:H5之家 来源:H5之家 2016-02-02 09:51 我要评论( )

分片需要解决两个问题: 第一个问题,分片方案需要描述shard与shard之间的联系,也就是cluster membership。 第二个问题,分片方案需要描述dbClient的一个请求应该交给哪个shard,也就是work distribution。 针对第

  分片需要解决两个问题:

  • 第一个问题,分片方案需要描述shard与shard之间的联系,也就是cluster membership。
  • 第二个问题,分片方案需要描述dbClient的一个请求应该交给哪个shard,也就是work distribution。
  •   

      针对第一个问题,解决方案通常有三:

      需求决定解决方案,对于游戏服务端来说,后两者的成本太高,而且增加了很多不确定的复杂性,因此现阶段这两种方案并不是合适的选择。比如gossip protocol,redis cluster现在都不算是release,确实不太适合游戏服务端。而且,游戏服务端毕竟不是web服务,通常是可以在设计阶段确定每个分片的容量上限的,也不需要太复杂的机制支持。

      但是第一种方案的缺点也很明显,做不到动态增容减容,而且无法高可用。但是如果稍加改造,就足以满足需求了。

     

      在谈具体的改造措施之前,先看之前提出的第二个问题。

     

      第二个问题实际上是从另一种维度看分片,解决方案很多,但是如果从对架构的影响上来看,大概分为两种:

  • 一种是proxy-based,基于额外的转发代理。例子有twemproxy/Codis。
  • 一种是client sharding,也就是dbClient(每个对数据服务有需求的服务)维护sharding规则,自助式选择要去哪个redis实例。redis cluster本质上就属于这种,client侧缓存了部分sharding信息。
  •   第一种方案的缺点显而易见,在整个架构中增加了额外的间接层,pipeline中增加了一趟round-trip。如果是像twemproxy或者Codis这种支持高可用的还好,但是github上随便一翻还能找到特别多的没法做到高可用的proxy-based方案,无缘无故多个单点,这样就完全搞不明白sharding的意义何在了。

      第二种方案的缺点就是集群状态发生变化的时候没法即时通知到dbClient。

      

      第一种方案,我们其实可以直接pass掉了。因为这种方案本质上还是更适合web开发的。web开发部门众多,开发数据服务的部门有可能和业务部门相去甚远,因此需要统一的转发代理服务。但是游戏开发不一样,数据服务逻辑服务都是一帮人开发的,没什么增加额外中间层的必要。

      那么,看起来只能选择第二种方案了。

     

      将presharding与client sharding结合起来后,现在我们的改造成果是:数据服务是全局的,redis可以开多个实例,不相干的数据需要到不同的shard上存取,dbClient掌握这个映射关系。

    引入新的问题

      目前的方案只能满足游戏对数据服务的基本需求。

     

      大部分采用redis的游戏团队,一般最终会选定这个方案作为自己的数据服务。后续的扩展其实对他们来说不是不可以做,但是可能有维护上的复杂性与不确定性。今天这篇文章,我就继续对数据服务做扩展,后面的内容权当抛砖引玉。

     

      现在的这个方案存在两个问题:

  • 首先,虽然我们没有支持在线数据迁移的必要,但是离线数据迁移是必须得有的,毕竟presharding做不到万无一失。而在这个方案中,如果用单纯的哈希算法,增加一个shard会导致原先的key到shard的对应关系变得非常乱,抬高数据迁移成本。
  • 其次,分片方案固然可以将整个数据服务的崩溃风险分散在不同shard中,比如相比于不分片的数据服务,一台机器挂掉了,只影响到一部分玩家。但是,我们理应可以对数据服务做更深入的扩展,让其可用程度更强。
  •   针对第一个问题,处理方式跟proxy-based采用的处理方式没太大区别,由于目前的数据服务方案比较简单,采用一致性哈希即可。或者采用一种比较简单的两段映射,第一段是静态的固定哈希,第二段是动态的可配置map。前者通过算法,后者通过map配置维护的方式,都能最小化影响到的key集合。

      而对于第二个问题,实际上就是上一节末提到的数据服务可用性问题。

     

    4.2.2 可用性方案   定义问题

      讨论数据服务的可用性之前,我们首先看redis的可用性。


      对于redis来说,可用性的本质是什么?其实就是redis实例挂掉之后可以有后备节点顶上。

      redis通过两种机制支持这一点。

  • 一种机制是replication。通常的replication方案主要分为两种。一种是active-passive,也就是active节点先修改自身状态,然后写统一持久化log,然后passive节点读log跟进状态。另一种是active-active,写请求统一写到持久化log,然后每个active节点自动同步log进度。
  •   还是由于CAP原则,redis的replication方案采用的是一种一致性较弱的active-passive方案。也就是master自身维护log,将log向其他slave同步,master挂掉有可能导致部分log丢失,client写完master即可收到成功返回,是一种异步replication。
    这个机制只能解决节点数据冗余的问题,redis要具有可用性就还得解决redis实例挂掉让备胎自动顶上的问题,毕竟由人肉去监控master状态再人肉切换是不现实的。 因此还需要第二种机制。

  • 第二种机制是redis自带的能够自动化fail-over的redis sentinel。reds sentinel实际上是一种特殊的reds实例,其本身就是一种高可用服务,可以多开,可以自动服务发现(基于redis内置的pub-sub支持,sentinel并没有禁用掉pub-sub的command map),可以自主leader election(基于sentinel实现的raft算法),然后在发现master挂掉时由leader发起fail-over,并将掉线后再上线的master降为新master的slave。
  •   

      redis基于自带的这两种机制,已经能够实现一定程度的可用性。那么接下来,我们来看数据服务如何高可用。


      数据服务具有可用性的本质是什么?除了能实现redis可用性的需求——redis实例数据冗余、故障自动切换之外,还需要将切换的消息通知到每个dbClient。

     

      由于是redis sentinel负责主从切换,因此最自然的想法就是问sentinel请求当前节点主从连接信息。但是redis sentinel本身也是redis实例,数量也是动态的,redis sentinel的连接信息不仅在配置上成了一个难题,动态更新时也会有各种问题。而且,redis sentinel本质上是整个服务端的static parts(要像dbClient提供服务),但是却依赖于redis的启动,并不是特别优雅。另一方面,dbClient要想问redis sentinel要到当前连接信息,只能依赖其内置的pub-sub机制。redis的pub-sub只是一个简单的消息分发,没有消息持久化,因此需要轮询式的请求连接信息模型。

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 网页版扫雷游戏 - 季末的寂寞

      网页版扫雷游戏 - 季末的寂寞

      2017-04-21 13:00

    • net.sz.framework 框架 登录服务器架构 单服2 万 TPS(QPS) - 失足程序员

      net.sz.framework 框架 登录服务器架构 单服2 万 TPS(QPS) - 失足

      2017-04-13 11:05

    • 面向个人的技术咨询服务 - 思想瞭望者

      面向个人的技术咨询服务 - 思想瞭望者

      2017-04-05 12:07

    • net.sz.framework 框架 轻松搭建服务---让你更专注逻辑功能---初探 - 失足程序员

      net.sz.framework 框架 轻松搭建服务---让你更专注逻辑功能---初探 -

      2017-04-02 10:11

    网友点评
    t