在flask应用内部调用Twisted recator.run()



我正在为Jasmin SMS网关编写python flask中的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)

我正在调用runScenario(),这是在JasminIntegration.py

中定义的
#!/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()

请帮我解决这个问题:

按照设计,反应器在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服务器?您可以完全使用twisted构建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我也试图在扭曲中这样做,但遇到了同样的问题,因为反应器不可重新启动。在twisted中,我做了这样的事情:

#!/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()

最新更新