I'm getting started with WSGI and until now, with a little help from some tutorials,I'm making some tests towards Flask with uWSGI in front of it, since that Flask is not a good option for Production environments (doesn't scale well and by default, it answers one request per time - http://flask.pocoo.org/docs/0.12/deploying/) and uWSGI gives flexibility and more reliability, spawning workers and processes. Am I wrong?
Most of the tutorials that I saw until, are pointing about setups with Nginx in front of WSGI, but is it really necessary? What I'm trying to do is just to give a scalable way to deliver requests to my Flask application, something with more performance and scalability.
So I have this basic setup:
hello.py
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080)
wsgi.py
from hello import app
if __name__ == "__main__":
app.run()
Running uWSGI:
uwsgi --socket 0.0.0.0:8080 --plugin python --wsgi-file wsgi.py --callable app --master --processes 4 --threads 2 &
When I perform a curl against the loopback address, I receive an empty reply..
curl http://127.0.0.1:8080
invalid request block size: 21573 (max 4096)...skip
curl: (52) Empty reply from server
Forgive me, but I can't see what I'm missing. Does anyone here, more experienced with WSGI, could point where is the failure of this setup? Any help would me much appreciated.
Reference documents:
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uwsgi-and-nginx-on-ubuntu-16-04
http://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
Your option, socket should be used when you combine uwsgi with web server (nginx for example). Otherwise you should use http, so
uwsgi --http 0.0.0.0:8080 --plugin python --wsgi-file wsgi.py --callable app --master --processes 4 --threads 2
will work.
Production environments (doesn't scale well and by default, it answers
one request per time - http://flask.pocoo.org/docs/0.12/deploying/)
and uWSGI gives flexibility and more reliability, spawning workers and
processes. Am I wrong?
You are right.
Most of the tutorials that I saw until, are pointing about setups with
Nginx in front of WSGI, but is it really necessary? What I'm trying to
do is just to give a scalable way to deliver requests to my Flask
application, something with more performance and scalability.
Well, nginx is designed to be in front and having it is much better then only application server (uwsgi). Specialisation, that's the key. Let your application server focus on bussiness processing and python.
Related
I am working on a FastAPI web application and using Uvicorn ASGI server. Now, want to configure server stats in Uvicorn but have not found a reference regarding.
Ex - As like uWSGI Stats Server provides stats -
uwsgi --socket :3031 --stats :1717 --module welcome
So, my question is Does Uvicorn supports the stats server mechanism? or, Is there any other way to achieve this?
No, there is an open issue on uvicorn for this. Check this comment for details https://github.com/encode/uvicorn/issues/610#issuecomment-611987371
I have a service, which contains 2 Flask-applications - let's name them app and monitoring_app running on different ports. monitoring_app is an utility service which provides metrics collected with prometheus_client. I have faced up with an issue trying run them under the Gunicorn server properly.
I have read through the similar topic:
Multiple Flask Application in single uwsgi
but it seems my problem can't be solved with Dispatcher Middleware.
I can start these applications without Gunicorn like this:
from threading import Thread
import os
from my_project import create_app, create_monitoring_app
def start_app(my_app):
my_app.run(host="localhost",
debug=True,
port=int(os.environ.get("API_PORT", "5000")),
threaded=True,
use_reloader=False)
if __name__ == "__main__":
app = create_app()
app_thread = Thread(target=start_app, daemon=True, args=(app,))
app_thread.start()
monitoring_app = create_monitoring_app()
monitoring_app.run(host="localhost",
port=int(os.environ.get("OPS_PORT", "5001")),
threaded=True,
use_reloader=False,
debug=True)
It works ok for development, but it runs under Flask development server which is not ok for production environment. With Gunicorn I can start them separately:
gunicorn "my_project:create_monitoring_app()" -b "[::]:$OPS_PORT" &
gunicorn "my_project:create_app()" -b "[::]:$API_PORT"
but then they will be in different interpreters and I can't use prometheus_client of app in monitoring_app what is critical for me
What can I do to achieve same behavior as if I run these applications from my development environment? Or may be I am doing something wrong and I should do it in another way?
I tried adding the websockets example project to the datastore project and the websockets work but when a page queries the datastore or tries to put a new entity I get a 502 response. In the logs it shows a critical error on the service worker. If I remove the websocket code the datastore code works as intended. The only difference I can see is the entrypoints for the app samples slightly differ
the websocket sample uses
entrypoint: gunicorn -b :$PORT -k flask_sockets.worker main:app
while the datastore sample uses
entrypoint: gunicorn -b :$PORT main:app
websocket sample https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/flexible/websockets
datastore sample
https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/flexible/datastore
The problem appears to be that GRPC (the default transport mechanism of the Cloud Datastore client) is not compatible with gevent. Aside from using a different websockets framework, you can work around the issue by activating grpc's gevent compatibility patch, using the following code:
import grpc.experimental.gevent as grpc_gevent
grpc_gevent.init_gevent()
As a complement of Andrew's answer, you can extend the gunicorn worker class to run gRPC applications.
# gevent_grpc_worker.py
from gunicorn.workers.ggevent import GeventWorker
from grpc.experimental import gevent
class GeventGrpcWorker(GeventWorker):
def patch(self):
super(GeventGrpcWorker, self).patch()
gevent.init_gevent()
self.log.info('patched grpc')
# config.py for gunicorn
import multiprocessing
from gevent_grpc_worker import GeventGrpcWorker
# http://docs.gunicorn.org/en/stable/design.html#how-many-workers
workers = multiprocessing.cpu_count() * 2 + 1
worker_connections = 10000
# Use an asynchronous worker as most of the work is waiting for websites to load
worker_class = '.'.join([GeventGrpcWorker.__module__,
GeventGrpcWorker.__name__])
timeout = 30
Then start your managed application by:
gunicorn -c config.py app:app
As you said it seems there is a problem with the flask_socket.worker, I have test it and it does not work with the datastore client.
I have tried with the Flask-SocketIO framework using the eventlet worker and the datastore queries work fine.
entrypoint: gunicorn -b :$PORT --worker-class eventlet -w 1 main:app
Also you need to add the eventlet module in the requirements.txt file eventlet==0.24.1
The downside of this is that it breaks the compatibility with the websocket code so you need to rewrite this part. Keep in mind that code samples are just intended to show in a few lines how to use the Google Cloud products and copy-paste them without modifying the configuration undelied in the app.yaml is not a good idea.
Very new to this.
I am trying to follow along with this tutorial.
The main difference to my setup is that I am not using a domain, but would rather just access the flask site (something for personal use only) through the ip address.
This step:
Test sample flask app: If you want, test yourself for typos. uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi
... works and I can access the flask app at ipaddress:5000
My /etc/httpd/conf/httpd.conf ServerName is configured with the ipaddress rather than flaskdemo.com
Problematically though once I spin up the apache service with
sudo systemctl start httpd
... I get a 503 at ipaddress
Thanks in advance for any support.
My question is basically what's in the title: how can I setup gunicorn to run a web.py app? (Also, if there are any differences, how would I do it on heroku?)
I already have my app running on heroku using the built in cherrypy, but I have not been able to get gunicorn to work with web.py (I just have no idea where to start - I couldn't find any tutorials).
I'm afraid I'm not familar with Heroku, but I can answer your basic question.
gunicorn is a HTTP server for running Python web apps via WSGI. web.py is a framework for creating Python web apps using WSGI.
So you don't really need a tutorial for using both together, as all you need to do is figure out how to pass the WSGI entry point of your web.py application to gunicorn. A WSGI application is just a Python callable with the right interface i.e. it takes certain parameters and returns a certain response. See this WSGI tutorial for more.
The "hello world" application from the web.py tutorial looks like this test.py:
import web
urls = (
'/', 'index'
)
class index:
def GET(self):
return "Hello, world!"
if __name__ == "__main__":
app = web.application(urls, globals())
app.run()
But that does not expose the WSGI application which gunicorn needs.
web.py provides a WSGI application via the wsgifunc method of web.application. We can add this to test.py by adding the following after the index class:
# For serving using any wsgi server
wsgi_app = web.application(urls, globals()).wsgifunc()
This is basically what the web.py documentation tells you to do in the deployment section, when using Apache + mod_wsgi - the fact that the Python code is the same for us with gunicorn is not a coincidence because this is exactly what WSGI gives you - a standard way to write the Python so that it can be deployed using any WSGI capable server.
As explained in the gunicorn docs, we can then point gunicorn at the wsgi_app member of the test module as follows:
(tmp)day#office:~/tmp$ gunicorn test:wsgi_app
2012-12-03 23:31:11 [19265] [INFO] Starting gunicorn 0.16.1
2012-12-03 23:31:11 [19265] [INFO] Listening at: http://127.0.0.1:8000 (19265)
2012-12-03 23:31:11 [19265] [INFO] Using worker: sync
2012-12-03 23:31:11 [19268] [INFO] Booting worker with pid: 19268