如何对传入的文件做出反应

如何对传入的文件做出反应

我需要处理添加到目录中的文件,然后删除它们。文件可能很大(20KiB 到 5 GiB)并且到达缓慢(通过scpftp)。如何监视路径(可能带有一个systemd.path单元)并仅在新文件完成写入时才做出反应?我发现我的服务总是在写入文件的第一个字节时触发,从而导致失败,因为完整的文件不存在。


尝试 1 ( DirectoryNotEmpty=)

我用的是*.pathDirectoryNotEmpty=.

优点:

  • 看起来像我想要的

缺点:

  • 触发太早了。当我处理文件时,文件仍在传输,因此它们不完整(已损坏)。
# /etc/systemd/system/incoming.path

[Path]
DirectoryNotEmpty=/var/incoming

# /etc/systemd/system/incoming.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming

# /usr/local/bin/incoming

for i in /var/incoming/*; do
  process $i
  rm $i
done

尝试 2(民意调查)

我使用 a*.timer每分钟触发一次脚本

优点:

  • 简单的

缺点:

  • 对于小文件,大约 90% 的时间有效;对于大文件,大约 0% 的时间有效。
  • 给管道增加最多 1 分钟的延迟
  • 日志日志因启动/停止传入设备而混乱
# /etc/systemd/system/incoming.timer

[Timer]
OnCalendar=minutely

# /etc/systemd/system/incoming.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming

# /usr/local/bin/incoming

for i in /var/incoming/*; do
  process $i
  rm $i
done

尝试 3(民意调查加 md5)

我继续轮询,但我也只处理仅*md5由客户端在发送主要内容后发送的小文件。这些都小于 1 MTU,所以我希望它们在写入过程中不会被中断。

优点:

  • 似乎一直有效(但不确定是否有效)

缺点:

  • 给管道增加最多 1 分钟的延迟
  • 日志日志因启动/停止传入设备而混乱
  • 对于用户来说更复杂。用户脚本需要生成md5sum然后发送该文件发送主文件。
# /etc/systemd/system/incoming.timer

[Timer]
OnCalendar=minutely

# /etc/systemd/system/incoming.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/incoming

# /usr/local/bin/incoming

for i in /var/incoming/*.md5; do
  file=$(basename $i .md5)
  process $file
  rm $file $file.md5
done

答案1

由于唯一的问题是“尝试1”如果文件仍然打开,您可以修改脚本以使用lsofsleep确保文件在处理之前关闭。

像这样的东西:

# /usr/local/bin/incoming

for i in /var/incoming/*; do
  while [ "$(lsof $i)" != "" ]; do
    sleep 1
  done
  process $i
  rm $i
done

再仔细想想,如果systemd在已经运行的脚本等待文件关闭时再次调用该脚本,则可能会出现竞争条件。

像这样的事情不太容易因竞争条件而导致错误(仍有改进的空间):

for i in /var/incoming/*; do
    while [ -e $i ]; do
        if [ "$(lsof $i)" != "" ]
        then
            sleep 1
        else
            process $i
            rm $i
        fi
    done
done

答案2

执行此操作的经典方法(例如,在邮件服务器中)是让写入过程写入传入目录,并在完成后将文件移动到处理目录。然后,处理服务可以对到达的新文件采取行动,而无需担心传入的文件仍在写入中。

这确实依赖于能够在上传过程中重命名到位,但可以让您使用DirectoryNotEmpty您一直尝试使用的功能。

相关内容