项目

项目面经里面可以写关于自己项目可以会被问到的所有问题,包括但不限于场景题,常规八股,调优题……

Yottery

为什么会选择用DDD架构呢,他跟传统MVC架构的区别或者说优势是什么

因为DDD领域驱动设计的话,它是将整个业务或者说整个系统,大比例的分隔开来,以达到高内聚,低耦合的效果。它主要讲系统分成这样几层:

  • 应用层:主要负责服务之间的组合和编排,对外提供粗粒度的服务。

  • 领域层:主要负责各个领域业务的实现,所有领域方法和服务等均须通过领域服务对外暴露

  • 基础层:提供数据库服务,仓储服务,缓存服务等基础服务

  • 接口层:用于处理用户发送的restful请求并将其传输给应用层

就以上来看,DDD架构设计的系统更适合于“命长”的系统,因为这样设计的话后期维护会更自然,贴近与实际。不会像MVC那样一个DTO包里面有成百上千个DTO,然后每个DTO之前有50%的字段都是基本上一样的,导致类爆炸

你简历中写的“去中心化的量化人群规则引擎组件”其中什么是去中心化

这个去中心化指的是不需要把所有的规则引擎服务都集中,不依赖于统一中心的链接,所有的规则都是独立的服务,只依赖于系统本身就可以启动运行

抽奖业务的实现流程是怎么样的

首先是用户领取活动:

就先查询是否存在执行抽奖活动单。如果存在的话就说明已经抽过了;不存在的话就继续执行下去。然后再查询活动的信息,校验一下活动的库存,日期,和个人参与次数是否校验通过。接着就是扣减活动的库存,因为这个现场是有大约500人同时进行抽奖的嘛,就属于秒杀场景,就需要使用Redis分布式锁(使用Redis.setNx 加一个分布式锁)来处理集中化库存扣减的问题,通过Redis,为了缩小锁的颗粒度将活动库存扣减编号作为锁的Key,在此期间还需要做超卖的判断,如果超出库存,就进行库存回滚(redis.decr)

然后就是首次成功领取活动成功的话,就发送MQ消息进行异步的解耦,更新用户领取活动之类的信息

完了过后就是执行抽奖,根据对应的抽奖算法进行处理,完了过后返回中奖结果

最后根据中间结果到数据库中扣减库存,这个过程中,在数据库中锁定活动领取记录并且保存抽奖记录,在一个事务里面

然后还有后续的发奖流程,是通过MQ实现的,如果消息发送失败了,就更新数据库中一个状态字段为1;成功了的话就更新为2

大概上就是这样一个流程

你说抽奖算法的复杂度达到了O(1),你是怎么实现它的

首先我是先将【概率与奖品对应的散列结果】放到一个ConcurrentHashMap里面,在计算每个奖品的概率范围值时,使用了斐波那契散列增量的算法,避免了散列冲突。

然后再执行抽奖的时候,我通过传过来的活动id,从这个map中获取对应的元祖。使用SecureRandom的nextInt方法获取一个随机索引,然后通过波那契散列增量算法获得散列索引,用这个散列索引在元祖里面找是否存在对应的奖品,如果不存在就返回未中奖;存在的话就返回奖品ID

总体来说就是通过契散列增量算法得到对应的索引,在map里面寻找就完了

你是怎么设计你的业务ID的呢,保证是分布式ID

我这里直接将生成ID服务抽取出来作为一个领域,专门为了生成分布式ID。

这个服务一共有三种ID策略:

  • 短码生成策略:但是仅支持很小的调用量,用于生成活动配置类编号,保证全局唯一

  • 11位随机数字生成策略

  • 雪花算法:也是我最终选择的

为什么要是用雪花算法生成ID,主要是为了满足分布式场景(比如说分库分表数据量很大的场景)

其优点是:

  1. 唯一:(主键id不能重复)

  2. 单调递增:配合mysql中InnoDB存储引擎中b+树的索引结构提高效率

  3. 不依赖第三方库:雪花算法依赖机器时钟,多台机器使用不同步,会导致重复id生成

除了雪花算法,你还知道哪些分布式id解决方案

​ 除了雪花算法之外的话最常见的就是UUID,它是一个128位的数字标识符,可以保证在全球范围内唯一。UUID算法通常使用当前时间、随机数和网络地址等信息生成。

​ 然后还有一个我知道的就是美团的Leaf,它的核心思想是将ID生成的工作分配给一个中心化的节点,这个节点负责生成唯一的ID。通过将ID生成的工作集中在一个节点上,可以避免多个节点之间的竞争和冲突,提高ID生成的效率和可靠性。

​ 这两者的区别就是说,如果要求ID的长度较短,可以选择UUID算法;如果要求ID生成的可靠性和稳定性较高,可以选择Leaf算法。

Last updated