在Net::SSH会话中执行Ruby方法

在Net::SSH会话中执行Ruby方法,ruby,ssh,ruby-1.9.3,net-ssh,Ruby,Ssh,Ruby 1.9.3,Net Ssh,Ruby 1.9.3、NetSSH 2.9.2 我正在做一个项目,在这个项目中,我需要在本地和远程两个不同的服务器上区分相同的目录及其子目录。从那里,我需要将最新/最近修改的文件复制到正确的服务器,如果本地服务器中没有文件,则从远程删除 注意:我不能使用rsync。我们正在将星号相关目录备份到GlusterFS。在数千个文件中,rsync将本地卷与Gluster卷进行比较的速度非常慢,而我们需要它的时间不到1分钟 这是我目前的代码。我省略了复制/删除文件的工作,因为我想一次只做一步 requir

Ruby 1.9.3、NetSSH 2.9.2

我正在做一个项目,在这个项目中,我需要在本地和远程两个不同的服务器上区分相同的目录及其子目录。从那里,我需要将最新/最近修改的文件复制到正确的服务器,如果本地服务器中没有文件,则从远程删除

注意:我不能使用rsync。我们正在将星号相关目录备份到GlusterFS。在数千个文件中,rsync将本地卷与Gluster卷进行比较的速度非常慢,而我们需要它的时间不到1分钟

这是我目前的代码。我省略了复制/删除文件的工作,因为我想一次只做一步

require 'thread'
require 'date'
require 'rubygems'
require 'net/ssh'

SERVERS = ['local17', 'development']
CLIENT = SERVERS[0]
CLIENT_PATH = '/home/hstevens/temp_gfs'
BRICK_PATH = '/export/hunter_test'

@files = {
  SERVERS[0] => {},
  SERVERS[1] => {}
}

def grab_filenames_and_dates(files, server)
  files.reject { |x| File.directory? x }
  files.each do |file|
    name = `ls --full-time "#{file}" | awk '{$1=$2=$3=$4=$5=$6=$7=$8=""; print $0}'`.strip
    date = `ls --full-time "#{file}" | awk '{print $6, $7, $8}'`.strip
    @files[server][name] = DateTime.parse(date)
  end
end

# Collect diff information on all servers
ls_threads = SERVERS.map do |server|
  Thread.new do
    if server == CLIENT
      files = Dir.glob("#{CLIENT_PATH}/**/*")
      grab_filenames_and_dates(files, server)
    else
      Net::SSH.start(server, 'hstevens') do |session|
        files = session.exec!(%Q(ruby -e 'puts Dir.glob("#{BRICK_PATH}/**/*")')).split("\n")
        grab_filenames_and_dates(files, server)
      end
    end
  end
end
ls_threads.each(&:join)
当我运行程序时,它适用于本地服务器客户端/local17,但在远程服务器上失败。我尝试调试将pwd打印到控制台“”的语句,结果显示,尽管该方法是在Net::SSH会话块内调用的,但它仍在本地服务器上运行

