抽奖系统(7)

1.MQ异步抽奖逻辑执行

这里消费异常消息,还需要回滚其他状态,活动,人员,奖品

二.MQ消费者接收消息实现

在service包底下建一个消费者mq包,建一个消费者类

注意看我上面消费队列的流程,一些细节点都在上面了

二.校验抽奖请求

看时序图,我们在drawPrizeService里面实现校验参数,这里奖品是否有效我们直接查看,活动奖品关联表就不用再去查奖品表了,因为后续我们需要看中奖人数以及设置奖品数量,所以直接查活动奖品关联表直接省事了

三.状态扭转思路

写状态扭转的方法

我们这里会发现活动的状态扭转必须要在其他模块之后,依赖于其他模块,如果后续我们添加业务模块,让活动又需要在添加的模块之后,这时候我们进行业务拓展就会出现问题,我们这里采用责任链+策略两种设计模式来,保证活动模块在其他模块之后,以及提高扩展性

新建一个activitystatus的包来管理这些不同策略实现类以及接口

策略模式实现:

  • 抽象策略(Strategy):定义所有具体策略必须实现的统一接口(或抽象类),规定了策略的核心方法(业务方法)。
  • 具体策略(Concrete Strategy):实现 / 继承抽象策略,提供具体的算法实现,是策略模式的具体执行逻辑载体。cucu
  • 以下方法定义与activityService包中
  • 以上的接口是有进行复用的所以需要进行解耦操作,有一些看似没有必要的检验其实有用,还有一点更新缓存我们采用从库表中读取数据后缓存,保持缓存与库表一致
  • 具体实现类中实现几个方法,后续还需要补充,具体的转换方法
  • 具体处理方法可以参考以下的代码,注意如果需要转换但是转换失败需要抛出异常
  • 由于该方法涉及到多个表,我们还需要保证事务的一致性@transactional,接下来我们来具体实现下每一个模块的具体策略,就是使用Operater下的实现类,就是实现sequence,needconvert,convert,如活动我们需要先校验参数,id和目标状态是否为空,数据库是否为空,目标状态和库里状态是否一致,以及奖品是否全部抽完了,然后prize和user也是一样的,参考以下代码
  • 接下来是扭转状态的最后一步就是更新缓存

四.保持中奖者名单

  • 这里会涉及到新的库表
  • 注意这里我们缓存的时候,要捕获异常,因为我们不希望缓存失败影响主流程,所以我们直接trycatch,还有注意缓存活动维度的中奖记录的时候,只有当活动所有奖品抽完之后才需要缓存活动维度的中奖记录

五.通知中奖者

  • 接下来来完成通知中奖者的部分
  • 对于短信服务我们之前已经集成进来了,现在我们引入邮箱服务,引入依赖,配置项,以及配置类,测试一下邮箱是否可以发送
  • 对于发送短信通知我们采用并发处理,通过线程池来并发处理,线程池我们引入配置以及配置类
  • 调用线程池来完成我们的短信和邮件通知,注意中奖名单可能是多个是一个列表
  • 参考以下代码

六.抽奖回滚实现

  • 有个问题就是他这里回滚操作为什么不直接在最外面再加一个Transaction?
  • 别忘了我们的redis里面也有数据要回滚,而Transaction不支持redis,所以这里我们需要手写回滚方法
  • 这里我们注意只有在状态扭转,以及保持中奖信息需要回滚,注意以下的顺序,回滚中奖者名单需要在回滚状态之后,因为我们的状态扭转是有transaction保证了事务的一致性,当状态扭转抛异常的时候,他会自动帮我们回滚,这时候我们直接return就行,因为连状态都扭转失败就会抛异常,后面的保证中奖者名单根本不会走,所以直接return,如果需要回滚则回滚,
  • 随后判断是否需要回滚中奖者名单,不需要return 需要回滚
  • 如果需要回滚就需要实现rollbackStatue,这里可以模仿扭转状态的写法,也采用策略模式的写法,定义在状态manager接口中,采用策略模式,使用之前已经写好的convert,注意这里rollback,我们前面判断了需要回滚就是奖品人员需要回滚,这里活动不管他是completed还是init我们都直接让他回滚成init,不会有问题,其实就是强制状态转换,回滚后别忘了缓存也需要更新

