senline

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

批量处理是常见的一种业务场景。举个例子:
hr系统在发工资后,发送工资通知邮件
工作流结束后,发送单据审批按成短信或邮件给业务发起人。
这种处理有两种方式,一种是实时发送,另一种就是批量发送,具体来讲就是首先将任务推送的“待发送池”,由定时任务从在特定的时间,或者特定的频率,从待发送池中读取待处理的数据逐个进行处理。

这种处理模式的好处是实现逻辑的分离:发工资 和 发送通知的分离。并且一般发送通知不是紧要的任务,完全可以在系统空闲的时候处理,把宝贵的系统资源留给关键业务。
批量处理的流程一般是这样的:
1,由某个条件(比如任务调度平台启动)触发任务处理程序
2,任务处理程序 从 待处理池中读取 若干任务
3,逐个处理每个任务
4,将任务处理状态写到任务中,完成任务处理

这里有几个重要的概念

一,任务处理状态
要实现任务的批量处理 ,要先给任务定义状态对任务的状态进行标识分类,处理程序对不同状态的任务做不同的处理。具体定义几种状态,要根据实际的任务类型、业务场景。但是任务一般会有以下几类状态:
1、等待处理
这种状态的任务是可处理的,一般是任务的初始状态。批处理程序会读取任务池中的 等待处理的任务进行处理。体现在sql上,就是使用这个状态过滤可以处理的任务。
2、不处理
由于各种原因,有些任务可能不需要处理。比如有些历史数据可能不需要处理。
3、处理失败
任务由于程序错误,或者条件不满足,网络超时等,没有被成功处理。处理失败的任务,可以再次处理。处理失败的,通常还要记录失败原因。
4、成功处理
被按约定的逻辑处理完毕。比如成功发送了短信。处理成功的任务下次就不会再处理了,可以移出任务池了。

这几个状态有两列来表示:
1,处理状态:等待处理0,处理成功1,处理失败-1
2,是否处理:1,0。
3,失败原因

二、出错重试

任务会因为多种原因初始失败。失败了怎么办?还要不要重试?
再1再2不再三。一般一次处理不成功,还会重试3次。因此还要增加一个重试次数以便进行计数。
因此还要增加一列 retry_times:重试计数。初始值为0

三、并发处理

大型应用系统,一定要考虑并发处理。就是说有可能会有多个任务处理器处理同一条任务。
并发处理要避免几个问题
1,任务被重复处理
2,死锁。

并发的情况出现的一种场景就是,部署了多个任务处理程序,处理同同一个任务队列。这时候就有可能会争用同一个任务。即如果处理不当,就会出现多个任务处理器同时处理同一个任务。
如果避免这个,就需要“锁”。
在一个任务处理程序处理某个任务之前,先把它“锁”住,也可以理解为打个标志。我们可以把这个标志放在任务状态上:增加一个状态-处理中 (2)。
这样,任务的处理过程是这样的:

1,检查任务的状态,对任务加锁

   1.1如果符合处理条件(状态是0,、-1(失败次数<3) ,并且 是否处理为 1)则执行1.2;否则执行2

   1.2   锁住它(可使用数据库的行锁)(select status from task where task-id = ## for update no wait)

   1.3 再次检查是否符合条件,如果符合处理条件(状态是0,、-1(失败次数<3) ,并且 是否处理为 1)则执行(1.4);否则执行2

   1.4 加锁:update task set status=2 wehre task-id=##  //修改任务状态为处理中2

   1.5 跳到 步骤 2(处理任务)

      #加锁的实现方式很多,比如分布式锁,各种自己实现的加锁机制等。我们这里使用数据库自己的锁机制。
      #这里之所以先检查任务状态再加锁,就是尽可能避免无效加锁的情况,加锁毕竟是很耗资源的行为,影响并发。加锁后再次检查条件的原因是 1.1,1.2 这两个步骤并不是原子操作。 

2、处理下一个任务。因为本任务不需要处理

3,处理任务

4,修改任务状态

5,完成任务处理

四、处理成功的任务

处理成功的任务,下次就不要再处理了,因此可以从任务池中转移到“完成任务表“中。另外不需要处理的任务也可以转移到完成任务表中。
转移的好处是出于性能考虑。如果不转移,任务池的数据随着时间推移,规模就会越来越大,遍历任务的时候,性能就会越来越低。将这些已经完成的、不需要处理的任务清理出去,任务池中的的数据规模总是维持在一个相对稳定的规模,任务处理程序就会维持在一个稳定的性能水平上。
任务池一般分3类:待处理任务池,已完结任务池,出错的任务池。
1、待处理任务池:存放等待处理的任务。一般新生成的任务会加到这个池中。
2、已完结任务池:存放已经处理成功的,或者不需要处理的任务。
3、出错的任务池:处理失败的任务会转移到这个池中。对处理出错但是需要重试的任务,依然放在待处理任务池中。只有经过指定得重试次数后仍然出错的,才转移到这个任务池中。

五、我们汇总下处理模式

我们做个总结。

不考虑消息队列,用传统的实现模式。
1,在任务池中查询符合条件的任务
使用数据库查询 select task-id from task-pool where
2,对每个任务 t
执行 三中的处理过程。每个任务作为独立的事务处理。事务轻量化。
(1),对任务加锁
(2),处理任务
(3),修改任务状态
(4),完成任务处理
(5),根据处理结果,将任务转移到对应的任务池中。

posted on 2022-06-28 11:06  森蓝2010  阅读(209)  评论(0编辑  收藏  举报