在 Flask 应用程序中调用 Twisted recator.run()

Calling Twisted recator.run() Inside flask app

我正在 python flask 中为 Jasmin SMS Gateway 编写 Web 服务,以从网关创建和删除用户。在 POST 请求的情况下,我调用 runScenario() 然后我开始 reactor.run() 这将在网关中创建用户。此代码在第一次 Web 服务调用时运行完美,但在第二次调用时出现此错误:

raise error.ReactorNotRestartable()
ReactorNotRestartable

这是我的 Flask 应用程序:

#!/usr/bin/env python
from flask import Flask, jsonify, request, Response, make_response, abort
from JasminIntegration import *

JasminWebservice = Flask(__name__)

@JasminWebservice.errorhandler(404)
def not_found(error):
    return make_response(jsonify({'error': 'Not found'}), 404)

@JasminWebservice.route('/jsms/webservice', methods=['POST'])
def create_user():
    if not request.json or not 'username' in request.json:
        abort(400)

    runScenario(request)
    reactor.run()
    return jsonify({'response':'Success'}), 201

if __name__ == '__main__':    
    JasminWebservice.run(host="0.0.0.0",port=7034,debug=True)

我正在调用 JasminIntegration.py

中定义的 runScenario()
#!/usr/bin/env python
import sys
import pickle
from flask import abort
from twisted.internet import defer, reactor
from jasmin.managers.proxies import SMPPClientManagerPBProxy
from jasmin.routing.proxies import RouterPBProxy
from jasmin.routing.Routes import DefaultRoute
from jasmin.routing.jasminApi import SmppClientConnector, User, Group, MtMessagingCredential, SmppsCredential
from jasmin.protocols.smpp.configs import SMPPClientConfig
from twisted.web.client import getPage


@defer.inlineCallbacks
def runScenario(Request):
    try:
        proxy_router = RouterPBProxy()
        yield proxy_router.connect('127.0.0.1', 8988, 'radmin', 'rpwd')

        if Request.method == 'POST':            
            smppUser = Request.json['username']
            smppPass = Request.json['password']
            smppThroughput = Request.json['tp']
            smppBindSessions = Request.json['sessions']

            if not smppUser:
                abort(400)

            if len(smppPass) == 0 or len(smppPass) > 8:
                abort(400)

            if not smppThroughput.isdigit():
                abort(400)

            if not smppBindSessions.isdigit():
                abort(400)

            # Provisiong router with users
            smpp_cred = SmppsCredential()
            yield smpp_cred.setQuota('max_bindings',int(smppBindSessions))

            mt_cred = MtMessagingCredential()
            yield mt_cred.setQuota('smpps_throughput' , smppThroughput)
            #yield mt_cred.setQuota('submit_sm_count' , 500)

            g1 = Group('clients')
            u1 = User(uid = smppUser, group = g1, username = smppUser, password = smppPass, mt_credential = mt_cred, smpps_credential = smpp_cred)
            yield proxy_router.group_add(g1)
            yield proxy_router.user_add(u1)

        if Request.method == 'DELETE':

            smppUser = Request.json['username']

            if not smppUser:
                abort(404)

            yield proxy_router.user_remove(smppUser) 
    except Exception, e:
        yield "%s" %str(e)

    finally:
        print "Stopping Reactor"
        reactor.stop()

请帮我解决这个问题:

Reactor 在 Twisted 中设计为不可重启,它执行不容易重启的初始化和终结。

在提供的示例中,您使用的是开发 WSGI 服务器(flask 的默认服务器,http://werkzeug.pocoo.org/docs/0.11/serving/),默认情况下它似乎是单线程的。

如果您避免使用线程并改用多进程服务器,您的问题就会消失。 也就是说,如果你只是 运行 它会起作用(参见 processes=2 => 每个请求将在一个新进程中处理,但不超过 2 个并发请求) :

if __name__ == '__main__':                                                      
    JasminWebservice.run(host="0.0.0.0", port=7034, debug=True, processes=2)

但是我不会依赖它——在编写单元测试并将您的应用程序限制为 运行 时,您会 运行 陷入类似的麻烦仅多进程环境不是一个好方法。

但问题似乎出在您的应用程序设计上——为什么您需要 Flask 和一个额外的 WSGI 服务器? 您可以在扭曲中完全构建 REST API,最终 运行 只有一个反应器可以处理对您的 API 和传入请求的查询。

你不能停止反应堆然后再运行它。您遇到的错误是设计使然。考虑使用 klein 它像 flask 一样使用 werkzeug 并利用 twisted 进行联网。语法甚至相似。将您的代码翻译成 klein 看起来有点像这样:

import json
from klein import Klein
from exception werkzeug.exceptions import NotFound
from JasminIntegration import *

JasminWebservice = Klein()

@JasminWebservice.handle_errors(NotFound)
def not_found(request, error):
    request.setResponseCode(404)
    return json.dumps({'error': 'Not found'})

@JasminWebservice.route('/jsms/webservice', methods=['POST'])
def create_user(request):
    try:
        data = json.loads(request.content.read())
        if not data or if 'username' not in data:
            raise NotFound()
    except:     # yeah I know this isn't best practice
        raise NotFound()

    runScenario(request)
    request.setResponseCode(201)
    return json.dumps({'response':'Success'})

if __name__ == '__main__':    
    JasminWebservice.run("0.0.0.0", port=7034)

附带说明一下,您不应该停止 reactor 除非您想完全退出您的应用程序。

@ffeast 我也尝试过扭曲但遇到了同样的问题,因为反应器不是 restartable.In 扭曲我做了这样的事情:

#!/usr/bin/env python
from pprint import pprint
import json
from twisted.web import server, resource
from twisted.internet import reactor
from JasminIntegration import *
import ast

class Simple(resource.Resource):
    isLeaf = True
    def render_GET(self, request):
        return "<html>Hello, world!</html>"

    def render_POST(self, request):
        pprint(request.__dict__)
        newdata = request.content.getvalue()
        newdata = ast.literal_eval(newdata)
        ret = runScenario(newdata)
        #print  request.content
        #print newdata
        return ''
site = server.Site(Simple())
reactor.listenTCP(7034, site)
reactor.run()