# 《Lottery 分布式抽奖系统》,关于面试中的技能、简历、问题汇总

作者:小傅哥
博客:https://bugstack.cn (opens new window)
课程:https://t.zsxq.com/0d7K7hJ0i (opens new window)

沉淀、分享、成长,让自己和他人都能有所收获!😄

此部分主要用于向读者提供星球项目之一的 Lottery 分布式抽奖系统如何体现到简历中,包括;专业技能、项目经验。

# 一、项目介绍

面试官您好,Lottery 是我的一个(学习/工作)项目,此项目不只是一个简单单一的抽奖,而是符合营销平台架构设计具备可扩展性的微服务架构设计。核心流程为根据不同人群标签的人群规律,选择不同的抽奖活动,每个活动的参与为一个抽奖单。可以有效的控制参与用户数和异常流程的补偿。领取抽奖单后执行使用了模板、工厂、策略的抽奖玩法设计。在这里设计了分段锁,避免独占锁的竞争,从而挺高效率。最后抽奖完成异步发送 MQ 消息方式进行驱动后续的发奖流程。

Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案,让我在这个过程中学习到非常多的内容,这写技术学习的内容,也可以更好的应对以后的开发工作。非常感谢您给我这次面试机会。

# 二、简历模板

  • 项目名称:营销活动平台 - Lottery 微服务抽奖系统
  • 系统架构:以 DDD 领域驱动设计开发,微服务拆分的分布式系统架构
  • 核心技术:SpringBoot、MyBatis、Dubbo、MQ、MySQL、XDB-Router、ES、ZK
  • 项目描述:抽奖系统是营销平台的重要微服务之一,可以满足 C 端人群的需求,例如拉新、促活、留存等。该系统运用抽象、分治和 DDD 知识,拆解服务边界,凝练领域服务功能。围绕抽奖服务建设领域服务,包括规则引擎、抽奖策略、活动玩法、奖品发放等。这可以满足业务产品快速迭代上线的需求,同时减少研发成本,提高交付效率。
  • 核心职责
    • 【高级】构建以 DDD 分层结构的处理方式,搭建整个抽奖系统架构。运用设计原则和工厂、代理、模板、组合、策略等设计模式的综合使用,搭建易于维护和迭代的系统工程。
    • 【高级】鉴于系统内有较多的规则策略过滤,包括准入、人群、风控、A/BTest等需求,为适应系统规模可快速开发和使用的方式,搭建了去中心化的量化人群规则引擎组件。通过业务需求对逻辑的扩展和内置引擎执行器的使用,完成自由组合的人群过滤服务。这降低了共性功能重复开发所带来的成本问题,并提高了研发效率。
    • 【高级】根据实际秒杀峰值场景 TPS 5000 ~ 8000 的需求,开发了统一路由组件。该组件不仅可以满足差异化不同字段的分库分表组合,还支持 Redis 库存分片和秒杀滑动库存分块。而且,开发了统一路由 XDB-Router 的 SpringBoot Starter 技术组件。该套组件已经经历了多次大促活动场景的考验,支持横向扩展,可以满足业务规模的快速增长。
    • 【简单】运用模板、策略、工厂三个设计模式,定义抽奖过程标准和实现对应的多类型抽奖的服务模块。
    • 【简单】因活动秒杀的并发场景,将秒杀从最开始的数据库行级锁优化为Redis Key 加锁,又从 Redis Key 的独占锁,优化为滑块锁。优化后整体秒杀有了非常可观的性能提升。
    • 【简单】解耦抽奖流程,把抽奖和发奖用MQ消息串联起来,避免一个流程太长,导致用户一直等待。

# 三、项目问答

# 1. 营销架构介绍

鉴于有些伙伴在面试时候,不知道怎么叙述抽奖这个微服务所在的位置,特此补充图

  1. 营销,是一个非常庞大的系统体系。包括众多的模块组成,其中抽奖只是一个重要的微服务之一。
  2. 包括,营销平台、返利平台、积分账户、抽奖系统、券系统、灌券系统、售卖系统,以及各类玩法的组件系统。它们的关系如图所示。

# 2. 为什么自研路由组件

