游戏登录排队( 限流)


文章目录

  • 游戏登录排队( 限流)
  • 前言
  • 为什么要排队(限流)
  • 限流算法
  • 固定窗口实现的登录限流流程图:
  • 代码实现
  • 生产环境下的限流组件
  • 参考


前言

能看到这篇帖子的兄弟,可能是想实现一个游戏登录排队的功能。游戏的登录排队机制,其实就是 服务的限流 、削峰机制,如果 你使用 “限流” 关键词可能会搜索到更多的相关资料。

为什么要排队(限流)

最近我司游戏要上线了,目前全平台总预约量有600多万。服务器目前的配额,压测目标是,允许250万玩家同时在线的情况下,服务依然稳定。但是游戏上线开服第一天,假设所有玩家(假设是压测的250万玩家)第一时间注册上线,那么带来的 DB写操作,将是噩梦。服务器本身可以支持250w玩家同时在线,但是db并不能满足250w玩家短时间的写操作。玩家注册操作的写db操作比较重。 所以就需要削峰、限流。也就是这里的登录排队。

假设数据库写操作最大支持 5000 次/秒,玩家注册时写 db 频率是 2 次/秒。那么 db能够承受的玩家登入速度 是 2500 人/秒。所以如果 一秒钟登录的用户超过2500人后,后来的用户登录就需要排队,至少要等到下一秒才能允许登录。

限流算法

  1. 固定窗口:
  2. 滑动窗口:
  3. 漏斗桶算法:
  4. 令牌桶算法:

固定窗口和滑动窗口都是计数法。

服务器架构图

为了方便理解,这里先介绍一下我的项目简易架构图。

redis的出流量是指查询还是写入 redis流量削峰_游戏排队

  1. 项目采用了身份认证系统与游戏业务系统分离,客户端(手机)使用http请求向 login服发起 身份认证请求。如果认证通过,login会在回包中告诉 客户端 gateway地址。
  2. 客户端获得gateway地址后,会去连接对应的gateway。 进入游戏逻辑。
  3. 客户端与 game服(游戏服)器通信必须进过gateway服。

登录排队机制应该在哪一步?

  1. 在client->login 的过程中
  2. 在 client -> gateway的过程中

不忘初心:前面“为什么需要限流” 中说明了,是因为服务器db扛不住压力,所以排队操作一般会放在数据库操作之前。

这里我们采用了方案一, 在login服上进行排队。登录验证使用的HTTP协议。短链接的HTTP 会有一个麻烦,当玩家排队结束时(轮到玩家登录了),服务器不能够主动告诉客户端 “到你了,轮到你登录了”。所以服务器只能在每次客户端登录请求的回复中 带上 一个等待时间。告诉客户端还需要等多久。 客户端收到这个时间后,自己在等待时间结束后重试。

如果采用方案二,客户端与服务器是长链接,可以在客户端的排队等待时间结束后,主动告诉客户端。(TCP是全双工通信,双方都可以发主动发送消息、接收消息。)

为什么要使用reids实现?

其实就是计数法,这里采用的固定窗口算法。每个时间片有一个计数。但是现在服务端是分布式的,会有多个login进程,它们可能就不在一个机器上。所以这个 计数 必须放在所有进程都可以访问的地方,这里就是采用的redis了。所有进程上的排队逻辑都访问同一个reids仓库,进行计数。redis实现的分布式锁也是这个原理,把锁放在所有进程都可以访问的redis上。

我实现的登录排队系统的功能:

  • 固定时间片中只允许一定数量(窗口大小)的玩家登录
  • 时间片和窗口大小可配置
  • 玩家排队时,告诉客户端当前排名和等待时间
  • 排队系统开启关闭可配置

固定窗口实现的登录限流流程图:


Created with Raphaël 2.2.0 start 获取当前时间片 是否在登录队列 获取在登录队列的排名 否有剩余窗口? 抢占登录名额是否成功 登录成功 end 返回等待时间,登录等待 加入登录队列 yes no yes no yes no


代码实现

redis实现的固定窗口算法限流

生产环境下的限流组件

一般而言我们不需要自己实现限流算法来达到限流的目的,不管是接入层限流还是细粒度的接口限流其实都有现成的轮子使用,其实现也是用了上述我们所说的限流算法。

比如Google Guava 提供的限流工具类 RateLimiter,是基于令牌桶实现的,并且扩展了算法,支持预热功能。

阿里开源的限流框架Sentinel 中的匀速排队限流策略,就采用了漏桶算法。

Nginx 中的限流模块 limit_req_zone,采用了漏桶算法,还有 OpenResty 中的 resty.limit.req库等等。

参考

限流算法概述springboot限流机制