无法使用 nginx、fastcgi 和 bash 禁用 cgi 上的缓冲

问题描述 投票:0回答:3

我想制作一个 CGI 来启动程序并在网络浏览器中实时显示输出。这里的实时意味着程序一旦产生一行输出,它就应该显示在浏览器页面上。

我选择在 bash 中编写它来包装程序执行,以便我可以处理请求参数。

webbrowser -> nginx+fastcgi -> bash -> program

我有一个测试程序,每半秒输出一行,共10次。

我想我可以在响应头中声明纯文本上下文类型,然后执行程序。

不幸的是,输出仅在执行结束时一次性出现在浏览器中。我在 Firefox 和curl 中测试了它。

我已经测试了许多选项及其任意组合来解决该问题:

  • 使用
    fastcgi_buffering off
    指令禁用 nginx 中的缓冲
  • 添加
    X-Accel-Buffering
    标题
  • 使用
    stdbuf -oL program
  • 使用 xml http 请求 (XHR) + 服务器端事件 (SSE) 而不是纯文本。

什么都不起作用。

我猜缓冲问题出在 nginx 和 bash 之间,但我找不到办法禁用它。

我可以尝试什么?

bash nginx fastcgi server-sent-events buffering
3个回答
0
投票

我找不到解决链中缓冲问题的方法。我尝试了 perl 而不是 bash,但没有成功。

所以我选择填充缓冲区:在受控程序的每一行输出之后,我回显一堆“”。由于该内容无法由网络浏览器作为纯文本处理,因此我使用服务器发送事件方法。

#!/bin/sh

printf "HTTP/1.0 200 OK\r\n"
printf "Content-type: text/event-stream\r\n"
printf "Cache-Control: no-cache\r\n"
printf "X-Accel-Buffering: no\r\n"
printf "\r\n"

flush() {
    padding=4100
    dd if=/dev/zero bs=$padding count=1 2>/dev/null
}

subprogram | while read l;
do
    printf "data: ${l}\n\n"
    flush
done

包装页看起来像这样:

<html>
<head>
   <meta charset="UTF-8">
   <title>Server-sent events demo</title>
</head>
<body>
  <pre></pre>
<script>
var evtSource = new EventSource('/sse.sh?subprogram');
var pre = document.querySelector('pre');
evtSource.onmessage = function(e) {
  pre.textContent += e.data + '\n';
}
</script>
</body>
</html>

在这种情况下,网络浏览器会负责删除多余的“ ”。

缺点是cgi输出远远大于程序输出。


0
投票

已经有一段时间了,但为了解决这个问题,我在 fcgiwrap 代码中实现了自定义修改。

fcgiwrap 中问题的根源是缺乏对

FCGI_fflush()
的显式调用,即使 stdout 管道被刷新也是如此。

为了解决这个问题,我建议使用以下链接从 GitHub 克隆我的 fcgiwrap 存储库:fcgiwrap 存储库。这应该有助于解决问题。


0
投票

谢谢!我遇到了同样的问题,并且在网上找不到任何其他提及的内容。 我冒昧地简化了你的方法以适应一个文件:

#!/usr/bin/env bash
# fcgiwrap does not flush
# send bash output as html and add &zwnj; zero width non joining characters to fill 4k buffer

cat << \
~~~
HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: no-cache
X-Accel-Buffering: no

<!DOCTYPE html>
<html>
 <body>
  <div style="max-height: 97vh;  margin: 0 15px; overflow: hidden auto; display: flex; flex-direction: column-reverse;">
   <div>
    <pre>
~~~

subprogram | while read l;
do
    printf '%s' "${l}      "
    printf '%*s\r\n' 682 | sed 's/ /\&zwnj;/g'
done

cat << \
~~~
    </pre>
   </div>
  </div>
 </body>
</html>
~~~

第一个具有

flex-direction: column-reverse;
的两个 div 确保当新输出到达时屏幕保持滚动。

也许这对来这里的其他人有用。

© www.soinside.com 2019 - 2024. All rights reserved.