首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

如何设计一个小而美的秒杀系统?-2

如何设计一个小而美的秒杀系统?-2

精简 RPC 调用服务请求流程通常是在接入层访问用户中心进行用户鉴权,然后转发请求到后端服务,后端服务根据业务逻辑调用其他上游服务,并且查询数据库资源,再更新服务/数据库的数据。每一次 RPC        调用都会有额外的开销,所以,比如上面一点所说的预加载,使得每个节点在系统运行期间都有全量的查询数据可在本地访问。抢红包的核心流程就被简化为了生成红包和人的映射关系,以及发放红包的后续操作。再比如,我们采用了异步拉的方式进行红包发放到账,用户抢红包的请求不再经过发放这一步,只记录关系,性能得到进一步提升。        如图 2 所示。
图 2. 服务依赖精简示意图实际上有些做法的可伸缩性是极强的。例如红包数据的预生成信息,在当时的场景下是能够作为本地内存缓存加速访问的。当红包数据量很大的时候,在每个服务节点上使用本地数据库、本地数据文件,甚至是本地        Redis/MC 缓存服务,都是可以保证空间足够的,并且还有额外的好处,越少的 RPC,服务抖动越少,我们只需要关注系统本身的健壮性即可,不需要考虑外部系统 QoS。
抢红包的并发请求处理春节整点时刻,同一个红包会被成千上万的人同时请求,如何控制并发请求,确保红包会且仅会被一个用户抢到?
  • 做法一:使用加锁操作先占有锁资源,再占有红包。
可以使用分布式全局锁的方式(各种分布式锁组件或者数据库锁),先申请 lock 该红包资源且成功后再做后续操作。优点是不会出现脏数据问题,某一个时刻只有一个应用线程持有        lock,红包只会被至多一个用户抢到,数据一致性有保障。缺点是,所有请求同一时刻都在抢红包 A,下一个时刻又都在抢红包 B,并且只有一个抢成功,其他都失败,效率很低。
  • 做法二:单独开发请求排队调度模块。
排队模块接收用户的抢红包请求,以 FIFO 模式保存下来,调度模块负责 FIFO        队列的动态调度,一旦有空闲资源,便从队列头部把用户的访问请求取出后交给真正提供服务的模块处理。优点是,具有中心节点的统一资源管理,对系统的可控性强,可深度定制。缺点是,所有请求流量都会有中心节点参与,效率必然会比分布式无中心系统低,并且,中心节点也很容易成为整个系统的性能瓶颈。
  • 做法三:巧用 Redis 特性,使其成为分布式序号生成器(我们最终采用的做法)。
前文已经提到,红包系统所使用的红包数据都是预先生成好的,我们使用数字 ID 来标识,这个 ID 是全局唯一的,所有围绕红包的操作都使用这个 ID        作为数据的关联项。在实际的请求流量过来时,我们采用了"分组"处理流量的方式,如下图 3 所示。
访问请求被负载均衡器分发到每个 Service        Cluster 的分组 Bucket,一个分组 Bucket 包含若干台应用容器、独立的数据库和 Redis 节点。Redis 节点内存储的是这个分组可以分发的红包 ID 号段,利用 Redis 特性实现红包分发,各服务节点通过 Redis 原语获取当前        拆到的红包。这种做法的思路是,Redis 本身是单进程工作模型,来自分布式系统各个节点的操作请求天然的被 Redis        Server 做了一个同步队列,只要每个请求执行的足够快,这个队列就不会引起阻塞及请求超时。而本例中我们使用了 DECR 原语,性能上是可以满足需求的。Redis 在这里相当于是充当一个分布式序号发生器的功能,分发红包 ID。
此外,落地数据都持久化在独立的数据库中,相当于是做了水平分库。某个分组内处理的请求,只会访问分组内部的 Redis 和数据库,和其他分组隔离开。
整个处理流程核心的思想是,分组的方式使得整个系统实现了高内聚,低耦合的原则,能将数据流量分而治之,提升了系统的可伸缩性,当面临更大流量的需求时,通过线性扩容的方法,即可应对。并且当单个节点出现故障时,影响面能够控制在单个分组内部,系统也就具有了较好的隔离性。
图 3. 系统部署逻辑视图
返回列表