目前在做的系统中,有一些功能模块需要从三方服务或者公司内部别的系统数据库中定时的拉取数据,拉取数据后再入库到自己的系统。数据接入是定时的, 但是有这样的情况出现:三方的数据提供方或者数据源有时候数据提供的不及时, 数据提供延迟几小时或者几天都有可能, 这时候用定时任务去拉取数据的方式就不能满足实际的需求了。
比如:我的系统有一个定时任务,每天凌晨2点去拉取昨天的数据处理并入库。 理想情况下这样的数据接入方式是可行的。 但是当数据源提供方提供数据延迟了几天, 那么这几天系统的定时就不能够拉取到数据了, 因为定时任务拉取数据的时候并没有数据源。 那么就需要一种数据拉取补录机制。
目前我在我的系统中,为了应对数据提供方提供数据延迟的情况, 我做了一个简单的补录,其原理也是一个补录定时任务,只是执行的时间比入库数据的定时任务推迟几个小时。比如定时入库的定时任务在每天凌晨两点执行, 那么负责数据补录的定时任务就在次日2点执行。 但是这种处理显然不是一种很好的方式。 因为数据源提供方延迟提供数据的时间是不确定的, 可能延迟几个小时, 可能延迟几天,甚至一周,都是有可能的,而补录的定时任务要想完全补录数据,不丢失数据的话,只能将补录时间尽可能的往后推,但是时间推的过于之后,数据的时效性就没有了。
为了既满足时效性,有能够完成数据补录,我设计了下图的机制:
设计思路如下:
对每一个定时任务入库执行后, 加入一个入库成功与否的判断。怎么判断入库成功与否需要和具体业务结合,如果判断到当次执行的定时任务入库失败了,那么就将这次执行定时任务的类,方法,参数记录到 一个叫 “数据补录器” 的机制中去,数据补录器也是一个定时任务, 他负责定时的执行补录器中的配置项, 换句话说, 数据补录器重存放的都是某次入库失败的任务,需要重新执行的。 数据补录器就负责定时去执行这些任务。 数据补录器执行完某个要重试的任务后,继续进行 数据入库检查 , 检查到入库成功, 则从数据补录器中删除掉该任务, 如果依然数据入库失败,则等待下次数据补录器执行补录。 直到 数据入库检查器检查到入库成功, 将该任务从数据补录器删除。
引入这个机制可以很好的解决数据源提供延迟的问题,能够对丢失的数据进行补录,只要数据补录器执行频次够高,就能解决延迟数据补录时效性的问题。 但引入这个机制同样会带来一些问题。
比如说,如果数据源提供方不提供某一天的数据,(这一天的数据就是没有)那么数据补录器会一直对这一天的数据进行补录。 这样肯定是不行的,所以还需要有数据补录任务的淘汰机制,如果某个任务一直执行不能入库数据,那么就将它淘汰。后续不在执行它。 我们可以提供两种简单的任务淘汰方式。
第一种,计数器淘汰方式, 对于补录的执行任务,对它的执行次数进行计数。如果该补录执行任务执行了N次还没有将数据补录进去,(我们可以认为要补录的这一天数据源提供方不会再提供数据),我们就将这个补录的执行任务删除。
第二中, 时间戳淘汰方式。 每次执行补录执行任务后,更新一下最新执行时间戳。记录下这次的执行时间。 如果一个任务要补录3号的数据,但是再次执行后,它的时间戳已经更新到了9号,也就是说连续6天依然没有将数据补录进去,我们就将这个补录执行任务淘汰,以后不再进行补录。
这个机制带来的第二个问题就是持久化的问题,我之前打算 数据补录器的实现,是用服务级别的系统缓存。也就是说要将需要补录执行的任务都存放在缓存中。 但是存在缓存中的任务需要持久化, 不然每一次更新服务,杀掉之前服务的进程,缓存中要执行的补录任务会全部丢失。 如果不嫌麻烦,可以采用数据库持久化方式,但是为了简单,可以将 存放执行补录任务的对象持久化到配置文件中。 每次向数据补录器中添加补录任务后,就做一次持久化。 同时,服务一启动,就将配置文件中的内容(要执行补录的任务-类-方法-参数等信息)加载到数据补录器的缓存中。
这现在只是我的一个想法,已经有了思路,可以应用到系统中。但是我一直没有做。一来是没时间,而来没必要。 这个东西在我脑子里很久了,记下来主要是怕自己忘掉。