抽奖系统的简单设计

抽奖是一种业务上经常出现的营销方式,也是一种经常会出现高并发的应用场景。抽奖系统是一个相对独立、应用场景较为广泛的业务,可以考虑进行系统模型抽象,提供系统级业务复用的能力,系统单独维护、单独部署,保证通用性和可伸缩性。

抽奖系统模块设计

基础模型

抽奖系统的模型最初设计比较简单,只需要有一个奖品池,维护一个奖品记录,中奖概率。各种业务场景下的抽奖活动都具备这些因素,只是用户获得抽奖机会的方式可能会不尽相同,此外在一些细节处有一些定制化的需求。

用户请求通过ng打到服务层,服务层进行抽奖资格的校验,然后进行抽奖,最后再同步的进行抽奖服务和履约触达,每次抽奖都会直接与db层进行若干层交互,这期间可能会有多次事务,保证奖品不会超抽以及用户不会多抽。

模块抽象

各个业务场景下的抽奖,用户的抽奖资格可能是通过多种不同的途径获得,不易抽象处理,因此这个模块可以每个业务自己实现处理(也可以预定义一些抽奖资格的途径方法作为可选项)。

抽奖平台需要包含以下几个模块。

  • 奖品模块: 一个奖品是一次抽奖行为中用户获得一系列奖励的组合,奖品模块应当具备对各种不同类型的奖励资源的打包能力,用户抽中该奖品之后可以一次性的获得该奖品下对应的所有奖励,对上游的所有业务暴露统一的“奖品ID”。由于很多抽奖的行为都需要组合不同的资源打包处理,因此奖品模块可以收敛到抽奖平台处理。
    1. 物料组装:负责物料的组装打包,各种资源位的信息等。
    2. 奖品中奖群体规则:在实际场景中,用户不能抽中某些奖品也是非常常见的,例如已经用户已经买了三科体验课此时就不能抽中体验课代金券,用户已经有了某科主题课之后原则上也不能抽中该主题课等。这个应该是对所有抽奖活动通用的限制用户的奖品维度的抽奖规则。但是由于效率原因,中奖规则的计算不应该每次实时的去查各种数据处理,而应该接入用户画像规则,在此框架下进行限制。现有技术背景下可以选择用户群组,用户标签等加以实现。
  • 活动模块: 活动模块是抽奖活动的主体,标识每一次抽奖活动,一般来说,一次抽奖就是一个活动对象。
    1. 活动奖品:活动奖品维护奖品模块下的一些奖品在本次活动中的一些个性化的信息,例如库存,中奖概率等信息。
    2. 活动规则:该模块只是用于活动规则的信息展示,但是这块可以抽象出一些基本的活动规则,并在抽奖活动前加以校验,例如,很多活动都是所有用户均可参加,如春晚抽奖,元旦跨年抽奖等。
  • 抽奖模块:根据配置的活动规则,活动奖品,校验用户的抽奖资格,奖品的库存等信息,完成一次抽奖行为。
  • 履约/触达模块:履约触达模块应当与能够对不同类型的抽奖活动以及不同的奖品类型提供统一的履约和触达逻辑,履约模块分为实时和延时两种类型,不同的活动奖品可以选择是否延时发送,例如实物奖品需要收集发货地址,因此需要延迟几天推单,优惠券/主题课等奖品则是越快完成履约越好。
    1. 奖品履约
    2. 部分奖品触达
  • 安全风控模块:抽奖活动会面临着黑产的风险,这将导致很多奖品都被黑产给抽走,无法有效衡量活动的预期效果,且带来了一定的经济损失,因此,长远来看,集成抽奖平台的安全风控模块是有必要的。
  • 其他模块:上面的几个模块已经基本满足了现有场景下的大部分抽奖活动的需求。但是还会有一些其他的场景,可能需要以下模块的支持:
    1. 概率组模块:有一些场景要求根据一些特定的行为赋予用户不同的中奖概率,例如邀请新用户提升中奖概率,玩游戏充钱必得特定奖品等。还比如产品有用户每次不同的抽奖会有不同的中奖概率,第n次必得某奖品等。这种场景下要求抽象出概率组的模型,对用户在不同的抽奖行为中赋予不同的抽奖概率。
    2. 计数模块:现有的抽奖系统能保证抽奖到履约触达的一条链路,但是不能满足对一些实时性以及关键性指标可见的需求。大型的抽奖需求,都是通过grafa大盘等进行打点统计。例如:本次抽奖平台有有统计每次履约出现异常的信息的需求。

