如何在定时任务中处理新文件
我该怎么检查已经处理过的文件,以免重复处理呢?或者说,我现在的做法有什么问题吗?
你好,
我正在使用tshark,设置了环形缓冲区选项,这样每当文件达到5MB或运行1小时后就会自动保存到文件中。我写了一个Python脚本来读取这些XML文件,并把数据存入数据库,这个过程运行得很顺利。
我的问题是,这个过程非常消耗资源,因为一个5MB的文件转换成XML后可能会变成200MB,所以我不想进行不必要的处理。
这个脚本每10分钟运行一次,每次处理大约5个文件。它会扫描文件夹,查看有没有新文件,然后把文件的哈希值存入数据库。在下一次运行时,如果这个哈希值不在数据库里,我就会扫描这个文件。
问题是,这个方法并不是每次都有效,有时候它会处理那些已经处理过的文件。当我检查那些它一直想处理的文件的哈希值时,发现它在数据库里根本没有记录,所以它才会一次又一次地尝试处理。
我在脚本的输出中打印了文件名和哈希值:
using file /var/ss01/SS01_00086_20100107100828.cap with hash: 982d664b574b84d6a8a5093889454e59 using file /var/ss02/SS02_00053_20100106125828.cap with hash: 8caceb6af7328c4aed2ea349062b74e9 using file /var/ss02/SS02_00075_20100106184519.cap with hash: 1b664b2e900d56ca9750d27ed1ec28fc using file /var/ss02/SS02_00098_20100107104437.cap with hash: e0d7f5b004016febe707e9823f339fce using file /var/ss02/SS02_00095_20100105132356.cap with hash: 41a3938150ec8e2d48ae9498c79a8d0c using file /var/ss02/SS02_00097_20100107103332.cap with hash: 4e08b6926c87f5967484add22a76f220 using file /var/ss02/SS02_00090_20100105122531.cap with hash: 470b378ee5a2f4a14ca28330c2009f56 using file /var/ss03/SS03_00089_20100107104530.cap with hash: 468a01753a97a6a5dfa60418064574cc using file /var/ss03/SS03_00086_20100105122537.cap with hash: 1fb8641f10f733384de01e94926e0853 using file /var/ss03/SS03_00090_20100107105832.cap with hash: d6209e65348029c3d211d1715301b9f8 using file /var/ss03/SS03_00088_20100107103248.cap with hash: 56a26b4e84b853e1f2128c831628c65e using file /var/ss03/SS03_00072_20100105093543.cap with hash: dca18deb04b7c08e206a3b6f62262465 using file /var/ss03/SS03_00050_20100106140218.cap with hash: 36761e3f67017c626563601eaf68a133 using file /var/ss04/SS04_00010_20100105105912.cap with hash: 5188dc70616fa2971d57d4bfe029ec46 using file /var/ss04/SS04_00071_20100107094806.cap with hash: ab72eaddd9f368e01f9a57471ccead1a using file /var/ss04/SS04_00072_20100107100234.cap with hash: 79dea347b04a05753cb4ff3576883494 using file /var/ss04/SS04_00070_20100107093350.cap with hash: 535920197129176c4d7a9891c71e0243 using file /var/ss04/SS04_00067_20100107084826.cap with hash: 64a88ecc1253e67d49e3cb68febb2e25 using file /var/ss04/SS04_00042_20100106144048.cap with hash: bb9bfa773f3bf94fd3af2514395d8d9e using file /var/ss04/SS04_00007_20100105101951.cap with hash: d949e673f6138af2d388884f4a6b0f08
它应该只处理每个文件夹中的一个文件,所以总共只有4个文件。这导致了不必要的处理,我还得处理重叠的定时任务和其他服务受到的影响。
我希望通过这篇帖子能找到更好的解决办法,或者希望有人能告诉我为什么会这样,我知道后者可能很难,因为原因可能有很多。
这是我的代码(我不是程序员,只是系统管理员,所以请多包涵 :P),第30-32行处理哈希值的比较。
提前谢谢大家。
5 个回答
我看到几个问题。
如果你有重叠的定时任务(cron jobs),你需要一个锁机制来控制访问。也就是说,确保一次只能有一个进程在运行,这样就能避免重叠的问题。你可以设置一个脚本来实现这个功能。通过创建一个目录来建立“锁”(使用mkdir命令是原子操作,意味着它要么成功,要么失败),处理完数据后再删除这个锁目录。如果脚本在尝试创建目录时发现目录已经存在,那就说明另一个进程正在运行,这时脚本可以直接退出。
如果你不能修改定时任务的设置,那就把可执行文件重命名,然后把你的脚本命名为和旧的可执行文件一样。
哈希值并不能保证是文件的唯一标识符,虽然它们很可能是唯一的,但并不能绝对保证。
我对这些文件的内容了解不多,所以这可能不适合你,但如果你只有一个使用这些文件的地方,我建议你使用文件夹来管理这些文件,并根据它们的状态来移动文件。具体来说,你可以有这样的文件夹结构:
/waiting
/progress
/done
然后你可以利用 mv
命令的相对原子性来改变每个文件的“状态”。(我认为 mv
是否真正原子化取决于你的文件系统。)
当你的处理任务想要处理一个文件时,它会把文件从 waiting
文件夹移动到 progress
文件夹(并确保移动成功)。这样,其他任务就无法再处理这个文件,因为它不再在等待状态。当文件处理完成后,它会从 progress
移动到 done
文件夹,这时一个清理任务可能会删除或归档那些不再需要的旧文件。
处理那些随机时间创建的文件,一个不错的方法是使用 incron
,而不是 cron
。需要注意的是,由于 incron 使用了 Linux 内核的 inotify 系统调用,这个方法只适用于 Linux 系统。
简单来说,cron
是根据日期和时间来运行任务的,而 incron
是根据监控的文件夹中的变化来运行任务的。例如,你可以设置 incron,让它在每次新文件被创建或修改时都执行某个任务。
在 Ubuntu 系统上,这个软件包叫做 incron
。至于 RedHat 系统,我不太确定,但我相信这个链接里的包是对的:http://rpmfind.net//linux/RPM/dag/redhat/el5/i386/incron-0.5.9-1.el5.rf.i386.html。
安装好 incron 包后,查看
man 5 incrontab
了解如何设置 incrontab 配置文件。你的 incron_config
文件可能看起来像这样:
/var/ss01/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss02/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss03/ IN_CLOSE_WRITE /path/to/processing/script.py $#
/var/ss04/ IN_CLOSE_WRITE /path/to/processing/script.py $#
然后,要把这个配置注册到 incrond 守护进程中,你需要运行
incrontab /path/to/incron_config
就这么简单。现在,每当在 /var/ss01、/var/ss02、/var/ss03 或 /var/ss04 中创建一个文件时,命令
/path/to/processing/script.py $#
就会被执行,$# 会被新创建文件的名字替换。
这样就不需要存储或比较文件的哈希值了,文件只会在创建后立即处理一次。
只要确保你的处理脚本不要在监控的文件夹的顶层写入文件。如果这样做了,incrond 会注意到新文件的创建,然后再次启动 script.py,这样就会陷入无限循环。
incrond 只监控单个目录,而不会递归监控子目录。所以你可以让 tshark 写入 /var/ss01/tobeprocessed,使用 incron 监控 /var/ss01/tobeprocessed,然后让你的 script.py 写入 /var/ss01,例如。
另外,值得一提的是,还有一个 Python 接口可以使用 inotify,叫做 pyinotify。与 incron 不同,pyinotify 可以递归监控子目录。不过在你的情况下,我觉得这个递归监控的功能并不是特别有用或必要。