棋牌游戏服务器内部架构基本功能解析和需要注意的东西

棋牌游戏服务器内部架构基本功能解析和需要注意的东西

  • 数据共享如何实现

由于棋牌类不分区不分服,一般都会按世界服的思路来设计,也就是说,服务器是一个由多台物理机组成的集群。当用户登录服务器后创建房间时,会根据负载均衡算法分布到任何一台服务器上。所以,不管用户登录的是哪一台服务器都是可以获取玩家的数据,一般都会采用Redis来做数据共享。

  • 如何进入房间

在同一局游戏中,会要求所有人都在同一个房间中,可以规定在同一间房间中的用户必须登录到同一台物理机上。在创建房间完成之后,其他人根据房间号查找房间的时候,可以根据房间号换取房间所在服务器的IP和端口,判断当前登录用户的服务器的IP的端口是否和所在服务器是否一致,若相同则不做切换,若不同则需连接到房间所在的服务器上。

  • 如何保证房间操作的顺序

创建房间成功之后接下来的操作都要保证它的顺序性,所以房间需要有一个自己的消息队列,可以把每个房间到达服务器的消息封装为一个任务,将这个任务放到消息队列中,然后有一个任务执行者去按顺序执行这些任务。

  • 登录

登录都需要接入第三方,所以这块是HTTP操作,需统一提供一个Web服务,用来做登录验证。因为在登录时调用第三方的HTTP服务的过程会很慢。如果放在逻辑服去做的话可能会卡住业务逻辑任务。因为不同的玩家业务请求可能在一个线程中,如果有任务卡那么这个任务之后新来的请求都会被卡住,进而会导致消息延迟。

  • 公告通知

公告一般会在登录时向服务器获取一次,将它放在Web服务器与业务逻辑分离,当业务逻辑服维护或更新时不会影响用户的登录。

对于世界消息通知,统一由网关处理并发送给本网关中所有的用户。对于管理后台发送过来的通知,如玩家充值、自动广播等,可直接发送到网关提供的HTTP服务接口,由网管统一发送给网管中的所有用户。对于游戏内的通知,如结算通知等,可直接使用游戏中的通知模块处理之后,发送到网管在发送给各个客户端。

  • 用户ID

由于棋牌类游戏是世界服无分区,所以用户的ID必须是全局唯一的,可以利用Redis的incr()方法原子的递增,如果不想被别人根据递增的ID推算出有多少注册用户,递增的梯度可以随机,比如每次递增的值都是从1到1024中随机的一个。

  • 创建房间

当房主房间房间时,房间ID需要在任何一台服务器都可以查询到,所以创建房间成功后,房间ID要存储在贡献内容Redis中,每个房间ID对应一个房间所在的IP或服务器ID。当有用户进入房间查询房间ID时,可以判断房间是否和自己登陆的游戏服是否相同。

  • 查找并加入房间

根据房间ID查找到房间后,获取房间所在服务器的ID或IP,如果和所登录的服务器相同,就可以直接加入房间。如果不一样,则要把房间所在服务器的ID或IP返回给客户端,让客户端重新与房间所在服务器建立连接,并使用登录时的token验证用户。

  • 游戏脚本调用

在验证游戏是否合法时,客户端和服务器都需要验证,验证的算法是一样的,使用Lua脚本编写一份脚本,在服务器和客户端都可以同时使用。同一个算法使用同一个脚本,在开发新的同类游戏时,只需要替换下脚本,也就不用再重复开发。

  • 后台管理系统

棋牌类后台管理系统会根据运营需求开发,需求不一。不过后台管理系统是要和游戏服务器通信的,这种通信方式最好是采用Redis的订阅发布机制,这样可以把某个消息事件同时发送到所有业务服务器上,并根据用户所在服务器进行处理。

  • 玩家同屏

玩家同屏是棋牌类游戏的一个重点,对应大型ARPG或MMO游戏来说,这并不是什么难事儿。因为同屏就是服务器对客户端的消息进行转发。由于棋牌游戏同步数据量比较小,常见的同步方式有两种:

  1. 客户端主动拉取

客户端定时主动向服务器请求一个用户的消息队列,当一个玩家有操作需要同步到其他玩家时,在服务器端先把消息放到用户的消息队列中,等待客户端的拉去操作。这种做法的好处是不需要考虑网络闪断或弱网环境,消息都是同步获取的。缺点是定时拉取的时间间隔很短,可能会不到一秒就要拉取一次。

  1. 服务器主动推送

