Вот еще одно возможное решение. Это программа, которую вы должны запустить как отдельный процесс в конвейере, который представляет REST API, который при запросе будет возвращать последние N строк, прочитанных на стандартном вводе (где N и номер порта предоставляются на стандартном вводе). Он использует run
в flask, поэтому не должен использоваться в ситуациях, когда внешний мир имеет доступ к порту локального сервера для выполнения запросов, хотя это можно адаптировать.
import sys
import time
import threading
import argparse
from flask import Flask, request
from flask_restful import Resource, Api
class Server:
def __init__(self):
self.data = {'at_eof': False,
'lines_read': 0,
'latest_lines': []}
self.thread = None
self.args = None
self.stop = False
def parse_args(self):
parser = argparse.ArgumentParser()
parser.add_argument("num_lines", type=int,
help="number of lines to cache")
parser.add_argument("port", type=int,
help="port to serve on")
self.args = parser.parse_args()
def start_updater(self):
def updater():
lines = self.data['latest_lines']
while True:
if self.stop:
return
line = sys.stdin.readline()
if not line:
break
self.data['lines_read'] += 1
lines.append(line)
while len(lines) > self.args.num_lines:
lines.pop(0)
self.data['at_eof'] = True
self.thread = threading.Thread(target=updater)
self.thread.start()
def get_data(self):
return self.data
def shutdown(self):
self.stop = True
func = request.environ.get('werkzeug.server.shutdown')
if func:
func()
return 'Shutting down'
else:
return 'shutdown failed'
def add_apis(self, app):
class GetData(Resource):
get = self.get_data
class Shutdown(Resource):
get = self.shutdown
api = Api(app)
api.add_resource(GetData, "/getdata")
api.add_resource(Shutdown, "/shutdown")
def run(self):
self.parse_args()
self.start_updater()
app = Flask(__name__)
self.add_apis(app)
app.run(port=self.args.port)
server = Server()
server.run()
Пример использования: вот тестовая программа, вывод которой мы хотим обслуживать:
import sys
import time
for i in range(100):
print("this is line {}".format(i))
sys.stdout.flush()
time.sleep(.1)
И простой конвейер для ее запуска (здесь из приглашения оболочки linux, но может быть выполнен через subprocess.Popen
), обслуживающий последний 5 строк, на порту 8001:
python ./writer.py | python ./server.py 5 8001
Пример запроса, здесь используется curl в качестве клиента, но это можно сделать через Python requests
:
$ curl -s http://localhost:8001/getdata
{"at_eof": false, "lines_read": 30, "latest_lines": ["this is line 25\n", "this is line 26\n", "this is line 27\n", "this is line 28\n", "this is line 29\n"]}
сервер также предоставляет URL-адрес http://localhost:<port>/shutdown
для его завершения, хотя, если вы вызовете его до того, как впервые увидите "at_eof": true
, то ожидайте, что писатель отправит d ie со сломанным каналом.