【问题标题】:How to deal with NFS latency in shell scripts如何处理 shell 脚本中的 NFS 延迟
【发布时间】:2010-11-07 20:40:32
【问题描述】:

我正在编写一些经常写一些东西的 shell 脚本 到一个文件,然后执行读取该文件的应用程序。我发现通过我们公司,网络延迟差异很大,因此例如简单的sleep 2 将不够健壮。

我尝试编写一个(可配置的)超时循环,如下所示:

waitLoop()
{
   local timeout=$1
   local test="$2"

   if ! $test
   then
      local counter=0
      while ! $test && [ $counter -lt $timeout ]
      do
         sleep 1
         ((counter++))
      done

      if ! $test
      then
         exit 1
      fi
   fi
}

这适用于test="[ -e $somefilename ]"。但是,仅测试存在是不够的,我有时需要测试某个字符串是否写入文件。我试过了 test="grep -sq \"^sometext$\" $somefilename",但这不起作用。谁能告诉我为什么?

还有其他更简洁的选项来执行这样的测试吗?

【问题讨论】:

  • 是否可以简单地在文件上运行tail,只检查tail的输出(即最后一行)?这假设 tail 会比你的脚本更聪明地检查文件何时更改(而且很有可能,tail 会以更正确的方式进行)
  • 我认为正确的方法将取决于“一些东西”是什么以及“应用程序”是什么。您能否就您要解决的实际问题提供更多详细信息?所写的问题有点含糊;您可能没有想到完全不同的方法。

标签: bash ksh latency


【解决方案1】:

你可以这样设置你的测试变量:

test=$(grep -sq "^sometext$" $somefilename)

grep 不起作用的原因是引号很难传递参数。你需要使用eval:

if ! eval $test

