介绍项目:
面试官好,抽奖系统是我的一个学习项目,核心流程是根据不同人群标签,通过规则引擎选择匹配对应的抽奖活动。每个活动对应一个抽奖单,可以有效控制参与用户。领取抽奖活动后使用了模板、工厂、策略模式进行抽奖模块设计。抽奖完成通过异步发送MQ消息方式驱动后面的发奖流程。
抽奖系统的拓扑结构
api层:负责处理用户的抽奖请求,与后端逻辑服务器进行交互,并返回中奖结果。
逻辑层:用规则引擎过滤人群,实现抽奖算法。
数据存储层:数据库存储用户信息、抽奖活动信息、中奖记录等。
消息队列:处理奖品发送。
模板、工厂、策略模式使用在哪些地方?
模板模式:核心概念是在抽象类中定义一个算法的实现框架,具体的实现步骤延迟到子类去实现,重点是固定一套算法流程,不改变实现方式。定义了参与活动、抽奖、发奖的整体流程。首先定义一个抽象类,这个抽象类中包含了一个模板方法,可以用final关键字修饰,他是一个具体的方法,定义了整个过程的流程和步骤,除此之外,还会定义抽象步骤方法,具体的实现在子类中进行实现。
用户领取活动-----执行抽奖------结果落库------发送MQ触发发奖流程(异步实现)------返回结果
工厂模式:提供一个接口或抽象类用于创建对象,允许子类去决定实例化哪一个类。实现发奖,首先定义一个抽象的奖品类,然后每种奖品设计自己的实现类,创建一个配置类,把所有奖品类的实现封装到map中,定义一个工厂类,通过map.get()把发货奖品的实现交给子类去实现,减少if else的使用,并且,定义了新的奖品类之后也可以直接加入map,能够减少代码改动。
策略模式:实现多种算法,主要是指将每个算法都封装起来,并且使他们之间在使用时可以互换。定义一个抽奖策略的实现接口,每种抽奖算法实现该接口形成对应的抽奖类,根据运行中传递的参数选择对应的实现方式,可以将所有策略的实现方式封装在map中,这样可以更加灵活的扩展。它的特点就是将策略的实现和对策略的选择分开。
单例模式
微服务是如何划分的?是只有一个抽奖的微服务吗?
答:微服务是指将一个大的任务分成小的模块,每个模块围绕特定的业务功能进行开发构建,并通过轻量级的方式互相通讯。按我的理解是活动发放、抽奖活动、奖品发放每一个都可以作为一个微服务。
Dubbo
一个开源的rpc框架,提供了高性能、透明的rpc远程调用服务,以及服务注册与发现。rpc是一个远程调用的技术方法的广义概念,而dubbo是具体的实现,支持多种序列化方法和传输协议。
能够增强对微服务应用的调用、负载均衡,在抽奖系统中主要用到的是不同微服务之间的调用。
它主要是应用于不同服务之间通信、但是因为我的抽奖流程都定义在同一个应用中,所以模块间的通信可以本地调用。
但是可以使用dubbo进行集成测试,模拟这些服务分布在不同容器中的情况。要看能否具备远程通信的能力。
项目中的接口如何幂等实现?
答:程序中接口的幂等性,是基于数据防重字段实现的,比如UUID唯一索引,在插入时会抛异常,一般会加入查询、数据库缓存的方式减少对数据库的写操作。
用到了哪些redis数据结构?会出现redis缓存击穿、穿透的情况吗?
答:使用了字符串,哈希,布隆过滤器。
规则引擎的设计目的,根据什么来筛选?
目标精准性:某些活动中,组织者可能希望抽奖针对特定的人群,所以需要进行过滤一。
多活动管理:如果多个抽奖活动同时进行,不同的活动可能有不同的目标和人群。
动态规则更改:可以方便的更改筛选条件规则而不是嵌套使用if else,无需更改整个抽奖系统。
实现方式:引擎规则是一个决策树判断,定义一个过滤的接口,抽象类封装一个比对接口,具体类返回决策key对应的值,比如性别“女”,年龄23这样子,交给抽象类去比对。
可以根据制定的规则把节点的filter逻辑封装到map中得到树的信息,然后遍历,拿出每个节点的key比如gender,得到具体值进行比对,得到下一个node,再从map中进行取值依次遍历。
性别、年龄、消费情况、身份、搜索、点击等。
规则都是既定的,为什么还要用决策树?决策树和布尔检索有区别吗?
答:规则是既定的,决策树可以帮助我们组合这些规则,是的在确定场景下,可以根据不同的输入条件执行不同的路径,布尔检索主要是一种检索技术,如果是这样的话。
表结构是怎么设计的:
活动配置表:活动ID,开始、结束时间,活动库存,活动状态,活动策略
抽奖策略配置表:策略ID,策略描述,抽奖算法,发奖方式,发奖时间,奖品编号,奖品库存
用户表:参与活动记录表,活动ID,参与时间,可参与的次数等。
用户抽奖计算结果表:活动ID,是否中奖,奖品ID,发奖状态,发奖时间等。
哪些地方使用到了MQ,为什么要异步使用MQ:
主要就是解耦,抽奖成功之后发送MQ消息来触发发奖流程。抽奖场景中,发奖流程可能包括日志记录、数据库更新、奖品发送等,直接串行执行的话可能会有延迟,影响用户体验。异步发送的话可以快速响应,并在后台处理发奖过程。
为什么要解耦,MQ解耦发奖之后的具体流程:
答:更加灵活,抽奖和发奖两者可以独立修改和升级。如果出现错误,任务被放在消息队列里面,可以稍后被重新处理。异步处理,后台处理,能够及时响应。
用户参与抽奖——发送消息到MQ:将中奖消息发送到消息队列
响应用户:即使发奖还没开始,用户也可以立即受到响应,如正在处理中。。。
处理消息——通知用户:后台服务从消息队列中抽取中奖信息并开始处理,验证,更新数据库等。
为什么使用卡夫卡,怎么配置卡夫卡,消息丢失怎么办,消费失败怎么办:
为什么:高吞吐量,适用于高性能生产和消费场景;持久性,可以保持大量数据;可以实时处理数据流。
首先要在pom文件里引入卡夫卡的依赖,yml中配置监听者消费者的属性,比如端口号、错误重试次数、序列化方式、以及监听的线程数等。启动zookeeper服务器和卡夫卡服务器。
怎么办:重试策略,设置重试次数和时间间隔;将不能发送的数据持久化到数据库中;当发送失败次数达到一定阈值时,启动报警机制。
怎么办:重试机制,如果多次无法消费,放入死信队列。报警机制。
xxl-job是干啥的?为什么选择xxl-job执行定时任务
XXL-JOB是一个分布式任务调度平台,定时扫描上线的任务进行调度,在指定时间内执行某些操作。使任务的监控和管理变得简单明了。
xxl-job在抽奖系统中主要可以扫描日志或数据库中是否有MQ消息发送失败,并且定时控制活动状态的变更。
具体怎么实现:首先应该要有日志记录,操作失败时会有日志记录下来。使用xxl-job设定一个定时任务,比如隔一段时间就扫描查找失败的任务,根据业务判断结果执行实际的补偿,可能是重新执行一个数据库操作、发送一个MQ消息等。补偿的结果也要记录。
为什么用户领取活动完毕,发送MQ更新库存中的库存:
解耦,灵活性,可以独立配置更新,异步处理。