如果面试问:“为什么要自研,市面不是有吗,怎么回答?” 可以从以下3个点解答;

  1. 维护性;市面的路由组件比如 shardingsphere 但过于庞大,还需要随着版本做一些升级。而我们需要更少的维护成本。
  2. 扩展性;结合自身的业务需求,我们的路由组件可以分库分表、自定义路由协议,扫描指定库表数据等各类方式。研发扩展性好,简单易用。
  3. 安全性;自研的组件更好的控制了安全问题,不会因为一些额外引入的jar包,造成安全风险。

当然,我们的组件主要是为了更好的适应目前系统的诉求,所以使用自研的方式处理。就像shardingsphere 的市场占有率也不是 100% 那么肯定还有很多公司在自研,甚至各个大厂也都自研一整套分布式服务,来让自己的系统更稳定的运行。分库分表基本是单表200万,才分。那么面试怎么说;

你们为什么分库分表?

  1. 我们分库分表用的非常熟。但不能为了等到系统到了200万数据,才拆。那么工作量会非常大
  2. 我们的做法是,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。
  3. 同时,抽奖系统,是瞬时峰值较高的系统,历史数据不一定多。所以我们希望,用户可以快速的检索到个人数据,做最优响应。因为大家都知道,抽奖这东西,push发完,基本就1~3分钟结束,10分钟人都没了。所以我们这也是做了分库分表的理由。

# 3. 规则引擎的设计目的

  1. 面试官您好。关于规则引擎这块,首先;这是一个基于降低重复编码和提高可维护性的,并需要符合当前项目诉求的,同时不过渡的设计和减少运维成本的前提下,在技术调研后所做的微型规则引擎设计实现。
  2. 此规则引擎的主要作用是解决,抽奖业务场景中对个性化运营诉求的处理,如;人群身份标签、交易记录、活动资格、授信状态等规则的可配置化的交叉使用。
  3. 所以基于这样的情况,此规则引擎的设计是一个二叉树判断,实现手段运用到了组合模式、工厂模式等。并为了便于维护和使用,进行了库表对二叉树的抽象设计,树根、节点、子叶,映射为二叉树编码的相关属性信息。同时,也可以基于这样的库表做前端页面的托拉拽配置操作,降低运营成本。
  4. 最后,其实动态的规则引擎配置,其实放大了看就是 BPMN + Drools + Groovy,的一个低代码实现框架。综上,面试官这个就是我在做规则引擎设计的一些思考、调研和落地。如果以后咱公司有其他更大的场景,我也可以扩展为 Reta 算法和低代码方案进行架构落地实现。

# 4. 秒杀的滑块锁讲解

  1. 是针对于用户参与的活动库存加锁的,如果是独占锁是针对于活动ID加锁的。
  2. 滑块锁的核心是去竞态,避免独占影响系统的响应性能。关于此类锁,这里又做了视频做了详细的讲解;Redis | bugstack 虫洞栈 (opens new window) - 如图。别看提秒杀一堆人说,但如果还讲用独占锁做活动的秒杀场景,就没做过大规模的秒杀。独占是会很大概率出事故的。
  3. 那为什么加一个锁呢,incr 不就可以。加锁是兜底,你不知道什么时候会出现 incr 不对的情况。如;集群配置问题【特例】、出现redis问题,需要恢复库存。如果没有锁,可能会超卖。https://t.zsxq.com/12sNS4E2J (opens new window) - 第一条评论加了说明。
  4. 对于非交易的活动类场景,要的就是一个快。快速响应、快速释放,可接受容错失败概率。但不要磨磨唧唧影响我的主核心交易链路。但凡在618、双11,营销敢超时,就直接下掉。保证用户可下单可支付。否则这黄金时间点,你耽误1分钟都是几个亿的成交额。所以,这类营销秒杀场景下,根本就是保证不超卖,也不恢复库存。

注意:独占锁是加给个人流程的 - 无资源竞争,如贷款单受理。分段/滑块/无锁化,是加给库存的 - 有资源竞争,如秒杀、商品发货等集中资源类。就跟大超时的收银台一样。原来就1个出口,后来一排出口,在后来又有无人化的电子出口。点点那个软件。

举例;incr 的速度很快,就像进入了公共的卫生间🚾。一个坑一个门,谁进去谁就锁上。没有就跑到下一个门。你说你不锁门吧,也没问题。但别人不知道,一拽开就比较尴尬。所以要加锁,锁门。

# 四、面试刷题