如下图所示,抽奖平台可以将各个部分模块化。

  • 对于运营来说,有新的抽奖需求时,运营的操作流程应该为:
    • 组装奖品,并对每个奖品限制特定标签的用户方可中奖。
    • 配置抽奖活动数据,限制用户参与活动的规则,限制当期活动中奖品的库存、中奖概率
  • 对于研发来说:
    • 当前设计中,每次不同的抽奖活动需要对应业务方的研发判断用户是否获得抽奖资格,该部分需要参与。(考虑设计几个通用场景,避免每次都要修改代码)
  • 对于安全同学来说:
    • 可以对整个抽奖平台使用一套安全体系

抽奖系统性能优化

抽奖平台可以满足大部分抽奖需求的功能可用性,但是对于一些流量比较大的秒杀性质的抽奖场景,该设计的性能将会受到挑战。

一般来说,提升系统应对高并发能力的方式有两种:限流削峰、系统性能优化

整体架构为:

限流削峰

限流削峰的主要目的是减少真正执行业务逻辑的并发量,可以采用以下方式来进行限流。

  • 前端限流:高并发抽奖场景下,前端都需要限制用户在若干时间内的请求次数,这样可以拦截很多流量,而且可以减少同一用户并发请求带来的问题。

  • Nginx限流:负载均衡层是所有流量的第一层防线,在nginx可以加一些限制连接规则,例如防cc(Challenge Collapsar)机制,限制一个IP下每分钟的最高请求数,超出则直接拒绝请求,这样可以避免脚本工具刷接口。(在一些秒杀场景下,可以在ng上维护与应用共享的状态变量,状态变更为已抽完之后,直接返回预设的状态码)

  • 应用层限流:此时,所有的请求都将在系统中执行,此时可以在代码层面进行削峰限流。

  • 图片

    • 应用保护限流:对系统进行压测,得出系统能够承受的最大并发量,超出则直接拦截返回异常信息,这样牺牲了一定的用户可用性。如rateLimter等。
    • 风控识别:通过接入安全风控系统,对一些已知的异常账号或者异常设备进行拦截,这样可以过滤掉很多非法的请求,从而使更多的正常请求能够继续执行。可以采用的风控策略为实时和离线两种:实时方式需要安全同学的算法实时计算,捕获异常的请求;离线可以采用历史黑名单机制等加以实现。1号店抽奖系统接入安全风控之后的流量对比如下:
  • 规则限流:为了避免更多的请求到达抽奖平台的下游,需要在抽奖之前校验用户的资格,校验抽奖规则等。但是要求这些校验行为支持高并发,且响应快,同样也需要对规则限流接口加以rateLimter限流。

性能优化

性能优化是全局系统性的问题,优化不能单单局限代码,JVM的层次,从页面到硬盘,一定要通盘考虑。在遇到性能瓶颈时,不要只从表面的代码排查问题,要深入,网络,硬件都有可能瓶颈。

一般可以采用以下几种方式来进行优化。

  • 数据库:一般机器并发量可以通过加机器来解决,但是不能解决数据库的瓶颈,因此需要降低与数据库的交互。
    • 减少事务的使用:事务的开启和结束操作会比较耗时而且会导致一次数据处理长期占用一个连接,因此可以尽量少用事务或者不用事务,通过乐观锁,唯一键等其他方式保证最终一致性。
    • 采用读写分离来提升读接口的性能,但是有一些写后读的场景,需要注意主从延迟的影响
    • 分布式缓存:可以采用redis等分布式缓存来减少与数据库的并发读写操作
    • 本地缓存:采用本地缓存加载常用的配置等改动较少的热数据,例如活动规则,活动配置,奖品数据等
    • 缓存:
    • 读写分离
    • 升级硬件,采用内存数据库等
    • 拆分数据库:若量级足够大,需要把数据库给拆分出来,并且位于不同的实例下,将资源准备若干份,用户请求路由到不同的实例中(一般没必要)
  • 消息队列: 在一些对实时性要求不高的场景中,可以采用消息队列来进一步削峰,履约可以采用这种策略。
  • 接口异步处理: 对一些没有必要实时返回的接口调用,采用异步操作处理,减少耗时,提升系统应对并发的能力。
  • 其他代码层面优化

因此,抽奖系统的架构设计可以进一步修改为:

图片

标签:

后端架构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值