使用rsync的文件锁

时间:2013-11-22 08:59:57

标签: rsync


从rsync手册文档中我看到,通过使用选项rsync-path,可以指定要在远程计算机上运行哪个程序来启动rsync。特别是,程序可以是一个包装脚本,它在中间调用实际的rsync命令,但在rsync调用之前和/或之后执行一些操作。一个可能有趣的用途是获取/释放锁(例如,一个群),以便远程端的rsync的操作可以与远端的另一个进程协调,该进程争用对其的写访问权。文件。可能有多个rsync进程同时持有共享锁(我知道有可能出现饥饿但现在并不关心它)。我正在处理的'writer'进程只会改变一些硬链接,因此它不会阻止rsync进程的任何重要时间。

我已经研究过其他协调方法,例如,在客户端和服务器之间实现自定义远程锁定协议,但它们都涉及更多的开发工作和/或由于其他原因而不能令人满意,这就是我感兴趣的原因。包装/(f)锁定方法。

我的问题是:

1)这是解决rsync'读者'与另一个“作者”进程协调访问同一目录的问题的合理方法吗?

2)在使用inetd(或xinetd)守护进程运行rsync时,还可以在rsync周围放置一个包装器,方法是在/etc/inetd.conf中添加如下所示的行(根据rsyncd.conf文件)页):

rsync stream tcp nowait root / usr / bin / rsync rsyncd --daemon

但是将/ usr / bin / rsync替换为rsync-lookalike包装器的路径,在这种情况下,它将是一个C / C ++代码程序,它抓住一个锁,分离rsync,等待rsync完成,然后释放锁。

谢谢, 汤姆

3 个答案:

答案 0 :(得分:1)

包装器方法的一个潜在问题:远程进程似乎使用额外的参数调用,这些参数附加到您使用--rsync-path指定的任何命令行。因此,如果您需要传递参数,则需要以下样式。

#! /bin/sh

lock_target=$1
shift

if ! lockfile ${lock_target}.lock ; then exit 1 ; fi

trap "rm  -f ${lock_target}.lock" EXIT HUP TERM INT

/usr/bin/rsync "$@"

答案 1 :(得分:0)

根据对原始帖子的评论,使用包装器方法在服务器端实现(f)rsync锁定确实是可行的。

答案 2 :(得分:0)

感谢问题和评论。有了你的想法,我使用--rsync-path解决了它(对我来说),但远程主机上没有任何包装程序,只需将所有有效负载脚本放入--rsync-path,只需几个技巧。

此特定示例使用rsync从远程主机提取数据,同时在远程主机上保留一个群,例如

,远程主机定期转储数据,同时保留一个flock,因此不能交错转储。

注意事项

  • rsync会将其参数附加到您在“--rsync-path”中指定的任何命令的末尾,因此命令需要处理它,为此我依赖于bash shell功能拉动和远程主机。
  • 远程主机上的任何前后处理都不能写入STDOUT,因为这会损坏rsync协议,rsync将会保释。任何错误输出都应该转到STDERR,并且它会在拉出主机时出现rsync STDERR输出。这就是所有错误处理中“1>& 2”的原因。
  • 这可能依赖于由rsync生成的远程命令来运行bash因为我认为好的旧sh不支持数组。这适用于RHEL7盒子之间。最后提出的可能的工作。

考虑到这一点,这里是我简化的概念 rehash(我没有运行这个特定的脚本,我的完整解决方案有额外的层,分散了主要注意力的注意力。)

拉主机上的脚本:

#!/bin/bash

function rsync_wrap() {
  {
    flock --exclusive --timeout ${LOCK_TIMEOUT} 100 || {
      echo "Failed to lock: ${LOCK_TIMEOUT}" 1>&2
      return 1
    }

    # call real rsync with original arguments
    rsync "$@"

    exit_code=$?

    if [ ${exit_code} -eq 0 ]; then
      # Do clean up when success
      # rm -f "${LOCK_FILE}"
      # rm -rf /eg/purge/data
    else
      # Do clean up when failed
    fi

    # Note, return is important, do not let it fall out
    return ${exit_code}

  } 100<"${LOCK_FILE}"

  echo "Failed to open lock file: ${LOCK_FILE}" 1>&2
  return 1
}

# Define vars
LOCK_FILE=/var/somedir/name.lock; # or /dev/shm/name.lock
LOCK_TIMEOUT=600; #in seconds

# Build remote command, define vars and functions inside the command
remote_cmd="
  # this approach deals with crazy chars in variables and function code
  $( declare -p LOCK_FILE )
  $( declare -p LOCK_TIMEOUT )
  $( declare -f rsync_wrap )

  rsync_wrap "

local_cmd=(
  rsync
  -a
  --rsync-path="${remote_cmd}"
  # I want to handle network timeouts in SSH, not in rsync,
  # because rsync does not know that waiting for lock is expected
  -e "ssh -o BatchMode=yes -o ServerAliveCountMax=3 -o ServerAliveInterval=30 ${IDENTITY_FILE:+ -i '${IDENTITY_FILE}'}"
  /remote/source/path
  /local/destination/path/
)

# Do it
"${local_cmd[@]}"

如果远程端执行-rsync-path而不是bash,那么也许整个远程命令可以包含在以下内容中:

local_cmd="bash -c '${local_cmd//\'/\'\\\'\'}'"