循环有序的发送通知实现方案

背景

      上周五,同事从21楼下到14楼问我一个问题,他们的产品想实现一个有序并且循环的去发送通知的一个需求。同事一时没想到比较好的解决方案,便找我来讨论一下,有没有更优的实现方式。于是我们便在14楼讨论了一番,得出一个相对比较好的解决方案,在此分享给大家,方便大家遇到类似的问题可以加以借鉴和参考。

需求描述

      假如数据库有三条待发送的通知模板A,B,C,按日期升序排列,假如排列顺序就是ABC,则第一轮发送顺序为ABC,第二轮发送顺序为ABC,第三轮顺序为ABC,依次循环往复A->B->C->A->B->C...如下图所示:

 

image.png

 

上面只是一种场景,突然有一天产品需要加入一条通知模板D,假如D的排列顺序在C之后,这里只是假设,也可能在ABC任何一个模板的前面,发送顺序则为下图所示:

image.png

 

对以上发送的要求是必须循环有序的发送,需要支持任意排列顺序后依然保持循环发送。

 

问题分析

1.初步分析

        乍一看,这个问题不是很简单嘛,放队列不就好了嘛,你长得好看,说什么都对,好像问题就这样轻松被解决了,哈哈。。。但是具体怎么实现呢?让我们再仔细分析分析。

  • 第一步,把模板排好序查出来(SQL根据时间升序,这个大家都会,但是按时间排序我感觉不方便,灵活性比较差,这个自己体会),放到缓存队列中(这里的队列完全可以用ArrayList代替,不一定非用queue,但是queue的好处还是很多滴,例如移除元素的时候就非常非常地方便了)
  • 第二步,现在缓存队列有了,我们就可以发起推送模板啦,第一轮发送开始,取出第一个A发送,然后取出B发送,再取出C发送,哇,这不就轻松实现了吗?多简单!

 

不知道大家发现问题没有?上面的需求很清楚,是循环有序发送。上面的方法貌似没有实现循环啊,只是实现了有序而已,那么问题又来了,怎么实现循环呢?请看深入分析。

 

2.深入分析

        上面分析的方法虽然可以保证顺序,但是无法循环发送,这该怎么办呢?于是我想到一个队列好像无法实现循环,那我就用两个,嘿嘿。。。好像两个队列就解决了问题,事实真的是这样吗?我们继续分析,假设我们创建了两个队列,一个队列是发送队列,另一个是接收队列。

  • 第一步,创建发送队列和接收队列,其目的是为了实现循环效果。
  • 第二步,我们初始化发送队列并放到缓存中,同时创建一个空的接收队列并放在缓存中。
  • 第三步,我们开始发送队列中的模板信息A,并把它添加到接收队列中,同时移除发送队列中的A,模板B和C同理A的发送步骤。
  • 第四步,当发送队列为空时,也就是发送队列中的数据都被发送完毕,此时,最初的发送队列变为了接收队列,而最初的接收队列变成了发送队列,如此交替变换身份,便实现了循环的效果。

 

好像这个方法很完美的实现了上面的需求,循环有序,堪称完美啊,哈哈。。。不知可否有人注意到两个队列轮流交互身份的条件是什么?(此处大家可以发散一下思维,哈哈。。。这里我先卖个关子,欢迎留言)

 

3.最终分析

         上面的分析基本上已经可以实现循环有序的发送通知了,但是却忽略了两点,那么是哪两点呢?咱们接着分析!

  1. 如果产品需要新加入一个模板通知D,那么该怎么办?(换句话说就是D模板怎么同步到缓存队列,并为其排好序)
  2. 缓存队列所依赖的服务器挂了或者重启了,那又该怎么办?(换句话说就是redis服务器挂了或者重启了)

 

对于第一个问题,其实还是可以实现的,就是稍微麻烦一点(具体的做法,我这里不在细细描述了,此处不是重点,感兴趣的同学可以自己思考一下,也可以私下找我探讨)。

但是第二个问题就比较棘手了,服务器挂了或者重启了,所有的数据都丢失了,这该怎么办?有的人估计会想到Redis可以持久化,即使挂了或者重启也没关系,其实这样可以吗?确实可以。

 

好像上面的方案确实可以解决可能发生的问题,并实现了需求功能,说实话,上面的方案是可行的,但是我却没有推荐同事用,原因是实现起来比较麻烦,那么有没有更简单的方案呢?答案是肯定的。

 

解决方案

        通过前面的层层分析,我们发现一个队列似乎不能满足需求,两个队列实现起来又比较麻烦,那么我们可以不做这个需求吗?哈哈。。。我也不想做,跟产品商量商量砍掉好了,嘿嘿。。。

         我当时思考了一会,觉得肯定有更简单的办法,只是没想到,于是大脑不停旋转,突然想到了学习数据结构队列时的实现方式,即循环队列的实现方式(看过数据结构的应该知道这个),循环队列的队头元素的指针指向下一个元素,下一个元素的指针继续指向下下一个,依次类推.......队尾元素的指针又指向队头元素,如下图所示:

image.png

 

由这个启发,我联想到当前的需求场景,我们是不是可以仿照循环队列的实现方式来实现我们的需求呢?答案很明显是可以的,而且实现起来超简单,哈哈。。。具体怎么实现呢?我们一起继续探索!

        首先我们知道了循环队列的特点,跟我们的需求很类似,那么我们就可以创造条件让我们的通知模板带有循环特性。

  1. 第一步,我们需要给数据库通知模板表添加一个是否已发送的标记字段,可以用int型的0,1表示,0代表已发送,1代表待发送;
  2. 第二步,初始化模板信息,排在第一位的为1,其余为0。
  3. 第三步,每次发送模板时只查询出为1标记的去发送,同时更新当前所发送模板标记为0,并且更新排在下一位的模板标记为1,依次类推。

 

就上面的简单3步,就轻松实现了循环有序发送的需求,并且支持任意排序和追加新的模板进来,即使服务器挂了重启,也不会影响之前发送的顺序,真正达到了灵活多变,这个问题相对比较好的解决了。

 

总结

        虽然这个问题看着挺简单的,其实细节点还是很多的,一不小心就会掉进坑里,如果考虑不到缓存会崩,亦或考虑不到新模板添加进来后重排序等问题,亦或是考虑到了没很好的处理到位,都可能会埋下隐患。还有就是实现方式,一开始我们拿到这个需求进行分析时,可能第一想到的只是一些常规手段,亦或是一些复杂的实现手段,但是仔细想想,可能会有更好的实现方式,当我们觉得实现起来比较麻烦的时候不妨换一种思路,或许会有柳暗花明又一村的感觉。其实上述的场景依然不够全面,还有很多场景未包含在其中,大家可以再拓展一下想象空间,发散一下思维,欢迎大家留言!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值