快速全局分布式用户配额管理的架构

Dan*_*ber 5 sql architecture postgresql web-services web

我们已经构建了一个免费的全球分布式移动分析REST API.这意味着我们的服务器遍布全球,运行相同应用程序的不同版本(美国,欧洲等).这些服务是负载均衡器的背后,所以如果他/她今天或明天都有请求,我不能保证同一个用户总是得到相同的应用程序/服务器.API是公开的,但用户必须提供API密钥才能让我们将其与付费请求配额相匹配.

由于我们对每个请求进行大量数字运算,因此我们希望尽可能减少请求时间,特别是对于身份验证/授权和配额监控.由于我们目前仅使用一个用户数据库(必须位于单个数据中心),因此有时美国用户向美国的应用程序/服务器发出请求,该应用程序/服务器在欧洲对用户进行身份验证.所以我们正在寻找一个用户数据库交互的解决方案:

  1. 发生在同一个应用服务器上
  2. get在所有应用程序服务器之间同步
  3. 应该很容易集成到java应用程序中
  4. 应该很快(每个请求都会发生变化)

我们到目前为止所做的事情:

  • 每个服务器上的单个数据库>未同步,噩梦
  • 所有服务器的单个数据库> ok,当与slave一起使用作为后备但是美国用户必须在Atlantic上进行身份验证
  • 开始安装bdr但在路上失败了(没时间,太复杂,难以转换)
  • 看着redis.io

由于这是我的第一个全球分布式REST API,我想知道其他公司是如何做到的.(yelp,谷歌等)

任何反馈都非常感谢,

干杯,

丹尼尔

gro*_*mal 7

没有正确的答案,有几种方法可以执行此操作.我将描述我最熟悉的那个(这可能是最简单的方法之一,尽管可能不是最强大的方法之一).

1.单独的身份验证和授权

首先是单独的身份验证授权.跨大西洋进行身份验证的用户很好,每个需要授权才能跨越大西洋的用户请求都不是.主用户凭证(例如密码哈希)是集中式资源,没有办法解决这个问题.但是,每次需要授权请求时,您都不需要主凭据.

这就是我工作的公司做的事情(尽管这是postgres/ python/ django,与此无关java):

  • 每个服务器都有一个数据库查询缓存,memcached但它不缓存用户身份验证(memcached非常类似于redis您提到的).
  • 始终在主数据中心执行身份验证.
  • 成功的身份验证会生成一天后过期的用户会话.
  • 会话被缓存.

这可能产生这样的情况,其中用户可以在给定时间具有由多于一个会话授权的动作.算法是:如果至少一个用户会话尚未到期,则授权用户.由于高速缓存是本地的,因此不需要为每个请求在大西洋上获取数据的昂贵操作.

2.会话复兴

在一天之后过期的会话可能对用户来说很烦人,因为他永远无法确定他的会话在接下来的几秒钟内是否会过期.由会话授权的用户做出的每个请求都应该将会话生存期再次延长到一整天.

这很容易实现:您需要在会话中保留timestamp最后一个请求.并根据它计算会话生命周期timestamp.如果多于一个会话可以授权请求,则算法应选择更新的较年轻会话(剩余最长寿命).

3.请求记录

这与我合作的实时系统一样.答案的其余部分是关于如何扩展这样的系统以便为记录请求和验证请求配额腾出空间.

假设

让我们首先做一些设计假设,并争论为什么它们是好的假设.您现在正在运行分布式系统,因为并非系统上的所有数据都在一个位置.分布式系统应优先考虑响应速度并尽可能保持水平(非分层),以实现这种一致性.

上面的会话机制已经牺牲了一些一致性.例如,如果用户登录并连续通话server A一天半,然后负载均衡器指向用户server B,则用户可能会惊讶于他需要再次登录.这是一个不太可能的情况:在大多数情况下,负载平衡器将划分用户的请求,同样在两个server Aserver B以上的那一天半的课程.而且两者,server A并且server B将在该点的现场会议.

在您的问题中,您想知道谷歌如何处理请求计数.我会在这里争辩说它以不一致的方式处理它.通过这种方式,我的意思是,如果您牺牲一致性,则无法强制执行严格的请求上限.谷歌或yelp等公司只是说:

"你每月有12000个请求,但如果你做的不止这些,那么每10个请求就要支付0.05美元".

这样可以实现更简单的设计:您可以在请求发生后的任何时间对请求进行计数,计数不需要实时进行.

最后一个假设:分布式系统会出现重复内部数据的问题.发生这种情况是因为系统的某些部分是实时运行的,而且部件正在进行批处理,而不会停止或为实时系统加时间戳.之所以发生这种情况,是因为您无法100%确定任何给定点的实时系统状态.来自客户的每个请求都必须具有某种唯一标识符,它可以像客户编号+序列号一样简单,但它需要存在于每个请求中.您也可以收到请求时添加此类唯一标识符.

设计

现在我们扩展在每个服务器上缓存的用户会话(通常在不同的服务器上以不同的状态缓存,彼此不知道).对于每个客户请求,我们将请求的唯一标识符存储为会话缓存的一部分.没错,中央数据库中的请求计数器不会实时更新.

在某个时间点(例如,日终处理),每个服务器执行请求标识符的批处理:

  • 实时会话重复,副本已过期.
  • 过期会话中的所有请求标识符连接在一起,并写入中央数据库.
  • 所有过期的会话都被清除.

这会产生竞争条件,其中实时会话可以在服务器与中央数据库通话时接收请求.因此,我们不会从实时会话中清除请求标识符.反过来,这会导致一个问题,即请求标识符可能会被记录到中央数据库两次.因此,我们需要标识符是唯一的,中央数据库应忽略已记录标识符的请求更新.

好处

  • 99.9%的正常运行时间,批处理不会中断实时系统.
  • 减少对数据库的写入,并减少与数据库的通信.
  • 容易横向生长.

缺点

  • 如果服务器出现故障,恢复执行的请求可能会很棘手.
  • 没有办法阻止客户做出比他被允许的更多请求(这是分布式系统的特性).
  • 需要为所有请求存储唯一标识符,计数器不足以衡量请求的数量.

开票用户不会更改,您只需查询中央数据库并查看客户执行了多少请求.