【问题标题】:Get Live logs in front end on rails application在 Rails 应用程序的前端获取实时日志
【发布时间】:2014-03-01 23:43:45
【问题描述】:

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

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是在每一个命令执行的时候发送数据,我需要这些数据立即发送到浏览器。有什么办法可以做到这一点。这样我就可以在前端浏览器中实现实时日志了。谢谢!

【问题讨论】:

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


    【解决方案1】:

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

    • 短轮询
    • 流媒体
    • 长轮询

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

    短轮询

    短轮询是从服务器获取待处理信息的经典方式。这意味着客户端每 X 秒询问一次服务器“你有新信息吗?”,服务器回答“否”,在这种情况下,客户端再次休眠 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
    

    【讨论】:

    • 我希望在我的情况下流式传输会是更好的选择,您能否提供更详细的方法来在 Rails 应用程序中实现这一点。感谢“Q”的回答!
    • 其实应该不会比我在rails中写的多多少...我确实在这个案例的客户端实现上添加了更多细节。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 2021-11-21
    • 2021-06-13
    • 2013-09-04
    相关资源
    最近更新 更多