我正在编写一个应用程序,它可以公开一个用flask实现的简单RPC接口。但是我希望它可以激活和停用该接口。此外,应该可以在同一个python解释器中运行多个应用程序实例,每个实例都有自己的RPC接口。
该服务仅暴露给localhost,这是一个原型,所以我不担心安全性。我正在寻找一个小而简单的解决方案。 这里显而易见的方法似乎是使用烧瓶开发服务器,但我找不到关闭它的方法。
我已经为我要公开的功能创建了一个烧瓶蓝图,现在我正在尝试编写一个类来包装RPC接口,类似于:
class RPCInterface:
def __init__(self, creating_app, config):
self.flask_app = Flask(__name__)
self.flask_app.config.update(config)
self.flask_app.my_app = creating_app
self.flask_app.register_blueprint(my_blueprint)
self.flask_thread = Thread(target=Flask.run, args=(self.flask_app,),
name='flask_thread', daemon=True)
def shutdown(self):
# Seems impossible with the flask server
raise NotImplemented()
我正在使用当前应用程序的变量my_app将此RPC接口正在处理的应用程序实例传递到请求的上下文中。
它可以从请求内部关闭(如此处所述http://flask.pocoo.org/snippets/67/),因此一种解决方案是创建关闭端点并向测试客户端发送请求以启动关闭。然而,这需要烧瓶终点才能达到此目的。这远非干净。
我查看了flask和werkzeug的源代码并找出了重要部分(https://github.com/pallets/werkzeug/blob/master/werkzeug/serving.py#L688处的上下文),如下所示:
def inner():
try:
fd = int(os.environ['WERKZEUG_SERVER_FD'])
except (LookupError, ValueError):
fd = None
srv = make_server(hostname, port, application, threaded,
processes, request_handler,
passthrough_errors, ssl_context,
fd=fd)
if fd is None:
log_startup(srv.socket)
srv.serve_forever()
make_server
返回werkzeugs服务器类的实例,该实例继承自pythons http.server
类。这反过来又是一个python BaseSocketServer
,它暴露了一个关闭方法。问题是这里创建的服务器只是一个局部变量,因此无法从任何地方访问。
这是我遇到死胡同的地方。所以我的问题是:
答案 0 :(得分:0)
回答我自己的问题,以防任何人再次发生这种情况。
第一个解决方案涉及从烧瓶切换到klein。 Klein基本上是具有较少特征的烧瓶,但是在扭曲的反应器顶部运行。这种集成非常简单。基本上它的工作原理如下:
from klein import Klein
from twisted.internet import reactor
app = Klein()
@app.route('/')
def home(request):
return 'Some website'
endpoint = serverFromString(reactor, endpoint_string)
endpoint.listen(Site(app.resource()))
reactor.run()
现在所有扭曲的工具都可用于根据需要启动和停止服务器。
我转向更进一步的第二个解决方案是摆脱HTTP作为传输协议。我在twisted的LineReceiver协议之上切换到JSONRPC。这样一切都变得更简单了,无论如何我都没有使用任何HTTP内容。
答案 1 :(得分:0)
这是一个可怕的可怕骇客,任何人都不应该将其用于任何目的……除非您试图编写集成测试套件,否则除了 。可能有更好的方法-但是,如果您要尝试完全按照问题的要求进行操作,那么这里就可以...
import sys
from socketserver import BaseSocketServer
# implementing the shutdown() method above
def shutdown(self):
for frame in sys._current_frames().values():
while frame is not None:
if 'srv' in frame.f_locals and isinstance(frame.f_locals['srv'], BaseSocketServer):
frame.f_locals['srv'].shutdown()
break
else:
continue
break
self.flask_thread.join()