2017-09-12 5 views
-1

J'essaie actuellement de concevoir une application de flacon qui permet des cartes en temps réel grâce à l'utilisation de douilles et de céleri. Je veux être en mesure d'obtenir des données de manière asynchrone, puis l'envoyer via un socket au client. J'obtiens cependant l'erreur: RuntimeError: Working outside of request context. Je reçois ceci quand la douille se connecte d'abord.Flacon + Céleri + socketio RuntimeError: Travailler en dehors du contexte de la demande

pile trace

Traceback (most recent call last): 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/celery/app/trace.py", line 374, in trace_task 
    R = retval = fun(*args, **kwargs) 
    File "/Users/bev/PycharmProjects/flask_project/celery_config.py", line 15, in __call__ 
    return TaskBase.__call__(self, *args, **kwargs) 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/celery/app/trace.py", line 629, in __protected_call__ 
    return self.run(*args, **kwargs) 
    File "/Users/bev/PycharmProjects/flask_project/main.py", line 20, in async_data 
    send(jsonify({"result": sample(range(101), 6)})) 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/flask/json.py", line 251, in jsonify 
    if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr: 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/werkzeug/local.py", line 347, in __getattr__ 
    return getattr(self._get_current_object(), name) 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/werkzeug/local.py", line 306, in _get_current_object 
    return self.__local() 
    File "/Users/bev/.virtualenvs/flask_project/lib/python3.6/site-packages/flask/globals.py", line 37, in _lookup_req_object 
    raise RuntimeError(_request_ctx_err_msg) 
RuntimeError: Working outside of request context. 

This typically means that you attempted to use functionality that needed 
an active HTTP request. Consult the documentation on testing for 
information about how to avoid this problem. 

main.py

from flask import Flask, render_template, jsonify 
from flask_socketio import SocketIO, send 
from random import sample 
from celery_config import make_celery 


app = Flask(__name__) 
app.config["SECRET_KEY"] = "thisisasecret" 
socketio = SocketIO(app) 

app.config.update(
    CELERY_BROKER_URL="amqp://localhost//", 
    CELERY_RESULT_BACKEND="rpc://" 
) 
celery = make_celery(app) 


@celery.task(name="main.async_data") 
def async_data(): 
    # for now this is very small as an example 
    # preferably batched to be done every 15 minutes. 
    send(jsonify({"result": sample(range(101), 6)})) 
    return True 


@app.route("/") 
def index(): 
    return render_template("chart.html") 


@socketio.on("connect") 
def handle_connection(): 
    async_data.delay() 
    print("You are connected and we are getting your data") 


if __name__ == "__main__": 
    socketio.run(app, debug=True) 

celery_config.py

from celery import Celery 


def make_celery(app): 
    celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'], 
        broker=app.config['CELERY_BROKER_URL']) 
    celery.conf.update(app.config) 
    TaskBase = celery.Task 

    class ContextTask(TaskBase): 
     abstract = True 

     def __call__(self, *args, **kwargs): 
      with app.app_context(): 
       return TaskBase.__call__(self, *args, **kwargs) 

    celery.Task = ContextTask 
    return celery 

graphique javascript

let chartConfig = { 
    type: "line", 
    data: { 
     labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], 
     datasets: [{ 
      label: "GOOG", 
      data: [], 
      borderColor: "rgba(22, 172, 65, 1)", 
      borderWidth: 1, 
      fill:false 
     }] 
    } 
}; 

let socket = io.connect("http://" + document.domain + ':' + location.port); 

socket.on("connect", function() { 
    socket.send("Connected Socket off to get data"); 
}); 

socket.on("message", function (data) { 
    chartConfig.data.datasets[0].data = data.result; 
    let ctx = document.getElementById("myChart").getContext("2d"); 
    let myLineChart = new Chart(ctx, chartConfig) 
}); 
+1

À quel moment vous avez eu l'erreur? Pourriez-vous ajouter une trace complète à la question? Comment lancez-vous l'application 'Flask' ​​et l'application' Celery'? –

+0

Je lance l'application Celery en utilisant 'python main.py' – Warmley

+0

Je suppose que' send() 'doit être dans la fonction socketio décorée' handle_connection'. Vous pouvez essayer d'utiliser 'send' dans un rappel à la tâche' async_data'. http://docs.celeryproject.org/en/latest/userguide/calling.html#linking-callbacks-errbacks –

Répondre

1

La raison est seulement dans le Flask. Faisons fait un petit test (main.py):

from random import sample 
from flask import Flask, jsonify 
from celery_config import make_celery 

app = Flask(__name__) 
app.config["SECRET_KEY"] = "thisisasecret" 

app.config.update(
    CELERY_BROKER_URL="redis://localhost:6379/0", # or your broker 
    CELERY_RESULT_BACKEND="redis://localhost:6379/0" # or your broker 
) 
celery = make_celery(app) 

@celery.task(name="main.async_data") 
def async_data(): 
    jsonify({"result": sample(range(101), 6)}) 
    return True 

@app.route("/") 
def index(): 
    async_data.delay() 
    return 'test' 

if __name__ == "__main__": 
    app.run(debug=True) 

Run Céleri et applications Flask, ouvrez '/' itinéraire. Vous verrez l'erreur suivante:

RuntimeError: Working outside of request context

Comment cela fonctionne. Vous utilisez la méthode jsonify. Comme vous pouvez le voir dans les docs, il transforme la sortie JSON en un objet Response avec l'application/json mimetype. Mais Celery task ne sait rien sur la réponse actuelle/active de Flask. C'est juste du code/traitement asynchrone. Si vous avez besoin de travailler avec json dans le céleri, vous pouvez utiliser n'importe quelle lib (json, ujson, simplejson, etc.).

Maintenant, changeons jsonify en json.dumps({"result": sample(range(101), 6)}). Vous verrez que tout fonctionne bien.

Espérons que cela aide.

+0

D'accord, ça aide un peu. Ce problème est lors de l'utilisation de socketio. Comme je veux que le résultat de 'async_data()' soit émis au client – Warmley

+1

@Warmley le problème n'est pas avec le socketio, js ou autre chose. Si vous devez obtenir le résultat de 'Celery task' vous devez utiliser [AsyncResult] (http://docs.celeryproject.org/en/latest/reference/celery.result.html#celery.result.AsyncResult). Vous pouvez obtenir uuid de tâche lorsque la tâche a été démarrée et ensuite vérifier l'état du côté client. –

+0

Mais alors, mon côté client ne sera-t-il pas interrogé? Le point de la websocket n'est-il pas long sondage? – Warmley