use*_*759 2 python linux subprocess flask
在这篇文章之后,我可以将tail -f日志文件发送到网页:
from gevent import sleep
from gevent.wsgi import WSGIServer
import flask
import subprocess
app = flask.Flask(__name__)
@app.route('/yield')
def index():
def inner():
proc = subprocess.Popen(
['tail -f ./log'],
shell=True,
stdout=subprocess.PIPE
)
for line in iter(proc.stdout.readline,''):
sleep(0.1)
yield line.rstrip() + '<br/>\n'
return flask.Response(inner(), mimetype='text/html')
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
Run Code Online (Sandbox Code Playgroud)
这种方法有两个问题。
tail -f log关闭网页后,该过程将持续存在。访问http://localhost:5000/yield n 次后会有 n 个 tail 进程我的问题是,是否可以让flask在有人访问页面时执行shell命令,并在客户端关闭页面时终止该命令?就像Ctrl+C之后tail -f log。如果没有,还有哪些替代方案?为什么我一次只能有 1 个客户端访问该页面?
注意:我正在研究启动/停止任意 shell 命令的一般方法,而不是特别尾随文件
这是一些应该完成这项工作的代码。一些注意事项:
您需要检测请求何时断开连接,然后终止进程。下面的 try/ except 代码可以做到这一点。然而,在inner()到达末尾后,Python将尝试正常关闭套接字,这将引发异常(我认为这是socket.error,按照How to handle a Broken Pipe (SIGPIPE) in python?)。我找不到一种方法来干净地捕获这个异常;例如,如果我在inner()末尾显式引发StopIteration,并用try/ except socket.error块包围它,它就不起作用。这可能是 Python 异常处理的限制。您可能可以在生成器函数中执行其他操作来告诉 Flask 中止流式传输,而无需尝试正常关闭套接字,但我还没有找到它。
你的主线程在 proc.stdout.readline() 期间阻塞,而 gevent.sleep() 来得太晚,无法提供帮助。原则上 gevent.monkey.patch_all() 可以修补标准库,以便通常会阻塞线程的函数将控制权交给 gevent (请参阅http://www.gevent.org/gevent.monkey.html)。然而,这似乎并没有修补 proc.stdout.readline()。下面的代码使用 gevent.select.select() 等待 proc.stdout 或 proc.stderr 上的数据可用,然后再生成新数据。这允许 gevent 在等待时运行其他 greenlet(例如,为其他 Web 客户端提供服务)。
网络服务器似乎会缓冲发送到客户端的前几 kB 数据,因此在将许多新行添加到 ./log 之前,您可能不会在网络浏览器中看到任何内容。之后,似乎立即发送新数据。不确定如何立即发送请求的第一部分,但这可能是流服务器的一个非常常见的问题,所以应该有一个解决方案。对于自行快速终止的命令来说这不是问题,因为它们的完整输出将在终止后发送。
您还可以在https://mortoray.com/2014/03/04/http-streaming-of-command-output-in-python-flask/找到有用的东西。
这是代码:
from gevent.select import select
from gevent.wsgi import WSGIServer
import flask
import subprocess
app = flask.Flask(__name__)
@app.route('/yield')
def index():
def inner():
proc = subprocess.Popen(
['tail -f ./log'],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# pass data until client disconnects, then terminate
# see /sf/ask/1295778361/
try:
awaiting = [proc.stdout, proc.stderr]
while awaiting:
# wait for output on one or more pipes, or for proc to close a pipe
ready, _, _ = select(awaiting, [], [])
for pipe in ready:
line = pipe.readline()
if line:
# some output to report
print "sending line:", line.replace('\n', '\\n')
yield line.rstrip() + '<br/>\n'
else:
# EOF, pipe was closed by proc
awaiting.remove(pipe)
if proc.poll() is None:
print "process closed stdout and stderr but didn't terminate; terminating now."
proc.terminate()
except GeneratorExit:
# occurs when new output is yielded to a disconnected client
print 'client disconnected, killing process'
proc.terminate()
# wait for proc to finish and get return code
ret_code = proc.wait()
print "process return code:", ret_code
return flask.Response(inner(), mimetype='text/html')
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5425 次 |
| 最近记录: |