【问题标题】:Ruby, Puma & Sinatra: display streaming outputRuby、Puma 和 Sinatra:显示流输出
【发布时间】:2021-01-14 14:20:17
【问题描述】:

我有一个 Puma 应用程序,当您输入 IP 地址并通过单击复选框选择对其进行跟踪路由时,它将执行跟踪路由:

下面的代码(实际上应用程序做得更多,但为了这个问题,我已经简化了它):

网站.erb:

class Pumatra < Sinatra::Base

    get '/' do
        erb :index
    end

    post '/run' do
    params.to_s
    end

 get'/traceroute_results' do
        @ipaddress  = params[:ipaddress]
        @traceroute = params[:traceroute]

            if @traceroute == "on"
                    stdout, status = Open3.capture2("traceroute -4 -w3 #{@ipaddress}")
                    @traceroute_result ="<pre><code>" + stdout + "</code></pre>"
            end

       @traceroute_header
            @traceroute_result
        end

        @traceroute_thread = Thread.new{traceroute()}
        @traceroute_thread.join

        erb :traceroute_results
    end
end

我的 views/index.erb 文件包含这个(只向你们展示相关的部分):

....
        <div class="checkbox">
            <label><input type="checkbox" id="traceroute" 
             name="traceroute">Traceroute</label>
        </div>
....

        <!-- Start Results block -->
        <section id="results_block" style="display:none;" class="l_panel bg_color_white l_relative">
            <div id="traceroute_results" style="display:none;" class="l_grid"></div>
        </section>
        <!-- End Results block -->

这一切都很好。 Traceroute 运行并在完成后显示结果。 但是,有时由于啤酒花没有响应,这需要很长时间才能完成,尽管这是正常的,并且最终会返回结果。 我的问题是:仅出于美观的原因,我想在运行时显示输出跟踪路由。所以,更多的是一种渐进的观点,而不是最终的完整输出。 这可能吗?我该如何做?

欣赏提示, J

编辑:

我在 site.erb 中试过这个

   get'/traceroute_results' do
                @traceroute_header = "<br>TCP Traceroute results:"
                  IO.popen("traceroute -4 -w3 #{@ipaddress}") do |io|
                        io.each do |line|
                        @traceroute_result = line
                        erb :traceroute_results
                        end
                  end

    end

但现在我的应用程序没有显示任何 traceroute 输出:(

我的traceroute_results.erb 文件:

<%= @traceroute_header %>
<%= @traceroute_result %>

编辑: Amadan 的有用帖子让我在我的 site.erb 中找到了这段代码:

 get'/traceroute_results' do
        @ipaddress  = params[:ipaddress]
        @traceroute = params[:traceroute]

          if @traceroute == "on"
            ipaddress  = params[:ipaddress]
              stream do |res|
                res << erb(:traceroute_header)
                Open3.popen3("traceroute -T -4 #{@ipaddress}") do |stdin, stdout, 
                  stderr, wait_thr|
                  while line = stderr.gets
                    res << erb(:traceroute_results, {}, { out: line })
                  end
                end
              end
          end
  end

并将我的traceroute_results 文件更改为:

<%= out %>

不幸的是,当 url 加载时,我仍然没有收到任何信息。我可以告诉它正在执行跟踪路由,因为它需要一段时间才能返回结果,但它返回的是空的。

真的非常感谢任何人在这方面的帮助和耐心。 我正在与 Sinatra 一起运行 Ruby Puma。任何人都可以在这样的设置中成功运行它吗?我还没有找到在 puma 上下文中实际工作的 Sinatra 示例...... :(

谢谢, J

【问题讨论】:

    标签: ruby sinatra puma


    【解决方案1】:

    首先,注意erb 不发送响应;它通常成为响应,因为它的结果是 Ruby 函数中的最后一件事,它被隐式返回,并且 Sinatra 以 get 块的返回值响应。您当前的实现会呈现您的子模板,但会丢弃每个结果;同时IO.popen(...) {...}返回nil,是没有响应的原因。

    现在,为了在 Sinatra 中生成流式响应,您可以返回响应 #each 的内容,或者使用 stream helper 为您创建这样的对象。试着改成这样:

    get '/traceroute_results' do
      ipaddress  = params[:ipaddress]
      stream do |res|
        res << erb(:traceroute_header)
        IO.popen("traceroute -4 -w3 #{ipaddress}") do |io|
          io.each do |line|
            res << erb(:traceroute_result, {}, { line: line })
          end
        end
      end
    end
    
    __END__
    @@ traceroute_result
    <div><%= line %></div>
    
    @@ traceroute_header
    <h4>Traceroute results</h4>
    

    【讨论】:

    • 感谢@Amadan 我正在努力理解,我应该将__END__ @@traceroute_result &lt;div&gt;&lt;%= line %&gt;&lt;/div&gt; 放在哪里?我将更新我的问题,向您展示我的 erb 文件。
    • 这只是拥有traceroute_result.erb 文件的替代方案。这与问题无关,我只是为了做出一个独立的答案。重点是使用stream 助手,并为其提供字符串。
    • 你不能在同一个erb中同时拥有迭代部分和非迭代部分使用这种结构;您需要将它们分开。您可以使用res &lt;&lt; erb(:traceroute_header) 作为stream do |res| 内的第一行,在循环之前放置一些内容。
    • 啊,我明白了。我在我的 site.rb 中尝试了您的代码,并将我的 traceroute_result.erb 更改为仅包含 &lt;%= @line %&gt; 。但是仍然没有输出。我可能做错了什么?谢谢你的耐心..
    • 我不知道 - 我无权访问您的代码。所以我写了一个独立的代码,你可以自己运行和测试。
    【解决方案2】:

    Amadan's answer 既正确又不完整,使用each 时可能会导致服务器上的线程不足。

    我不是 Sinatra 开发人员,但我编写服务器代码,这就是为什么我不得不警告不要使用 each

    each 的问题是服务器在等待each 迭代和返回时没有控制线程 - 这会阻止线程处理其他请求强>.

    更好的解决方案是使用线程池和队列来调用traceroute,并使用 SSE / WebSockets 连接将输出从队列流式传输到用户。

    这将保护服务器免受 DoS 情况的影响,并允许它:

    1. 当太多请求同时到达时显示“等待”消息(可能与队列的长度有关)。

    2. 当数据可用时,将数据从traceroute 流式传输到客户端,而不会阻塞服务器(将数据推送到客户端)。

    3. 如果客户端断开连接,请取消traceroute

    这可以使用 WebSocket 原生解决方案或使用劫持解决方案来执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-10
      • 2013-03-22
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 2022-01-06
      • 2015-09-21
      相关资源
      最近更新 更多