七叶笔记 » 数据库 » Redis 抽奖大转盘的实战示例

Redis 抽奖大转盘的实战示例

1. 项目介绍

这是一个基于Spring boot + Mybatis Plus + Redis 的简单案例。

主要是将活动内容、奖品信息、记录信息等缓存到Redis中,然后所有的抽奖过程全部从Redis中做数据的操作。

大致内容很简单,具体操作下面慢慢分析。

2. 项目演示

话不多说,首先上图看看项目效果,如果觉得还行的话咱们就来看看他具体是怎么实现的。

3. 表结构

该项目包含以下四张表,分别是活动表、奖项表、奖品表以及中奖记录表。具体的SQL会在文末给出。

4. 项目搭建

咱们首先先搭建一个标准的Spring boot 项目,直接IDEA创建,然后选择一些相关的依赖即可。

4.1 依赖

该项目主要用到了:Redis,thymeleaf,mybatis-plus等依赖。

4.2 YML配置

依赖引入之后,我们需要进行相应的配置:数据库连接信息、Redis、mybatis-plus、线程池等。

4.3 代码生成

这边我们可以直接使用mybatis-plus的代码生成器帮助我们生成一些基础的业务代码,避免这些重复的体力活。

这边贴出相关代码,直接修改数据库连接信息、相关包名模块名即可。

4.4 Redis 配置

我们如果在代码中使用 RedisTemplate 的话,需要添加相关配置,将其注入到Spring容器中。

4.5 常量管理

由于代码中会用到一些共有的常量,我们应该将其抽离出来。

对Redis中的key进行统一的管理。

4.6 业务代码

4.6.1 抽奖接口

我们首先编写抽奖接口,根据前台传的参数查询到具体的活动,然后进行相应的操作。(当然,前端直接是写死的/lottery/1)

为了避免用户重复点击抽奖,所以我们通过Redis来避免这种问题,用户每次抽奖的时候,通过setNx给用户排队并设置过期时间;如果用户点击多次抽奖,Redis设置值的时候发现该用户上次抽奖还未结束则抛出异常。

最后用户抽奖成功的话,记得清除该标记,从而用户能够继续抽奖。

4.6.2 初始化数据

从抽奖入口进来,校验成功以后则开始业务操作。

首先我们通过CountDownLatch来保证商品初始化的顺序,关于CountDownLatch可以查看 JUC工具 该文章。

然后我们需要检验一下活动的有效性,确保活动未结束。

检验活动通过后则通过ApplicationEvent 事件实现奖品数据的加载,将其存入Redis中。或者通过ApplicationRunner在程序启动时获取相关数据。我们这使用的是事件机制。ApplicationRunner 的相关代码在下文我也顺便贴出。

事件机制

有了事件机制,我们还需要一个监听事件,用来初始化相关数据信息。具体业务逻辑大家可以参考下代码,有相关的注释信息,主要就是将数据库中的数据添加进redis中,需要注意的是,我们为了保证原子性,是通过HASH来存储数据的,这样之后库存扣减的时候就可以通过opsForHash来保证其原子性。

当初始化奖品信息之后,则通过countDown()方法表名执行完成,业务代码中线程阻塞的地方可以继续执行了。

上面部分是通过事件的方法来初始化数据,下面我们说下ApplicationRunner的方式:

这种方式很简单,在项目启动的时候将数据加载进去即可。

我们只需要实现ApplicationRunner接口即可,然后在run方法中从数据库读取数据加载到Redis中。

4.6.3 抽奖

我们在使用事件进行数据初始化的时候,可以同时进行抽奖操作,但是注意的是这个时候需要使用countDownLatch.await();来阻塞当前线程,等待数据初始化完成。

在抽奖的过程中,我们首先尝试从Redis中获取相关数据,如果Redis中没有则从数据库中加载数据,如果数据库中也没查询到相关数据,则表明相关的数据没有配置完成。

获取数据之后,我们就该开始抽奖了。抽奖的核心在于随机性以及概率性,咱们总不能随便抽抽都能抽到一等奖吧?所以我们需要在表中设置每个奖项的概率性。如下所示:

在我们抽奖的时候需要根据概率划分处相关区间。我们可以通过Debug的方式来查看一下具体怎么划分的:

奖项的概率越大,区间越大;大家看到的顺序是不同的,由于我们在上面通过Collections.shuffle(lotteryItems);将集合打乱了,所以这里看到的不是顺序展示的。

在生成对应区间后,我们通过生成随机数,看随机数落在那个区间中,然后将对应的奖项返回。这就实现了我们的抽奖过程。

4.6.4 调整库存及记录

在调整库存的时候,我们需要考虑到每个奖品类型的不同,根据不同类型的奖品采取不同的措施。比如如果是一些价值高昂的奖品,我们需要通过分布式锁来确保安全性;或者比如有些商品我们需要发送相应的短信;所以我们需要采取一种具有扩展性的实现机制。

具体的实现机制可以看下方的类图,我首先定义一个奖品方法的接口(RewardProcessor),然后定义一个抽象类(AbstractRewardProcessor),抽象类中定义了模板方法,然后我们就可以根据不同的类型创建不同的处理器即可,这大大加强了我们的扩展性。

比如我们这边就创建了库存充足处理器及库存不足处理器。

接口:

抽象类:

我们可以从抽象类中的doReward方法处开始查看,比如我们这边先查看库存充足处理器中的代码:

库存处理器执行的时候首相将Redis中对应的奖项库存减1,这时候是不需要加锁的,因为这个操作是原子性的。

当扣减后,我们根据返回的值判断商品库存是否充足,这个时候库存不足则提示未中奖或者返回一个默认商品。

最后我们还需要记得更新下数据库中的相关数据。

方法执行完成之后,我们需要执行afterProcessor方法:

这个地方我们是通过异步任务异步存入抽奖记录信息。

在这边我们可以发现是通过Async注解,指定一个线程池,开启一个异步执行的方法。

创建一个线程池:相关的配置信息是我们定义在YML文件中的数据。

4.7 总结

以上便是整个项目的搭建,关于前端界面无非就是向后端发起请求,根据返回的奖品信息,将指针落在对应的转盘位置处,具体代码可以前往项目地址查看。希望大家可以动个小手点点赞,嘻嘻。

5. 项目地址

如果直接使用项目的话,记得修改数据库中活动的结束时间。

Redis

具体的实战项目在lottery工程中。

到此这篇关于Redis 抽奖大转盘的实战示例的文章就介绍到这了,更多相关Redis 抽奖大转盘内容请搜索七叶笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持七叶笔记!

相关文章