I am using gunicorn and flask for a web service. I am trying to get my head around running a streaming route (not sure if that is the correct terminology).
my route looks like this:
#app.route('/delay')
def delay():
from time import sleep
def delay_inner():
for i in range(10):
sleep(5)
yield json.dumps({'delay': i})
return Response(delay_inner(), mimetype="text/event-stream")
I expect that the server would yield the output each time that delay_inner does a yield. But, what I am getting is all the json responses at once, and only when the delay_inner finishes execution.
What am I missing here?
--EDIT--
I have fixed the issue for Flask and Gunicorn, I am able to run it as expected by using the flask server, and by going to the Gunicorn port. It streams the data as expected. However, and I should have mentioned this in the original post, I am also running behind nginx. And that is not set up correctly to stream. Can anyone help with that?
You need to turn off the nginx proxy buffering.
location /delay {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
}
and reload the config
nginx -s reload
Related
I have a slow request, and I want to change timeout for the worker during handling of that request and only for that request.
Basically, I have a flask application:
class Slow(Resource):
def post(self):
if slow_condition():
gunicorn.how.to.extend.processing.time.here()
do_something_slow()
api = Api(application)
api.add_resource(Slow, "/slow")
and I want to extend processing time if slow_condition returned True. How can I change timeout for the single request?
No way.
Flask is just web framework. Framework doesn't know anything about production server and settings, workers etc. By the way to change sever configuration you need to reload all workers(restart gunicorn, uWSGI, waitress etc). So you can only increase timeout parameter.
I am serving dash content inside a Flask app which uses blueprint for registering the routes.
App setup:
Dash is initialised with route_pathname_prefix=/dashapp/
dash_app = dash.Dash(
server=server,
routes_pathname_prefix='/dashapp/',
)
dash_app.css.config.serve_locally = True
dash_app.scripts.config.serve_locally = True
Registered dash app with Flask server
Used UWSGI to serve Flask app inside a docker container
[uwsgi]
wsgi-file = </path/to/app.py>
callable = app
http = 0.0.0.0:8080
processes = 4
threads = 2
master = true
chmod-socket = 660
vacuum = true
die-on-term = true
Upto this point everything was working fine locally. Once I added Nginx proxy, I got the below issue
Issue:
urls for _dash-layout and _dash-dependencies are missing the revers proxy uri. For example, I am serving my flask app at www.example.com/app/. But, on the browser, I saw that requests for _dash-layout and _dash-dependencies are coming at www.example.com/dashpp/_dash-layout instead of www.example.com/app/dashpp/_dash-layout.
I read the following forum discussion and tried applying the solution, and got this error,
requests_pathname_prefix needs to start with '/'`
This is my Nginx config,
location /app/ {
proxy_pass http://localhost:<port>;
proxy_redirect http://localhost:<port> http://example.com/app/;
proxy_set_header Accept Encoding "";
sub_filter_types *;
sub_filter 'href="/' 'href="/app/';
sub_filter 'src="/' 'src="/app/';
sub_filter_once off;
}
Anyone has pointers to what is missing. I am new to dash. So, if I missed adding any information please let me know, I will be happy to give additional details
Thanks
PS: I added the same question in dash forum. Posting it here for better reach.
Edit:
To add additional context, I found out that url for _dash-component-suites is generated as expected www.example.com/app/dashpp/_dash-component-suites. I went through the dash source code to understand how urls are generated. Both, _dash-component-suites and _dash-layout are prefixed with routes_pathname_prefix. Line 428 to 448 of dash.py in version 1.14.0 has the code for building urls.
This is confusing!!!.
I was able to fix this by removing sub_filter directive from nginx conf and updating url_prefixes in flask app. The steps I took are posted on this dash forum
I change the original file to add a new routing, but the changes don't work even if I restart the gunicorn server. What is the reason for this? Is it Git, Visual Code, my remote Linux, VirtualanEnv ... or what? I'm deeply confused
It is hard to what the reason is, as there are many "moving" parts (the IDE, Gunicorn, etc); but I cannot reproduce your issue.
Maybe the server was not reloaded or restarted, against your expectation, and you are simply still dealing with Gunicorn running old code?
If you are sure you have restarted your debug server, then it should not matter; otherwise make sure to pass the --reload option, see also: gunicorn autoreload on source change.
As to the application itself, the following MRE works for me:
import falcon
class Contest:
def on_get(self, req, resp, contest_id):
resp.media = {
'contest_id': contest_id,
'uri_template': req.uri_template,
}
def on_get_ping(self, req, resp):
resp.content_type = falcon.MEDIA_TEXT
resp.text = 'PONG\n'
application = falcon.App()
contest = Contest()
application.add_route('/api/v1/ping', contest, suffix='ping')
application.add_route('/api/v1/member/contest/{contest_id:int}', contest)
application.add_route('/api/v1/member/contest/new/{contest_id:int}', contest)
When running with gunicorn --reload --access-logfile - test:application, I can even comment out routes or bring them back, save, and the changes are reflected in the application's behaviour.
Checking the end points in question:
$ curl http://localhost:8000/api/v1/ping
PONG
$ curl http://localhost:8000/api/v1/member/contest/1
{"contest_id": 1, "uri_template": "/api/v1/member/contest/{contest_id:int}"}
$ curl http://localhost:8000/api/v1/member/contest/new/2
{"contest_id": 2, "uri_template": "/api/v1/member/contest/new/{contest_id:int}"}
Okay so I've realized that something weird is happening when I try to upload more than I specified in my config.py which is:
class Config:
#other configurations
MAX_CONTENT_LENGTH = 10 * 1024 * 1024
When I try to upload more than 10 MB, instead of giving me a 413 Error, application just refuses to connect. My error handler:
#errors.app_errorhandler(413)
def error_413(error):
return render_template('errors/413.html'), 413
My run.py:
from flaskblog import create_app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
I can see on terminal that I've gotten this error:
"POST /foo/bar HTTP/1.1" 413 -
Although my app seems to be running on the terminal, I can't access it whatsoever. It's just dead on the browser:
ERR_CONNECTION_REFUSED
I tried running it on uWSGI, Werkzeug, other browsers, no luck.
Any idea what's happening?
EDIT: I can access after I restart my computer. But I'm still curious why about this happens.
Also I use Cloud SQL with external IP for more information.
I had a similar error when using the MAX_CONTENT_LENGTH configuration.Did you register the blueprint errors if yes,Please try removing app_ from #errors.app_errorhandler.Like below:
#errors.errorhandler(413)
def error_413(error):
return render_template('errors/413.html'), 413
If this didn't work try removing the MAX_CONTENT_LENGTH line or if you are trying to connect from another device, run your app with the following command flask run --host=0.0.0.0 and then access it on the other device using your router's ip address which usually looks like 192.168.xxx.xxx and the port which your app is running on like 192.168.xxx.xxx:<port_of_your_app>.If this doesn't work it might be an issue with your firewall refusing incoming connections in the port that your app is running on in which case you can run the following command on your terminal sudo ufw allow <YOUR_PORT>.
Okay next time I should read documentations more carefully:
Connection Reset Issue When using the local development server, you
may get a connection reset error instead of a 413 response. You will
get the correct status response when running the app with a production
WSGI server.
from Flask - File uploads.
I'm sure I am missing something simple here. I can get PART of a python/flask script to be available through nginx, but the important bits are just not working. Here is my python:
#!/usr/bin/python
import flask
from flask import Flask, jsonify, render_template, request
import os
app = flask.Flask(__name__)
app.config['SERVER_NAME']='localhost'
#app.route("/stuff")
def index():
return render_template('index.html')
#app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
def application(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return ["Hello World!"]
Here is my uwsgi start up:
sudo -u www-data uwsgi -s /tmp/myApp.sock --module MyApp
The socket is correctly linked and available to nginx.
And here is my nginx config snippet:
location /test {
uwsgi_pass unix:/tmp/myApp.sock;
include uwsgi_params;
}
When I go to myserver/test I get the "Hello World!" like I Would expect. But when I go to myserver/test/stuff, I ALSO get "Hello World!" rather than the contents of my index.html(Which is valid, I use it elsewhere). And if I enter myserver/test/garbage.html, I get a generic nginx 404, rather than my custom one.
Can anyone point me in a direction?
Thanks
--edit--
Thank you for the answer, it does help, but does not solve my entire issue.
Adding "--callable app" to my uwsgi startup line DOES link the uwsgi server to nginx. Yah! I can confirm this by the fact that my customized 404 file IS being returned by nginx.
But that 404 is ALL I can get. I cannot get to my index.html, which is present in the same directory as the 404.html, has the same owner, and the same rights. It is almost the same file really with slightly different text.
This MAY be a problem with expectations. I am expecting to find my index.html at http://(myserver)/test/stuff But I get a 404.
Am I looking in the wrong place? Or is there something off in my flask, uwsgi, or nginx?
Thanks
You're application function does not call your flask app, which is why every route returns "Hello World", 200. I'm pretty sure you have two easy options.
The first is to drop the application function and replace it with application = app.
The second would be to change the uwsgi line to
sudo -u www-data uwsgi -s /tmp/myApp.sock --module MyApp --callable app
which renders your application function irrelevant anyway.
You can read more about using uwsgi here.
edit
As far as my knowledge about nginx goes, your nginx config should look like this
location = /test { rewrite ^ /test/; }
location /test { try_files $uri #test; }
location #test {
include uwsgi_params;
uwsgi_param SCRIPT_NAME /test;
uwsgi_modifier1 30;
uwsgi_pass unix:/tmp/myApp.sock;
}
It is the same as the recommended one on the linked uwsgi page above. You seem to be running not on the root url, so the basic config will not work.