ls: cannot access /export/hunter_test/sorttable.js: No such file or directory
ls: cannot access /export/hunter_test/sorttable.js: No such file or directory
./gluster_rsync.rb:36:in `parse': invalid date (ArgumentError)
    from ./gluster_rsync.rb:36:in `block in grab_filenames_and_dates'
    from ./gluster_rsync.rb:33:in `each'
    from ./gluster_rsync.rb:33:in `grab_filenames_and_dates'
    from ./gluster_rsync.rb:53:in `block (3 levels) in <main>'
    from /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.9.2/lib/net/ssh.rb:215:in `start'
    from ./gluster_rsync.rb:51:in `block (2 levels) in <main>'

如何在Net::SSH会话中正确地包装方法调用?

我100%没有欺骗你。。。但是您的简介正是创建rsync的原因。在具有不同功能但效率较高的服务器之间移动文件

在我看来,认为自己可以做得比20年的经过战斗测试的C代码更好有些误导。哪个FWIW的执行速度比ruby代码快得多。这可能就是为什么这么多人都支持rsync作为解决方案

虽然rsync是单线程的。。。问问你自己为什么。。。仅仅因为您可以在ruby中使用多线程并不意味着您应该这样做。它将打开一个完整的另一个意大利面怪物,你很快就会发现自己的任务是处理重复或不正确的版本。参见MongoDB关于原子性的讨论。在ruby中,您甚至无法接近原子,因此这将是一个问题

如果你想走这条路,我肯定会使用线程安全语言,至少是jRuby。FWIW线程安全是何塞创造长生不老药的众多原因之一,因为他对ruby并没有真正拥有长生不老药感到恼火

但是,您的方法存在一些问题,您需要后退几步,从整体上看待问题,例如,GlusterFS可能有类似的解决方案,可以在FS级别处理重复数据消除,或者,您可能需要通过API或某种队列系统来处理文件添加,这些系统将按顺序处理文件。它可能需要一个比你愿意或能够做的更大的改变,所以如果是我,我会犹豫是否用ruby编写牛仔代码,因为有一天一些开发人员会跳进代码中,并立即使用facepalm

多线程rsync不是ruby

我能想到的唯一解决方案就是集中精力使rsync传输更快

也许你可以用线程代替

或者使用此人的方法。这似乎是GlusterFS的一个问题,但可以更好地进行差分同步。然后,您的ruby脚本可以从主源获取文件


运行在net::ssh块中的Ruby代码仍然在您的计算机上运行这包括运行命令(如system或backticks)的方法

要在远程服务器上执行命令,需要使用session.exec或session.exec!后者是阻塞,前者需要运行ssh事件循环。您还可以显式打开通道并在那里执行命令-这些方法都是良心包装器

远程运行ruby没有特殊的支持。你当然可以使用exec!在另一台机器上运行ruby,假设它已安装,但仅此而已,

帮助我找到了以下解决方案。知道session.exec!由于只运行shell命令,我决定在SSH块中将方法see question拆分为多个步骤

Thread.new do
  files = nil
  Net::SSH.start(server, 'hstevens') do |session|
    files = session.exec!(%Q(cd "#{BRICK_PATH}" ; ruby -e 'puts Dir.glob("**/*")')).split("\n")
    files.delete_if { |x| File.directory? x }
    files.each do |file|
      name = session.exec!(%Q(ls --full-time "#{BRICK_PATH}/#{file}" | awk '{$1=$2=$3=$4=$5=$6=$7=$8=""; print $0}')).strip
      date = session.exec!(%Q(ls --full-time "#{BRICK_PATH}/#{file}" | awk '{print $6, $7, $8}')).strip
      @files[server][name] = DateTime.parse(date)
    end
  end
end

我还不知道这是否证明运行基准测试的速度更快,但在几个系统调用中它肯定比SSH-ing好。

为什么不使用为此设计的工具?如果不是全部的话,它可以做很多事情,是经过战斗测试的,并且是*nix系统的标准部分。我在SO聊天中经常听到这个。为问题添加原因。但基本上,rsync+gluster+many files=slow.rsync将比用Ruby编写的要快得多。rsync是编译代码,是为特定目的编写的。如果速度不重要,那么你可以用Ruby来完成,它会完成任务的。您还可以在后台查看哪些运行,查看文件更改并立即同步这些更改。我有包含1800多个子目录和764K+文件的目录,并使用lsyncd维护镜像。它可以在几秒钟内进行近乎实时的同步。这并不能回答我的问题。当SSH插入另一台计算机时,我需要执行我在上面的程序中定义的方法。我如何打开一个频道?DOCSV1网站让我有点困惑。NETSSHV1很古老。在v2上,您可以使用open_channel方法打开一个通道,但正如我所说,您只能在v2上执行shell命令
远程机器,而不是ruby方法,除非您的shell命令可能运行rubyv1文档,对我来说更好,因为它们在章节中已经列出。关于完整的语法,我阅读了v2文档。我现在明白了——我没有读到你在上面的评论中是如何解释的。谢谢。我接受了你的说法,因为我不能运行方法,只能运行shell命令。我知道你没有拖拉,但我被分配了这个任务,因为在我们的例子中,rsync和GlusterFS不能很好地配合使用。谢谢你的建议。我投票支持你,因为我同意你的观点,我正努力在线程中运行rsync。我仍然会继续我目前的项目,但我想更多地了解rsync。我确实感受到了你的痛苦,虽然re:从mgmt继承带有疯狂参数的项目。祝你好运如果速度很重要,请考虑只做一个LS命令,而不是通过AWK进行管道传输,然后在Ruby中解析它来提取名称和日期。Stringsplit可能足以取代awk正在做的大部分工作。