2017-06-06 46 views
0

我正在參數化我的散景應用程序,通過讓我的Flask應用程序通過專用路徑傳遞請求的數據通過查詢字符串參數。我知道數據發送路徑起作用,因爲當我使用它作爲url到AjaxDataSource時,我得到預期的數據繪圖。但是,當我嘗試使用requests.get api進行等效操作時,我得到一個503響應代碼,這讓我覺得我違反了基本的東西,在我的有限webdev體驗中我無法完全理解。我做錯了什麼或違反了什麼?Flask中的代碼503與嵌入式散景服務器應用程序通過requests.get獲取jsonified數據()

我實際上需要更多的數據檢索靈活性,而AjaxDataSource提供了其列表限制。我希望依靠requests模塊來傳遞任意類實例,以及通過序列化和反序列化Json傳遞什麼。

這裏是我已經展示了從flask_embed.html得到的故障的小例子...

import requests 
from flask import Flask, jsonify, render_template 
import numpy as np 
import pandas 
from tornado.ioloop import IOLoop 

from bokeh.application   import Application 
from bokeh.application.handlers import FunctionHandler 
from bokeh.embed    import autoload_server 
from bokeh.layouts    import column 
from bokeh.models    import AjaxDataSource,ColumnDataSource 
from bokeh.plotting    import figure 
from bokeh.server.server  import Server 

import pdb 

flask_app = Flask(__name__) 

# Populate some model maintained by the flask application 
modelDf = pandas.DataFrame() 
nData = 100 
modelDf[ 'c1_x' ] = range(nData) 
modelDf[ 'c1_y' ] = [ x*x for x in range(nData) ] 
modelDf[ 'c2_x' ] = range(nData) 
modelDf[ 'c2_y' ] = [ 2*x for x in range(nData) ] 

def modify_doc1(doc): 
    # get colum name from query string 
    args  = doc.session_context.request.arguments 
    paramName = str(args['colName'][0].decode('utf-8')) 

    # get model data from Flask 
    url = "http://localhost:8080/sendModelData/%s" % paramName 
    source = AjaxDataSource(data    = dict(x=[] , y=[]) , 
          data_url   = url  , 
          polling_interval = 5000  , 
          mode    = 'replace' , 
          method   = 'GET' ) 
    # plot the model data 
    plot = figure() 
    plot.circle('x' , 'y' , source=source , size=2) 
    doc.add_root(column(plot)) 

def modify_doc2(doc): 
    # get column name from query string 
    args = doc.session_context.request.arguments 
    colName = str(args['colName'][0].decode('utf-8')) 

    # get model data from Flask 
    url = "http://localhost:8080/sendModelData/%s" % colName 
    pdb.set_trace() 
    res = requests.get(url , timeout=None , verify=False) 
    print("CODE %s" % res.status_code) 
    print("ENCODING %s" % res.encoding) 
    print("TEXT %s" % res.text) 
    data = res.json() 

    # plot the model data 
    plot = figure() 
    plot.circle('x' , 'y' , source=data , size=2) 
    doc.add_root(column(plot)) 


bokeh_app1 = Application(FunctionHandler(modify_doc1)) 
bokeh_app2 = Application(FunctionHandler(modify_doc2)) 

io_loop = IOLoop.current() 

server = Server({'/bkapp1': bokeh_app1 , '/bkapp2' : bokeh_app2 }, io_loop=io_loop, allow_websocket_origin=["localhost:8080"]) 
server.start() 

@flask_app.route('/', methods=['GET']) 
def index(): 
    res = "<table>" 
    res += "<tr><td><a href=\"http://localhost:8080/app1/c1\">APP1 C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/app1/c2\">APP1 C2</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/app2/c1\">APP2 C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/app2/c2\">APP2 C2</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/sendModelData/c1\">DATA C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/sendModelData/c2\">DATA C2</a></td></tr>" 
    res += "</table>" 
    return res 

@flask_app.route('/app1/<colName>' , methods=['GET']) 
def bkapp1_page(colName) : 
    script = autoload_server(model=None , url='http://localhost:5006/bkapp1') 
    script = appendQuery(script , "colName" , colName) 
    return render_template("embed.html", script=script) 

@flask_app.route('/app2/<colName>' , methods=['GET']) 
def bkapp2_page(colName) : 
    script = autoload_server(model=None , url='http://localhost:5006/bkapp2') 
    script = appendQuery(script , "colName" , colName) 
    return render_template("embed.html", script=script) 

@flask_app.route('/sendModelData/<colName>' , methods=['GET']) 
def sendModelData(colName) : 
    x = modelDf[ colName + "_x" ].tolist() 
    y = modelDf[ colName + "_y" ].tolist() 
    return jsonify(x=x , y=y) 

def appendQuery(script , key , value) : 
    # Pass along the parameter as a query string to the script's src url: TODO this will formally be introduced in next release of Bokeh to avoid this hack 
    script_list = script.split("\n") 
    idxSrcAttr = 2 
    script_list[idxSrcAttr] = script_list[idxSrcAttr][:-1] + "&{}={}\"".format(key , value) 
    script = "\n".join(script_list) 
    return script 

if __name__ == '__main__': 
    from tornado.httpserver import HTTPServer 
    from tornado.wsgi import WSGIContainer 
    from bokeh.util.browser import view 

    print('Opening Flask app with embedded Bokeh application on http://localhost:8080/') 

    # This uses Tornado to server the WSGI app that flask provides. Presumably the IOLoop 
    # could also be started in a thread, and Flask could server its own app directly 
    http_server = HTTPServer(WSGIContainer(flask_app)) 
    http_server.listen(8080) 

    io_loop.add_callback(view, "http://localhost:8080/") 
    io_loop.start() 

