在业务处理中,我们有时候会碰到一些场景,他们需要执行多个步骤才能算完成,然而这几个步骤并非是连续立即执行的。
举一个例子,在处理订单业务的时候,我们接收到订单,需要将订单保存到数据库中,然后,要将其存入Redis缓存,完了还要将其转发给其他相关系统。转发其他系统的过程也是成对的操作,需要将转发的数据存入一份处理表同时向消息队列中也传一份,根据发送到对方系统中的反馈消息进行相应处理(处理成功则将处理表中对应数据删除,处理失败则对该数据进行重新发送,或者根据业务需求重新操作)。
这样的业务,会有几个显而易见的问题。
问题一、保存数据库的操作需要事务,但事务中很难包括对Redis和队列的操作。
问题二、向外系统发送数据的过程,可快可慢,在最快的场景中,消息发送出去,对方立即有相应的反馈回来,但本地数据,可能还没有保存到数据库中,此时,无法将该数据跟数据库中联系起来,结果将会出现数据错误。
一个解决方案就是,将数据库操作集中到一起放在事务中处理,将其对应的额外操作,放到事务外面,统一执行。
这样的解决方案,可以保证事务的完整性,当数据库操作失败的时候,数据库事务回滚,其余操作也可以终止。另外也不会发生消息接收系统反馈结果太快而导致的数据错误的情况。
但这不是完美的方案。
我们在执行业务的时候,“保存并缓存订单”和“保存并发送消息”,都是成对的操作,如果将他们割裂,业务和代码将会变得不直观。
是否可以有个更好的解决方案呢?
这个解决方案,我们称之为延迟执行模式。
我们在处理业务逻辑的时候,在事务中我们正常处理数据库操作,与此同时,在相应操作同时将对应的需要延迟执行的逻辑定义到一个延迟执行的类中。这个类我们称之为DelayedExecutor。并将这个延迟执行对象放到一个链表中,链表将在整个业务逻辑的上下游传递,在事务操作结束后,通过遍历链表来执行那些需要延迟执行的方法。
该模式的核心在于以下几个类和接口
DelayablelService 带有延迟任务的业务逻辑
public interface DelayablelService <T> 这是一个接口
其核心方法是 void doBusiness(T param);
处理业务逻辑,并在处理业务逻辑时,将需要延迟执行的逻辑定义好
我们所有要执行的代码就是从这里开始
param 是业务数据参数
DelayedExecutor 需要被延迟执行的逻辑接口
public interface DelayedExecutor
这是一个接口 其中定义了void execute();方法
该类在业务逻辑中,通过匿名内部类的方式实现。将业务方法中的数据传入该方法中。
DelayedExecutorHandler 延迟执行内容管理
public class DelayedExecutorHandler
一个管理类,管理DelayedExecutor
类中有一个内部list private List<DelayedExecutor> executors;
通过 public void add(DelayedExecutor executor)方法
在DelayablelService的doBusiness 执行以后,
调用public void handle()方法执行延迟执行的逻辑。
DelayedCallHandler 执行业务并调用被延迟的方法
public class DelayedCallHandler<T> 这是一个类
public void handle(DelayablelService businessService, T param)
通过这个方法调用 DelayablelService中的 doBusiness方法,
在 doBusiness执行完以后,通过DelayedExecutorHandler 获取到 延迟执行的对象列表遍历并执行其逻辑
网友评论