欢迎关注公众号(通过文章导读关注),发送【资料】可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景
、中间件系列笔记
和编程高频电子书
!
【11来了】文章导读地址:点击查看文章导读!
首先,还是先了解业务逻辑的背景,对于系统中不活跃的用户,需要通过给这些用户发送优惠券来激活这些用户的消费,那么在给这些用户发送优惠券的时候,可能并不想立即就发送,而是定时发送,那么这里的解决方案就是通过先将优惠券信息 落库
,再通过 xxl-job
去扫描这个表执行优惠券推送给用户的任务
那么整个发送优惠券的流程如下:
这里先主要说一下如何对定时任务进行落库的,首先创建了一个优惠券,将这个优惠券进行推送,那么既然定时推送,就需要在优惠券的信息中定义 3 个数据:定时推送开始时间
、定时推送结束时间
和 推送轮次
,推送轮次表示在开始推送到推送结束这之间需要推送多少次
定时任务的落库就是通过这 3 个数据计算出来每一次推送的时间点,每一次推送都作为一个定时任务落库,如果 推送轮次 = 1
,那么直接将 定时推送的开始时间
作为任务执行时间即可,如果 推送轮次 > 1
,那么需要通过 (定时推送结束时间 - 定时推送开始时间) / 推送轮次
来拿到每次任务的执行时间,将每次需要执行的定时任务都落库存储即可
至此,给不活跃用户发送优惠券的主流程就已经结束了,接下来就进入到 xxl-job 去扫描定时任务并执行的阶段
,xxl-job 执行流程如下:
首先去数据库中扫描 执行时间 <= 当前时间
并且 没有执行过
的任务
将该定时执行任务发送到 MQ 中,等待消费者消费
这里推送定时任务到 MQ 中,还是需要进行 分片 + 多线程进行推送
来提升推送速度(因为需要推送的用户数量太庞大),分片 + 多线程优化推送的流程和上边 创建促销活动
时是一样的,这里就不重复说了
发送到 MQ 之后,将该任务状态修改为 已执行
,并修改数据库
总结来说,定时发送优惠券就是先将定时任务落库,再通过 xxl-job 去扫描定时任务进行推送即可
这里讲的大量定时任务是什么呢?
首先,还是说一下业务背景,对于热门商品的推送在电商场景中是很常见的,那么每一个热门商品所需要推送给的用户可能都是不同的,因此会通过另外一个推荐系统,计算出大量的热门商品,之后再对这些热门商品进行用户的推送,而热门商品推送给用户一般也是定时进行推送的,这里使用了 xxl-job
来进行实现定时的推送,但是这里需要推送的商品数量也是很多的,单靠一个机器进行定时推送速度很慢,那么这里就通过任务分片来加快推送的速度
先获取 当前任务的分片索引
,再获取 总共任务分片的数量
,之后可以通过需要推送商品的 id 来进行分片处理,下边写一个简单的伪代码:
@XxlJob("job")
public void job() {
// 获取 xxlJob 中当前任务的分片索引
int shardIndex = Optional.of(XxlJobHelper.getShardIndex()).orElse(0);
// 获取 xxlJob 中当前任务的总分片数量
int totalShardNum = Optional.of(XxlJobHelper.getShardTotal()).orElse(0);
// 循环任务,推送到 MQ 中
for (Good good : goods) {
Long goodId = good.getId();
// 计算出商品 id 应该被哪一个任务分片处理
int curNo = goodId.hashCode() % totalShardNum;
// 如果当前任务分片索引和商品需要被处理的分片索引不同,就不处理,直接跳过
if (curNo != shardIndex) {
continue;
}
}
}
需要推送的商品和任务执行的分片对应关系如下:
在面试的时候,讲项目要讲整个业务闭环讲清楚,以及引入中间件的需要和背景
项目中引入 RocketMQ 的优点在于:
首先对于 削峰填谷
,在 RocketMQ 中通过减少消费者的线程数或者限制消费者的消费能力来进行削峰,在系统低负载期间,通过增加消费者的线程数量来进行填谷,可以保证系统在运行期间,负载基本上处于一个稳定的状态,不会突然因为极高的负载而出现意外情况
其次是异步优化,这是很关键的,比如在运营人员创建完促销活动之后,需要对用户进行活动的推送,那么这个推送是很消耗时间的,因此需要将推送的任务在创建促销活动中异步出去,将耗时任务从主流程中剥离出去慢慢执行,不影响主流程的执行时间
对于高扩展性,在用户创建完订单之后,如果取消订单,在不使用 MQ 的情况下,需要在取消订单的逻辑中去一个一个执行取消订单后需要执行的操作,如下:
这样会导致取消订单的动作和其他业务耦合度很高,如果使用 MQ 之后,只需要在这三个地方关注订单取消的事件,不需要将取消订单中做很多耦合的操作
如果后续需要对取消订单做出调整,只用在订阅【取消订单】事件的位置修改代码即可