Bash 脚本:reader 编写器锁

Bash scripting: reader writer lock

想象一下由几台 nix 机器组成的网络。专用节点存储文件并定期调度 Task A 修改这些文件。每个其他节点都安排 Task B 将这些文件同步 (rsync) 到本地存储。

Task A 可能会花费大量时间,并且文件集合需要在所有节点上处于一致状态。因此 Task B 不应该 运行 而 Task A 是 运行ning.

一个可能的解决方案是使用 reader-writer 锁。 Task ATask B 将分别在资源上设置写入和读取锁定。

我想知道我们如何使用 unix shell 脚本实现这种锁定机制。

通常的做法是使用 flock utility, which is part of the util-linux package. FreeBSD and NetBSD packages are also available, aiui, and probably others. (For MacOSX, see this question。)

flock 命令可以同时执行读取 ("shared") 锁定和写入 ("exclusive") 锁定。它基于 flock(2) 系统调用,因此是 合作 锁定(也称为建议锁定),但在大多数应用程序中都可以正常工作(但请参阅下面的文件是远程的情况)。

上面链接的手册页中有使用示例。最简单的用例是

flock /tmp/lockfile /usr/local/bin/do_the_update
flock /tmp/lockfile -s /usr/local/bin/do_the_rsync

两者都获得了对/tmp/lockfile的锁定,然后执行指定的命令(大概是shell脚本)。第一条命令获取排他锁;我本可以使用 -x 选项明确说明这一点。第二条命令获取共享锁。


由于问题实际上涉及网络锁的需要,因此有必要指出 flock() 在网络文件系统上可能不可靠。通常,目标文件应该始终是本地文件。

即使是非分布式应用,也需要考虑失败的可能性。例如,假设您在本地进行 rsync 以创建一个副本。如果主机在 rsync 过程中崩溃,您将得到一个不完整或损坏的副本。 rsync 可以从中恢复,但不确定当主机重新启动时,rsync 是否会在文件被修改之前启动。这应该不是问题,但您绝对需要考虑到这一点。

在分布式应用中,情况比较复杂,因为整个系统很少出现故障。您可以有不同服务器或网络本身的独立故障。

建议锁定不是持久的。如果锁文件的主机在持有锁的情况下崩溃并重新启动,则重启后将不会持有锁。另一方面,如果持有锁的远程服务器之一崩溃并重新启动,它可能不知道它持有锁,在这种情况下锁将永远不会被释放。

如果两台服务器都 100% 了解彼此的状态,这将不是问题,但很难区分网络故障和主机故障。

您需要评估风险。与本地情况一样,如果文件服务器在 rsync 正在进行时崩溃,它可能会重新启动并立即开始修改文件。如果远程 rsync 在文件服务器关闭时没有失败,它们将继续尝试同步并且生成的副本将损坏。使用 rsync,这应该会在下一个同步周期自行解决,但在此期间您会​​遇到问题。您需要确定这有多严重。

您可以通过使用持久锁来防止文件服务器在启动时启动修改器。每个 rsync 服务器在启动 rsync 之前在主机上创建自己的锁定文件(并且在知道该文件存在之前不启动 rsync)并在释放读锁之前删除该文件。如果一个rsync server重启了并且它的indicator文件存在,它就知道在rysnc的时候发生了crash,所以它必须删除indicator文件并重启rsync。

这在大多数 时间都可以正常工作,但如果 rsync 服务器在 rsync 期间崩溃并且永远不会重新启动,或者仅在很长时间后才重新启动,它可能会失败。 (或者,等效地,如果网络故障长时间隔离 rsync 服务器。)在这些情况下,很可能需要手动干预。对于 "too long".

的某些定义,在文件服务器上有一个看门狗进程 运行 会很有用,如果读锁持有时间过长,它会提醒操作员