在rails应用程序的前端获取实时日志

时间:2014-02-05 13:41:36

标签: ruby-on-rails ruby ssh eventmachine net-ssh

这里在rails 3.x应用程序中,我使用net :: ssh并向远程pc运行一些命令。我想将实时日志显示给用户的浏览器。如果有两个命令在net中运行: :ssh执行即echo "Hello"echo "Bye"被传递然后“Hello”应该在浏览器中显示在执行后立即完成。这是我用于ssh连接并在ruby上运行命令的代码铁路应用

Net::SSH.start( @servers['local'] , @machine_name, :password => @machine_pwd, :timeout => 30) do |ssh|      
  ssh.open_channel do |channel|
    channel.request_pty         
    channel.exec("echo 'ssh started'")                                   
    channel.exec("ruby -v") 
    channel.exec("rails -v") 
    channel.exec("echo 'ssh Finished'") 


    channel.on_close do |ch|
      puts "****shell terminated****"
    end

    channel.on_eof do |ch|
      puts "****remote end is done sending data****"            
    end
    channel.on_extended_data do |ch, type, data|
      puts "****got stderr****: #{data.inspect}"
    end
    channel.on_data do |channel, data|
      puts "****ondata****: #{data} \n"                    
      puts "****channel****: #{channel} \n"          
      $logs << data # this data to be sent immediately to browser in each go                                  
    end

  ssh.loop      
  end
end  

这里on_data在每个命令执行中发送数据,我需要立即将这些数据发送到浏览器。有什么办法可以做到这一点。所以我可以在前端浏览器中实现实时日志。谢谢!

1 个答案:

答案 0 :(得分:3)

要实现“立即向浏览器发送数据”,您需要使用以下三种策略之一:

  • 短轮询
  • 长轮询

(在以下示例中,我假设$logs的类型为Queue

短轮询

短轮询是从服务器获取待处理信息的经典方法。这意味着每隔X秒客户端询问服务器“你有新信息吗?”,服务器回答“否”,在这种情况下客户端再回到睡眠状态X秒,或服务器回答'是,这里有一些新信息......',客户端可以在睡眠之前再渲染一段时间。

此策略的主要优点是所有浏览器和所有ruby版本(简单的AJAX调用)都支持它。

此策略的缺点是,在看到新数据之前,您将延迟最多X秒。如果X太短 - 您将遭受大量网络开销(所有请求都有空响应)。

示例实施:

def get_logs
  available_logs = []
  while(line = $logs.pop(true) rescue nil) available_logs << line

  body available_logs.join($/)
end

流式策略是当您的客户端向服务器打开请求时,服务器开始向客户端发送响应,但是当可用信息结束时,它不会关闭连接,而是将其保持打开状态。然后,当信息到来时,它会在打开的套接字上继续流式传输信息。

在Rails 4.0中有一个ActionController::Live来实现这个策略。对于Rails 3.2,您可以尝试查看以下答案:https://stackoverflow.com/a/4320399/1120015

示例实施:

def get_logs
  class LogConsumer
    def each
      while $channel.active?
        yield $logs.pop + $/
      end
    end
  end

  self.response_body = LogConsumer.new
end

此解决方案与其他两个解决方案的主要区别在于客户端实现不是那么直接 - 客户端不能只等待响应返回然后呈现它(如默认的jQuery用法)。对于示例实现,很多地方指向Ajax Patterns,目前显然不可用: - (。

我见过的另一个选项是使用portal插件*:

portal.open("/get_logs", {
  inbound: function(data) {
    render_log_lines(data);
  }
});

*我没有使用此插件的经验,所以这应该作为研究的方向,而不是作为一个工作的例子......

长轮询

长轮询意味着您的AJAX客户端请求服务器获取下一个日志行。如果服务器有一个(或多个)要提供,它将它返回给客户端,客户端呈现行,并再次请求。如果服务器没有要提供的行,则它不会返回空响​​应,而是挂起到请求,并等待一行可用。只要新行可用,它就会返回给客户端。客户端呈现该行,然后立即再次请求服务器。

当您选择此策略时,您必须确保在Web服务器或客户端中没有设置timeout,也不能在中间的任何其他点(负载平衡器等)中设置

示例实施:

def get_logs
  available_logs = []
  while(line = $logs.pop(true) rescue nil) available_logs << line

  if available_logs.empty?
    available_logs << $logs.pop
  end

  body available_logs.join($/)
end