Celery task.delay blocked in docker container - django

I use celery in my django project. It works well on my MacBook and in a CentOS VM. When I run it in a docker container, the request which contains add.delay(add is a task) method is always blocked.
I created a demo project on github: https://github.com/fengyouchao/proj_test
My task:
#shared_task
def add(x, y):
return x + y
My view:
def index(request):
a = int(request.GET.get('a', 1))
b = int(request.GET.get('b', 2))
add.delay(a, b)
return HttpResponse("Hello world")
def hello(request):
return HttpResponse("hello")
In the demo project I created three services in docker-compose.yml:
web - The service which run "manage.py runserver 0.0.0.0:8000"
celery - The service which run "celery"
rabbitmq - The service wich run rabbitmq-server
Run services
docker-compose up
Test
curl localhost:8000 # blocked
curl localhost:8000/hello # OK
Run the django project in current system(use the same rabbitmq-server in docker container)
manage.py runserver 0.0.0.0:18000
Test
curl localhost:18000 # OK , and the "celery" service printed task logs
This problem has been bothering me for a long time, and I don't know where the problem is. I hope someone can help me. Thanks!

I just came across a similar issue,
I am using rabbitmq container as a broker so added CELERY_BROKER_URL in settings.py
when I run the add.delay() in manage.py django shell, inside the container it got struck but works fine in production
So I have added the following change, it started working
app = Celery('app', broker="amqp://rabbitmq")

I have faced the same issue, and fixed by it importing the app created on proj/proj/celery.py in my proj/proj/__init__.py like this:
from __future__ import absolute_import, unicode_literals
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
__all__ = ('celery_app',)
You can see more information in Celery's first steps with django documentation.
Hope it helps!

Related

Restart python Script on Server after crashing

I am currently trying to set um a server to run a tensorflow Application. It is working fine but if there are too many requests the server terminates the flask Application which is requesting the answer from my tensorflow model.
This means that the Server is useless as long as I don't restart the flask app manually with python3 flaskApp.py in the server terminal.
Is there a way to restart the Python Script automatically once it fails? <== !! main question !!
It doesn't bother me when I don't get a return value once in a while but I don't want to manually restart the flaskApp once a day.
Here is the code for my flask Application, the method 'handler' returns a probability from my tensorflow model running in the background.
from flask import Flask, redirect, request, jsonify
from modelv4 import *
from waitress import serve
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
#app.route('/', methods=['POST'])
def processjson():
data = request.get_json()
satz = data['text']
print(satz)
ruckgabe = handler(satz)
ruckgabe = round(ruckgabe*10000)
ruckgabe = ruckgabe / 100
ruckgabe = str(ruckgabe)
ruckgabe = jsonify({"ruckgabe": ruckgabe})
#ruckgabe.headers.add('Access-Control-Allow-Origin', '*')
return ruckgabe
if __name__=='__main__':
serve(app, host="0.0.0.0", port=8080)
The server is running on aws EC2 as an Ubuntu instance, so you get a basic linux terminal.
If you need any more information to answer my question, please let me know.
Since the application is running on an ubuntu server I recommend using systemd.
You can let systemd auto-restart it in case it fails or is accidentally killed.
To do this, you can add the restart option to the .service file you created specifically for your application.
How can I run my flask application as a service with systemd?
A possible configuration of your .service file could be the following:
[Unit]
Description=My flask app
After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5s
ExecStart= <script to start your application>
[Install]
WantedBy=multi-user.target

Watson assistant deployment on Flask+WSGI server (gunicorn or wsgi)

I am deploying my watson assistant chatbot on Flask + Gunicorn + Nginx.
I am able to successfully dockerize and run , but something is breaking my code. Multiple watson assistant sessions are being created while I send the messages to watson services. While I try to reply for an intent I get answer for another intent or slot or does not understand message
I have reviewed all the tutorials on digital ocean and github, but I think creating chatbot session should be handled differently.
app.py
from flask import Flask, render_template,Response,make_response,jsonify
import os
from ibm_watson import AssistantV2
import random
from random import randint
import json
#import report
from io import StringIO
app = Flask(__name__)
conversation = AssistantV2(
iam_apikey = 'key',
url='https://gateway.watsonplatform.net/assistant/api',
version='2019-05-19')
session = conversation.create_session("someid").get_result()
variables = None
#context_val = {}
#app.route('/')
#app.route('/index')
def chat():
return render_template('chat.html')
#app.route('/send_message/<message>')
def send_mesage(message):
text = ''
response = conversation.message(
assistant_id = 'id',
session_id= session['session_id'],input={'text': str(message),'options': {
'return_context': True}}
).get_result()
variables = response['output'].get('user_defined')
#context = response['context']['skills']['main skill']['user_defined']
for i in response['output']['generic']:
text = text+ i['text']+'\n'
return text
if __name__ == "__main__":
app.run(host='0.0.0.0')
wsgi.py
from app import app
if __name__ == "__main__":
app.run()
Dockerfile
FROM python:3.6
WORKDIR /app
ADD . /app
RUN chgrp -R 0 /app/app.log && chmod -R g=u /app/app.log
RUN pip install -r requirements.txt
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "app", "-p 8080:8080"]
RUN chmod 770 /app
USER 1001
When working with IBM Watson Assistant with the V2 API, you need to be aware of the following objects:
First, you create an Assistant. It manages the connection to Watson Assistant.
Next, a Session is a per user interaction within the chat.
Last, a Message flows to Watson within a session with a Response coming back.
You probably have seen this simple code sample in the docs, your own code is - on a general level - similar. To make it work, you need to create Watson sessions per user sessions, then send the messages as part of the corresponding session. That way, the chat context is kept correctly. Your code currently initialize Watson and creates a session once. You need to create a session per user. Look into session management.

