多条件拦截链如何实现?
Posted zby9527
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多条件拦截链如何实现?相关的知识,希望对你有一定的参考价值。
问题提出
-
用户参与抽奖,每日只能参与3次,总共只能参与15次,如何控制?(限额)
-
给用户发送短信,每天只能发送3条,每条间隔10分钟,如何控制?(防骚扰)
- 两个问题其实类似 ,都是多个限制条件
以问题2制定方案
- 使用redis做计数器,使用StringRedisTemplate作为API
- 使用两个校验器分别校验两个约束
- 后续添加规则只需要新增校验器
- 以【dayLimit:用户ID】为key,自增value,如果自增后的值大于3,返回false,否则返回true;过期时间设置为次日0点
- 以【periodLimit:用户ID】为key,当前时间为value,过期时间设置为10分钟,使用setIfAbsent进行设置。如果设置成功,说明10分钟内没有发送过。如果设置失败,说明10分钟内发送过,获取key的剩余过期时间为短信需要延时时间。
存在问题
- 第一步如果成功,第二步失败,那么白白消耗了一次每日的限制,需要回滚
- 如果在第二步失败回滚,那么需要在校验器2里面回滚校验器1,两个步骤产生了强行关联!
- 校验器1必须在校验器2前面
- 添加了校验器3,那么就要回滚1和2
如何解决?
-
合并成一个校验器:优点:类之间不存在依赖性;缺点:不符合开闭原则,比如添加一个规则7天只能发10条,需要继续往这个校验器里面加代码
-
使用redis的lua脚本,优点:降低了类的依赖性;缺点:逻辑在脚本里面
-
emmmm,好像没有很好的解决方案,这种依赖和回滚必然存在
最后思考
- 不依赖事务就只能自己回滚了,那就尽量设计下类结构吧
- 还是按照一个规则一个校验器来写
- 接口声明校验方法和回滚方法,由具体校验器来实现
- 接口还要一个getPrevious方法,返回上一个校验器
- 所有的校验器使用责任链的方式串起来,每个校验器提供一个回滚方法,该方法在下一个校验器不通过时调用,同时,每个校验器还要通过getPrevious调用上一个校验器的回滚方法,达到递归回滚的目的
- 使用抽象校验器实现getPrevious和调用上一个校验器
- 这样设计后,每个校验器只需要专注实现自己的校验逻辑以及回滚逻辑
- 校验器的顺序起来不用必须固定了!
- emmmm,看起来也符合了开闭原则!
最后的问题
- 事务,某个校验器执行中宕机,无法回滚。记好日志吧,低概率高成本低损失不值得修复。
收获
- 最后思考里的设计本来没想到的,打字打到这里突然灵光乍现!解决了多重回滚问题。
- 写代码的时候发现我的校验器是使用Sping注入到List里面的,调用是通过遍历调用的,那么其实顺序就有了,没有责任链方式的previous关系了
- 即如果校验器是使用List保存的,那么getPrevious也可以不要了,遍历list的时候使用下标方式,回滚的时候回滚遍历过的校验器就行了
思考<笔记<动手
笔记<动手以上是关于多条件拦截链如何实现?的主要内容,如果未能解决你的问题,请参考以下文章