【讨论】:

    【解决方案2】:

    我会说 在文本文件中检查字符串的方法是 grep。

    你的具体问题是什么?

    您也可以调整您的 NFS 挂载参数,以摆脱根本问题。同步也可能有所帮助。请参阅 NFS 文档。

    【讨论】:

    • 我无法更改挂载参数。我的脚本应该足够健壮以处理延迟。问题不在于 grep 本身,而在于 grep 测试没有像我预期的那样得到评估。
    • 好吧,如果你不能改变你的环境,那就是个问题。无论如何,在更改文件后尝试“同步”?根据 NFS 文档,它应该这样做(只要它调用 sysnc.2)。
    【解决方案3】:

    如果您想在“if”中使用 waitLoop,您可能需要将“exit”更改为“return”,以便脚本的其余部分可以处理错误情况(甚至没有消息用户了解在脚本终止之前失败的原因)。

    另一个问题是使用 "$test" 来保存命令意味着您在实际执行时不会得到 shell 扩展,只是评估。因此,如果您说 test="grep \"foo\" \"bar baz\"",而不是在文件中查找具有七个字符名称 bar baz 的三个字母字符串 foo,它会查找五个字符字符串九个字符文件“bar baz”中的“foo”。

    因此,您可以决定不需要 shell 魔法,并设置 test='grep -sq ^sometext$ somefilename',或者您可以让 shell 明确地处理引用,例如:

    if /bin/sh -c "$test"
    then
       ...
    

    【讨论】:

      【解决方案4】:

      尝试使用文件修改时间来检测何时写入而不打开它。类似的东西

      old_mtime=`stat --format="%Z" file`
      # Write to file.
      new_mtime=$old_mtime
      while [[ "$old_mtime" -eq "$new_mtime" ]]; do 
        sleep 2;
        new_mtime=`stat --format="%Z" file`
      done
      

      但是,如果多个进程尝试同时访问该文件,这将不起作用。

      【讨论】:

        【解决方案5】:

        我刚刚遇到了完全相同的问题。我对包含在 OP 中的超时等待使用了类似的方法;但是,我还包括了文件大小检查。如果自上次检查文件后文件大小增加,我会重置超时计时器。我正在编写的文件可能是几个 gig,因此它们需要一段时间才能跨 NFS 写入。

        对于您的特定情况,这可能有点过头了,但我也让我的编写过程在完成写入后计算文件的哈希值。我使用了 md5,但类似 ​​crc32 的东西也可以。这个散列从写入器广播到(多个)读取器,读取器等待直到 a)文件大小停止增加 b)文件的(新计算的)散列与写入器发送的散列匹配。

        【讨论】:

        • 谢谢。你如何广播 md5 哈希?并且该哈希的等待循环本身就足够了吗?
        • 我们正在使用现成的网络消息传递协议(Perl 的 POE;但 JMS 或 AMQP,对于这个功能,是相同的)。在我们的例子中,写入者和读取者在不同的机器上,写入者将散列广播作为指示写入完成的消息的一部分。读者拿起消息,等待他们查看文件的大小停止增长,然后检查哈希。
        • 而且,是的,对散列的等待循环也可以工作——但计算散列与检查文件大小相比相对昂贵,所以我作为“看门人”做简单/便宜的事情" 到沉重/昂贵的东西。
        【解决方案6】:

        我们也有类似的问题,但原因不同。我们正在读取 s 文件,该文件被发送到 SFTP 服务器。运行脚本的机器不是 SFTP 服务器。

        我所做的是在 cron 中设置它(尽管带有睡眠的循环也可以)来对文件进行 cksum。当旧的 cksum 与当前的 cksum 匹配时(文件在确定的时间内没有更改)我们知道写入已完成,并传输文件。

        为了更加安全,我们在进行备份之前从不覆盖本地文件,并且仅在远程文件连续有两个匹配的 cksum 且该 cksum 与本地文件不匹配时才进行传输。

        如果你需要代码示例,我相信我可以挖掘出来。

        【讨论】:

          【解决方案7】:

          shell 将您的谓词拆分为单词。使用$@ 获取所有信息,如下代码所示:

          #! /bin/bash
          
          waitFor()
          {
            local tries=$1
            shift
            local predicate="$@"
          
            while [ $tries -ge 1 ]; do
              (( tries-- ))
          
              if $predicate >/dev/null 2>&1; then
                return
              else
                [ $tries -gt 0 ] && sleep 1
              fi
            done
          
            exit 1
          }
          
          pred='[ -e /etc/passwd ]'
          waitFor 5 $pred
          echo "$pred satisfied"
          
          rm -f /tmp/baz
          (sleep 2; echo blahblah >>/tmp/baz) &
          (sleep 4; echo hasfoo   >>/tmp/baz) &
          
          pred='grep ^hasfoo /tmp/baz'
          waitFor 5 $pred
          echo "$pred satisfied"
          

          输出:

          $ ./waitngo [ -e /etc/passwd ] 满意 grep ^hasfoo /tmp/baz 满意

          可惜打字稿不如实时观看有趣。

          【讨论】:

            【解决方案8】:

            好吧……这有点古怪……

            如果您可以控制该文件:您也许可以在此处创建一个“命名管道”。 因此(取决于编写程序的工作方式)您可以以同步方式监控文件。

            最简单的:

            创建命名管道:

            mkfifo file.txt
            

            设置同步接收器:

            while :
            do
                process.sh < file.txt
            end
            

            创建一个测试发件人:

            echo "Hello There" > file.txt
            

            “process.sh”是您的逻辑所在:这将阻塞,直到发送者写入其输出。理论上写程序不需要修改....

            警告:如果接收器由于某种原因没有运行,您最终可能会阻止发送器!

            不确定它是否符合您的要求,但可能值得研究。

            或者为了避免同步,试试 'lsof' ?

            http://en.wikipedia.org/wiki/Lsof

            假设您只想在没有其他内容写入文件时(即写入过程已完成)从文件中读取 - 您可以检查是否没有其他内容具有文件句柄?

            【讨论】:

            • 好主意,但需要注意的是,如果发送方和接收方位于两个不同的主机上,即使它们具有相同的 NFS 挂载点,命名管道也不起作用。
            • 哇!应该正确阅读标题 - 是的,无法跨 NFS 安装!
            猜你喜欢
            • 2018-06-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-01
            • 1970-01-01
            • 1970-01-01
            • 2016-07-19
            相关资源
            最近更新 更多