這裏的網頁渲染... Comparison of working vs not working Flask Model json retrieval

這裏的一些調試輸出...

C:\TestApp>python flask_embedJSONRoute.py 
Opening Flask app with embedded Bokeh application on http://localhost:8080/ 
> C:\TestApp\flask_embedjsonroute.py(52)modify_doc2() 
-> res = requests.get(url , timeout=None , verify=False) 
(Pdb) n 
> C:\TestApp\flask_embedjsonroute.py(53)modify_doc2() 
-> print("CODE %s" % res.status_code) 
(Pdb) n 
CODE 503 
> C:\TestApp\flask_embedjsonroute.py(54)modify_doc2() 
-> print("ENCODING %s" % res.encoding) 
(Pdb) n 
ENCODING utf-8 
> C:\TestApp\flask_embedjsonroute.py(55)modify_doc2() 
-> print("TEXT %s" % res.text) 
(Pdb) n 
TEXT 
> C:\TestApp\flask_embedjsonroute.py(56)modify_doc2() 
-> data = res.json() 
(Pdb) 

    File "C:\Anaconda3\lib\json\decoder.py", line 357, in raw_decode 
    raise JSONDecodeError("Expecting value", s, err.value) from None 
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) 

回答

0

這似乎不是與散景本身的問題,而是在服務器中的線程和阻塞問題t帽子運行Flask應用程序。

這是可重複的除了散景完全...

import requests 
from flask import Flask, jsonify, request 
import pandas 
import pdb 

flask_app = Flask(__name__) 

# Populate some model maintained by the flask application 
modelDf = pandas.DataFrame() 
nData = 100 
modelDf[ 'c1_x' ] = range(nData) 
modelDf[ 'c1_y' ] = [ x*x for x in range(nData) ] 
modelDf[ 'c2_x' ] = range(nData) 
modelDf[ 'c2_y' ] = [ 2*x for x in range(nData) ] 

@flask_app.route('/', methods=['GET']) 
def index(): 
    res = "<table>" 
    res += "<tr><td><a href=\"http://localhost:8080/sendModelData/c1\">SEND C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/sendModelData/c2\">SEND C2</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/RequestsOverFlaskNoProxy?colName=c1\">REQUEST OVER FLASK NO PROXY C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/RequestsOverFlaskNoProxy?colName=c2\">REQUEST OVER FLASK NO PROXY C2</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/RequestsOverFlask?colName=c1\">REQUEST OVER FLASK C1</a></td></tr>" 
    res += "<tr><td><a href=\"http://localhost:8080/RequestsOverFlask?colName=c2\">REQUEST OVER FLASK C2</a></td></tr>" 
    res += "</table>" 
    return res 

@flask_app.route('/RequestsOverFlaskNoProxy') 
def requestsOverFlaskNoProxy() : 
    print("RequestsOverFlaskNoProxy") 
    # get column name from query string 
    colName = request.args.get('colName') 

    # get model data from Flask 
    url = "http://localhost:8080/sendModelData/%s" % colName 

    print("Get data from %s" % url) 
    session = requests.Session() 
    session.trust_env = False 
    res = session.get(url , timeout=5000 , verify=False) 
    print("CODE %s" % res.status_code) 
    print("ENCODING %s" % res.encoding) 
    print("TEXT %s" % res.text) 
    data = res.json() 
    return data 

@flask_app.route('/RequestsOverFlask') 
def requestsOverFlask() : 
    # get column name from query string 
    colName = request.args.get('colName') 

    # get model data from Flask 
    url = "http://localhost:8080/sendModelData/%s" % colName 
    res = requests.get(url , timeout=None , verify=False) 
    print("CODE %s" % res.status_code) 
    print("ENCODING %s" % res.encoding) 
    print("TEXT %s" % res.text) 
    data = res.json() 
    return data 

@flask_app.route('/sendModelData/<colName>' , methods=['GET']) 
def sendModelData(colName) : 
    x = modelDf[ colName + "_x" ].tolist() 
    y = modelDf[ colName + "_y" ].tolist() 
    return jsonify(x=x , y=y) 

if __name__ == '__main__': 
    print('Opening Flask app on http://localhost:8080/') 

    # THIS DOES NOT WORK 
    #flask_app.run(host='0.0.0.0' , port=8080 , debug=True) 

    # THIS WORKS 
    flask_app.run(host='0.0.0.0' , port=8080 , debug=True , threaded=True) 

Different behavior serving the same data

人們可以從屏幕上看到拍攝直接從sendModelData服務數據正確地呈現JSON,但是當通過requests.get方法獲取由於Python控制檯中報告的503代碼而產生異常。

如果我試圖消除我通過環境變量啓用的proxies的效果,但此方法從未完成,並且請求會使瀏覽器無限期旋轉。

來想一想,甚至可以使用請求作爲中間人來完全沒有必要,我應該能夠得到json字符串並自己去反序列化它。那麼,這將在我的實際代碼中工作,在我的實際代碼中,Bokeh渲染是在完全不同的python模塊中完成的,因此這些函數甚至不可用,除非我拼湊應用程序的分層。

編輯 事實證明我違反了與瓶的開發環境的根本的東西...

您正在運行與瓶測試服務器的WSGI應用程序,它通過 默認使用單線程處理請求。因此,當您的一個 請求線程嘗試回撥到同一臺服務器時,仍嘗試處理該請求時仍爲 。 https://stackoverflow.com/a/22878916/1330381

那麼接下來的問題是如何在原有的散景範例應用此線程=真正的技術?這可能無法通過flask_embed.py示例對Tornado WSGI服務器的依賴,該服務器從此question建議Tornado是單線程的設計。 考慮到上述發現,更爲關鍵的問題是AjaxDataSource如何避免requests模塊面臨的這些線程問題?

相關問題