I'm working on Flask/Python back-end for web application, which will be deployed with Docker. It's receiving some data from the user, then send it to the RabbitMQ queue. On the other side there're workers in docker containers. The consume task, process and send result back to other RabbitMQ message queue. It works pretty well when I'm running Flask app locally without placing it into docker. What I'm doing - on application start-up I'm running receive thread, which is linked to callback function that consume result from message queue and update note in database. Deploying it with docker and gunicorn as web server, thread is not started with no reason.
How can I run background threads, that would work alongside with Flask web application through the whole web application cycle?
At this moment I'm thinking about next options:
Try to start thread on application start-up
Run background task with Celery (cons: after reading a lot of stuff I think Celery is not applicable here because it mostly using for task that could be finished in the upcoming future. I could be wrong. If yes, please correct me).
Flask module called app_scheduler. I've used it just couple times and I think Celery is better in this case.
My web app structure is pretty simple:
app.py
models.py
app
|-engine - # classes Send and Receive, function run_receive()
|-tasks
|-__init__.py
|-routes.py
|-tools.py
|-email.py
|-templates
|-__init__.py # create_app() function here
My create_app() function from the app/__init__.py file.
def create_app(config_class=Config):
app_ = Flask(__name__)
app_.config.from_object(config_class)
# extensions
db.init_app(app_)
migrate.init_app(app_, db)
celery.init_app(app_)
mail.init_app(app_)
# blueprints
from app.engine import bp as bp_engine, run_receive
from app.tasks import bp as bp_tasks
app_.register_blueprint(bp_engine)
app_.register_blueprint(bp_tasks)
...
So what I want to do is on start-up run thread which will consume results from RabbitMQ. Function for this action is called run_receive() and is placed in the app/engine/__init__.py.
Also in the future I'd like to run another thread to check whether notes in the database are out-of-date. So it would be another thread, that would be invoked every day to check database.
So what are the best practices in this situation?
Thank you for your response.
Related
I've setup a kafka server on AWS and I already have a Django project acting as the producer, using kafka-python.
I've also setup a second Django project to act as the consumer (kafka-python), but I'm trying to figure out a way to run the consumer automatically after the server has started without having to trigger the consumer through an API call.
Everything I've tried so far either runs the consumer and blocks the server from starting or runs the server and blocks the consumer.
I did something like this on a Django project : I put the consumer launch into a daemon thread into a method and I call this method in the manage.py file.
I'm not really sure about impacts of modify manage.py file, but it's work fine.
def run_consumers():
thread = threading.Thread(name=my_consumer, target=main.lauch_consumer, args=()
thread.daemon = True
thread.start()
And in manage.py I added :
if os.environ.get('RUN_MAIN'):
# Run consumers once at server start
run_consumers()
I've been reading a lot around different ways to shutdown a Flask app but I don't get it how I could implement something for my use case.
I wrote and am testing a simple Flask app which takes a POST request to create some resources within Google Cloud. This Flask app is deployed into a container and is running on Cloud Run.
My question is, I want to shutdown the app right after a 200 response or would there be a way to handle one request per Cloud Run instance?
app = Flask(__name__)
#app.route('/', methods=['POST'])
def main():
#some validation on the request.json
try:
kick_off_terraform()
return ("Success", 200)
except Exception as e:
print(e)
After doing some research I found out I can control the concurrency on the GCP side, and that way I can allow only one request per instance on Cloud Run.
gcloud run deploy $SERVICE_NAME
--image gcr.io/$GCP_PROJECT_ID/$SERVICE_NAME:$CI_COMMIT_SHORT_SHA
--region=us-east1
--platform managed
--concurrency=1
Sadly hacks like --concurrency=1 or --max-instances=1 are not great because shutting down the server after a request may cause the request to fail. (When I did that in the past, requests failed.)
Based on your question I am guessing you might not have fully grasped Cloud Run runtime behavior. Please note that:
You don't need to "shut down" a container on Cloud Run. It automatically suspends once all requests finish, and you are not even charged for the idle time that occurs outside of a request.
Operations like kick_off_terraform() can't happen in the background (they have to finish before you return the response), because Cloud Run currently doesn't allocate CPU in the background.
What you need is something like "run to completion containers" and you may need to wait a bit for that to be supported by Cloud Run.
I'm using flask + uwsgi + nginx to deploy website on server.
In the flask, my code is below, here is what I want to design: every time when I click run model, it would run a model in another process, but the interface would link to the waiting interface immediately.
train_dataset.status = status
db.session.commit()
text = 'Start training your models, please wait for a while or check results several hours later'
# would run a model in another process
task = Process(target=start_train, args=(app.config['UPLOAD_FOLDER'], current_user.id, p.id, p.task), name="training.exe")
task.start()
print(task.pid, task.name)
session[f"{project_name}_train"] = task.pid
# in the meanwhile, link to waiting interface
return render_template('p_computeview.html', project_name=project_name, text=text,
status=status, p=p, email=email, not_identify=not_identify)
And when I test in local development environment
app.run()
it's ok, when I click run model, the interface just link to wait interface and I can see the model running logs.
But when I deploy to server, I chose uwsgi + nginx + flask.
In uwsgi.ini, I already specify the processes
processes=2
threads=5
But when I click run model, the interface was still, didn't link to waiting interface, however I can see the model running logs, and when finished modeling, the interface would link to waiting interface (which prove that the Process function was not working ??)
my server have 2 cpus, so I think it can support multi process
Can someone help me ? I guess there are some problems in uwsgi or nginx ?
The desire to run separate threads or processes within a request context is a common one. For various reasons, except in very narrow circumstances, it is a desire that leads to frustration. In this case, as soon as task goes out of scope, the Process machinery gets torn down.
If you want to start a long-running task from a request handler, use a framework like celery or Rq, which arrange to run jobs entirely out of process from the http server.
I have made a website with django. I want to deploy this application to production.
Now I am confused about several things: at the time of the development I can run my application using the command: python manage.py runserver IP_OF_SERVER:PORT.
Now by this approach I can do everything. My tool (website) will only be used locally.
Is it fine that I deploy my website with this command only? Is is necessary to do django production processes and if it is necessary how to do that? I am new to django. Please help me to understand.
Usually, these kinds of things are deployed in a three tier fashion.
Here, the general approach is like so
[Database] <-(db calls)-> [Django app] <- wsgi -> [gunicorn] <- http -> [nginx] <- http -> public
your application is the "Django app" block over here. You could run it with something like manage.py runserver but that's a very lightweight toy server which can't really handle high levels of traffic. If you have a request that takes 1ms to handle and 100 users try to make the same request, the last client will have to wait 100ms before she can get the response. It's easy to solve this by just running more instances of your application but the dev server can't do that.
A so called "app server" like gunicorn will allow you to run a more powerful web server for your application that can spawn off multiple workers and handle some kinds of mischievous traffic patterns.
Now, even gunicorn can be bested by a high performance server especially for serving static assets like images, css, js etc. This is something like nginx. So, we set up our thing to have nginx facing the world and serving all static assets directly. And request to the actual application will be proxied to gunicorn and that will be served by your actual app.
This is not as complex as it sounds and you should be able to get something running within a day or so. All the technologies I've mentioned have substitutes with different characteristics. This is a good tutorial on how to get things going and what to look out for during deployment.
If you want to setup your Django production server through IIS in Windows server (If users are less, you can even use normal Windows 7 or 10 professional machines as well). This video can help you do it step by step
https://www.youtube.com/watch?v=kXbfHtAvubc
I have brought up couple of production websites this way.
Although the one you are trying to do also works, but the only problem with your approach is that you should take care that console is never closed by anyone or you accidentally. But there are lot more hidden problems, usually for production, this is not recommended.
To avoid accidental closures, you can do below in Windows (running it as a service):
For this approach, you need pywin32 needs to be installed, install it from here: https://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/
import win32service
import win32serviceutil
import win32event
import subprocess
import os
class PySvc(win32serviceutil.ServiceFramework):
# you can NET START/STOP the service by the following name
_svc_name_ = "Name your Service here"
# this text shows up as the service name in the Service
# Control Manager (SCM)
_svc_display_name_ = "External Display Name of your service"
# this text shows up as the description in the SCM
_svc_description_ = "Description what this service does"
def __init__(self, args):
import platform
win32serviceutil.ServiceFramework.__init__(self, args)
# create an event to listen for stop requests on
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
# core logic of the service
def SvcDoRun(self):
os.chdir('your site root directory')
subprocess.Popen('python manage.py runserver IP:PORT')
# if you need stdout and stderr, open file handlers and keep redirecting to those files with subprocess `stdout` and `stderr`
# called when we're being shut down
def SvcStop(self):
# tell the SCM we're shutting down
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# fire the stop event
win32event.SetEvent(self.hWaitStop)
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(PySvc)
I have a python script.
Main thread (if name=='main', etc): when the main thread initiates, it runs several threads to listen to data streams, events, and to process them. The main thread then starts running the Flask application (app.run()). Processing and data is sent to the front-end Flask app (no issues here)
The Apache Server and mod_wsgi requires me to directly import the app, meaning that my other threads won't run.
My dilemma. In the examples I've seen, the .wsgi script from someapp imports app as application. This would only run the flask application. If I managed to somehow run the python script instead as main, the flask application would be ran on localhost:5000 by default and is not recommended in production to change or use .run().
First of all, is it possible to get this application on a server in this current structure? How would I get the whole application to work on a server? Would I need to completely restructure it? Is it not possible to specify host: 0.0.0.0 port:80 then run the python script instead of just importing the app? Any help is appreciated, any forwarding to other documentations.
Edit: for the sake of testing, I will be using AWS Ubuntu (any other linux distro can be used/switched to if needed).
Sort and misleading answer is yes, it is possible (make sure there is any other program that uses port 80 such as apache etc):
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
However, you should not do that. Not recommended as it states in the documentation:
You can use the builtin server during development, but you should use
a full deployment option for production applications. (Do not use the
builtin development server in production.)
Proxy HTTP traffic through apache2 to Flask is much better.
This way, apache2 can handle all your static files and act as a reverse proxy for your dynamic content, passing those requests to Flask.
To have threads check the documentation of WSGIDaemonProcess.
Example of Apache/mod_wsgi configuration should looks like this:
WSGIDaemonProcess mysite processes=3 threads=2 display-name=mod_wsgi
WSGIProcessGroup mysite
WSGIScriptAlias / /some/path/wsgi.py
I managed to find an answer to this without diverging too far from guides on how to get a Flask application working with Python3 and Apache2.
In short, when you initialise Flask, you most likely do something like this:
from flask import Flask
app = Flask(__name__)`
The proposed solution:
import atexit #for detecting flask exit
import threading
from flask import Flask
shareddata = 0
running = False
def init_app():
global shareddata
global running
running = True
app = Flask(__name__)
# some threading goes here
# e.g.
def jointhread():
running=False
t.join()
def MyThread1():
while(running):
#do something
t1 = threading.Thread(target=MyThread1, args=[])
t1.start()
atexit.register(jointhread)
return app
app = init_app()
Threading might not work, whichever's applicable.
I had a similar issue where there was a thread I wanted to constantly monitor data using an API. I ended up importing the function(s) I wanted threaded to my WSGI file and kicked them off there.
Example
import threading
from main import <threaded_function>
my_thread = threading.Thread(target=<threaded_function>)
my_thread.start()