状态回滚后我们完成中奖名单的判断是否回滚,和回滚方法,判断是否回滚,直接查看中奖表如果数据个数大于0就是要回滚​​​​​​​,我们之前将保持中奖记录写在抽奖的service,所以删除也写在该服务下

注意我们写一个强制删除方法,他即可删除活动维度,也可以删除奖品维度,方便复用,删除以后也要删除缓存,对应的活动奖品对应的删掉,活动的直接删掉,我用我们只有抽完奖品才会缓存,而有奖品回滚了,肯定没有抽完所以必须要删掉

七.定义死信队列实现消息重发

我们简单测试一下功能关于正向流程,以及是否会回滚,接下来我们来实现消息重发这个功能

这里我们需要新定义一个死信队列我们可以通过原有的配置类上添加一份修改队列名字交换机以及路由键,但是有一个点是需要修改的就是需要把正常队列和死信队列绑定一下,这样子他才会把消费失败的消息转发到死信队列之中(消息堆积),所以需要修改普通队列的方法,要绑定一下死信队列

此时我们改一下代码正常队列的消费者,直接抛异常消费失败之后,普通队列就会把消息发给死信队列,此时我们就可以看到消息堆积的现象了,那么死信队列中的消息后续怎么办呢这时候就需要我们也为他定义一个消费者,死信队列的消费者

我们这里死信队列消费者走的逻辑就是延时,然后将消息重发给正常队列,由正常队列的消费者消费,延时主要为了,留出时间为我们解决为什么进入死信队列(网络,服务器,代码逻辑),这里就直接将消息发给正常队列,延时可自行补充

脚步任务中可以添加一个定时功能留出我们处理异常的时间

八.查询中奖记录

对于后端来说只剩下最后一个接口

这里注意我们设计一个既可以是活动维度也可以是奖品维度的接口

补齐前端的代码此项目就完成了

九.项目部署服务器

先修改一下我们之前使用的日志过滤器

创建开发和测试两种配置文件

在application.properties里面只留下这句话,另外两个要删掉,通过这句话来选择走dev还是test

test环境即为服务器上面的环境,首先修改数据库用户密码

redis端口需要改成服务器上面使用的端口,因为我们之前是本地连接服务器的隧道,现在是服务器使用自己服务器上面的redis端口,直接使用6379即可

修改一下我们pic文件上传目录

这时候就可以打包了,但是我们想要让他跳过test的里面的内容,可能会因为一些原因打包失败,所以在pom文件中加上下面这个可以跳过test打包

接下来在服务器上面建立库表,通过.sql文件,通过服务器登录数据库以后调用source方法运行

之间创建后存放该jar包的文件夹方便后续管理以后,我开放的是8080端口,如果发现被占用,就查一下谁在使用8080端口(sudo lsof -i:8080)然后杀掉(kill -9 PID)运行即可,也可以调用指令让他后台运行(# 直接将Java项目放入当前终端后台运行,终端可继续操作 nohup java -jar xxx.jar)

相关推荐
s***87272 小时前
TCP/IP协议栈深度解析技术文章大纲
hive·spring boot
JANG10242 小时前
【Qt】项目打包
开发语言·qt
熏鱼的小迷弟Liu2 小时前
【Redis】如何用Redis实现分布式Session?
数据库·redis·分布式
Dylan的码园2 小时前
深入浅出Java排序:从基础算法到实战优化(上)
java·数据结构·算法
J_liaty2 小时前
前后端跨域处理全指南:Java后端+Vue前端完整解决方案
java·前端·vue.js·spring boot·后端
jason.zeng@15022072 小时前
基于数据库 + JWT 的 Spring Boot Security 完整示例
数据库·spring boot·oracle
颜淡慕潇2 小时前
深度解读 Spring Boot 3.5.9— 工程视角的稳健演进与价值释放
java·spring boot·后端·spring
夜郎king2 小时前
一文掌握:Java项目目录结构文档自动化生成
java·自动化·java原生目录生成
CoderCodingNo2 小时前
【GESP】C++五级/六级练习题(前缀和/动态规划考点) luogu-P1719 最大加权矩形
开发语言·c++·动态规划