当一个玩家出牌的消息需要同步给其他玩家时,服务器会获得这个玩家和服务器建立的socket连接,然后服务器使用socket主动向客户端发送消息。这种方式要考虑网络闪断造成消息丢失的问题。因为服务器推送的消息客户端可能会收不到,所以客户端需要根据心跳来判断网络是否断开连接,如果断开就需要重新从服务器拉取整个房间状态,或者是根据服务器发送的消息号,当客户端接收到的服务器消息号有跳号时,比如应该收到10却收到20说明中间有消息丢失,这个时候需要重新拉整个房间的状态信息。这种做法的缺点是开发复杂,需要考虑网络问题。优点是只有在有消息时才会推送,没有的话不推送,不占用带宽等网络资源,可以增加用户同时在线数量,也就增加了服务器的承载量。

  • 共享内存

共享内存在系统中的地位看上去很像是数据库前端的缓存,和《天龙八部》的ShareMemory类似,sharedb也采用定长的数据结构,通过共享内存来实现进程间的数据共享。sharedb的存在使得游戏逻辑处理和数据保存逻辑得到很好的隔离,游戏逻辑不必关心后端的数据是如何保存的,只要sharedb挂上定期存盘的服务,在接口定义明确的情况下,后端到底采用什么样的数据库变得不是那么重要,从而降低了系统的耦合度。

  • 数据同步和持久化

由于棋牌类游戏数据量少,计算量也很小,所以完全可以不使用内存缓存而直接使用Redis做共享内存,用户的所有数据都缓存到Redis中,更新也同步更新到Redis中,这样不管一个用户登录哪一台业务服务器都能获得自己的最新数据。

在更新数据库时由于数据第一缓存是Redis,所以活跃的用户数据都可以从Redis中直接获得,而不用查询数据库,所以数据库的更新可以采取异步更新,而不会产生数据延迟。但需要注意的是数据的异步更新必须是有序性的,那么这就产生了一个问题,如何保证用户的更新不会乱呢?

因为业务服务器是由多个的,用户可能连接到其中的任意一台,如果说登录的是服务器A加入的房间时服务器B,那么连接就会切换,为了保证数据更新的顺序,可以做一个数据持久化服务,将需要更新数据库的任务实时发送到这台服务器上,由数据库持久化服务执行对数据库的更新,这样不管用户连接到那台业务服务器它的更新都是由顺序的。

由于棋牌类的业务少数据更新少,所以查询可以有Redis缓存以减少数据库查询的压力,而更新实行实时更新到数据库,前期可以不需要开发数据持久化服务,等用户积累到一定数量后,发现更新数据库比较缓慢的时候再单独做一个数据库持久化服务。

Redis负责缓存,由于缓存的数据是最新的,通常会比数据库中的要新。例如在玩家登录游戏后,玩家所有的数据都以Redis中为准,Redis中的数据会定时同步到数据库中,同步的有游戏服务器中的模块同步,另外一般5分钟同步一次所有变更的玩家数据。如果Redis启动了RDB快照,则会自动定时同步数据到磁盘。

  • 存储方案

网关维护一份当前启用的游戏数据并存储在Redis中,采用哈希的结构。游戏服连接到网关后,网关记录启用的游戏到Redis中,登录服在用户登录时需要从这里读取启用的游戏列表。

网关和游戏服共同维护一份在线玩家数据并采用哈希结构,玩家加入房间成功后由游戏服设置在线数据,玩家推出时由游戏服更新数据并同步到客户端。

玩家在游戏中的各种行为由游戏服保存在进程内存中,玩家账户变更时由游戏服直接更新Redis中的用户数据,玩家道具变更时由游戏服中的异步更新模块负责更新并同步到客户端。

玩家断线时,网关捕获到网关断线,直接通知游戏服进行相应逻辑处理并更新共同维护在Redis中的数据。

网关断线时游戏服捕获到网关断线,而对于扎金花、斗牛等这种超时自动棋牌的游戏,游戏服需要设置当前游戏中的所有玩家断线。对于斗地主这种超时则对服务器进行自动托管而不做任何操作,当游戏结束后30秒不准备则会自动踢人,并共同维护Redis中的数据。

网关启动时,当玩家重新连接网管,此时检查玩家是否在游戏中,如果在则立即返回并不做任何操作。

游戏服断线时网关捕获到,会从Redis中删除游戏数据并通知所有在断线游戏中的玩家游戏断线返回大厅,并删除用户在线数据中的游戏字段。

游戏服启动之后连接网关,网关注册游戏数据并保存到Redis中。

义乌市森焱网络公司专注开发棋牌游戏十一年,本着顾客第一,质量第一,售后第一的理念用心做好每一款游戏,森焱有你们才精彩!!!

  • 打赏
  • 分享
分享到...
棋牌游戏服务器内部架构基本功能解析和需要注意的东西
请选择打赏方式
棋牌游戏服务器内部架构基本功能解析和需要注意的东西
棋牌游戏服务器内部架构基本功能解析和需要注意的东西 棋牌游戏服务器内部架构基本功能解析和需要注意的东西
  • 微信
  • 支付宝