Start Celery worker on production. Using Django/Python on Azure/linux app service

I have a website with an API that customers can send their API-post-calls. These API's have attachments in form of a PDFs or similar that gets stored in a folder /MEDIA/Storage/. The app is written in Django.
The API-call gets stored in a model through DRF and serializers. After the data is stored some logic is done, emails os sent, lookups and storing in data-tables etc. Since this takes so much time. I implemented Celery (Azure Cache for Redis as Broker) in my app, so that only the first storage in model is done as usual. The rest us queued up through Celery.
This works well on my local machine (mac os). But not on production (Azure/Linux).
I have tried git hooks, but i cannot get it working.
I have tried some terminal through ssh on the azure VM, but no luck...
I have looked into Daemonization but it was complicated.
settings.py
CELERY_BROKER_URL = 'redis://:<password>=#<appname>.redis.cache.windows.net:6379/0'
CELERY_RESULT_BACKEND = 'django-db'
CELERY_CACHE_BACKEND = 'django-cache'
celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hapionline.settings')
app = Celery('hapionline')
app.config_from_object('django.conf:settings', namespace="CELERY")
app.autodiscover_tasks()
#app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
views.py
class ProcSimpleList(generics.CreateAPIView): # Endast Create för att skapa en proc
serializer_class = ProcSimpleSerializer
permission_classes = (IsAdminOrReadOnly,)
lookup_url_kwarg = 'proc_id'
def perform_create(self, serializer):
q = serializer.save()
# Queue from starting worker. Queue created when starting cereal.
transaction.apply_async(queue='high_priority', args=(q.proc_id, self.request.user.pk))
Local machine: All works well with the command: celery -A hapionline worker -l info -Q high_priority
Production: I do not know where to run the command on the production server?
If the worker is started on the local machine, it starts the Azure Cache, and calling the production environment API works. But since the worker is started locally the Paths too attached files in the API are incorrect and local, not production-like. /User/../Media/.. instead of /wwwroot/../media/..
Any ideas? How do I start a worker on the production VM? Is there a way to run a the start worker "script" after the git push azure master?
I skipped Azure and moved the app to Heroku. This worked as a charm.

Celery + Django not working at the same time

I have Django 2.0 project that is working fine, its integrated with Celery 4.1.0, I am using jquery to send ajax request to the backend but I just realized its loading endlessly due to some issues with celery.
Celery Settings (celery.py)
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'converter.settings')
app = Celery('converter', backend='amqp', broker='amqp://guest#localhost//')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
#app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
Celery Tasks (tasks.py)
from __future__ import absolute_import, unicode_literals
from celery import shared_task
#shared_task(time_limit=300)
def add(number1, number2):
return number1 + number2
Django View (views.py)
class AddAjaxView(JSONResponseMixin, AjaxResponseMixin, View):
def post_ajax(self, request, *args, **kwargs):
url = request.POST.get('number', '')
task = tasks.convert.delay(url, client_ip)
result = AsyncResult(task.id)
data = {
'result': result.get(),
'is_ready': True,
}
if result.successful():
return self.render_json_response(data, status=200)
When I send ajax request to the Django app it is loading endlessly but when terminate Django server, and I run celery -A demoproject worker --loglevel=info that's when my tasks are running.
Question
How do I automate this so that when I run Django project my celery tasks will work automatically when I send ajax request?
If you are on development environment, you have to run manually celery worker as it does not run automatically on the background, in order to process the jobs in the queue. So if you want to have a flawless workflow, you need both Django default server and celery worker running. As stated in the documentation:
In a production environment you’ll want to run the worker in the background as a daemon - see Daemonization - but for testing and development it is useful to be able to start a worker instance by using the celery worker manage command, much as you’d use Django’s manage.py runserver:
celery -A proj worker -l info
You can read their documentation for daemonization.
http://docs.celeryproject.org/en/latest/userguide/daemonizing.html

Unable to configure Gunicorn to serve a flask app running another loop concurrently

I have a simple flask app, say like this:
# app.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
I also have a slack bot reading messages
#bot.py
def serve(self):
while True:
message, channel = self.parse_slack_output(self.slack_client.rtm_read())
if message and channel:
self.handle_message(message, channel)
time.sleep(self.READ_WEBSOCKET_DELAY)
I want both the codes to run concurrently. So in app.py I do:
#app.py
if __name__ == "__main__":
import threading
import bot
flask_process = threading.Thread(target=app.run)
bot_process = threading.Thread(target=bot.serve)
bot_thread.start()
flask_thread.start()
This code works as expected with $ python app.py, But when I bring in gunicorn the bot thread doesn't seem to work.
I have tried:
gunicorn app:app
gunicorn --workers=2 app:app
gunicorn --threads=2 app:app
I also tried the multiprocessing library and got the same results.
Any idea how this issue can be tackled? Thanks.
Edit: I now understand how lame this question is. I shouldn't be writing code in if __name__ = "__main__": block. That is not what is run by gunicorn. It directly picks up the app and runs it. Still have to figure how to make it handle the bot thread.
I have made this work with the following solution:
# app.py
from flask import Flask
import threading
import bot
def create_app():
app = Flask(__name__)
bot_process = threading.Thread(target=bot.serve)
return app
app = create_app()
#app.route('/')
def hello_world():
return 'Hello, World!'
This makes sure that gunicorn --workers=1 app:app runs both the app and the bot in different threads. While this works, one drawback with this solution is I am not able to scale up the number of workers to > 1. As this would not only scale the app thread, but also the bot thread, which I don't want. The bot would then unnecessarily listen for messages in two threads.
Any better solution in your mind? please convey it. Thanks.