3

I have a REST API backend with python/flask and want to stream the response in an event stream. Everything is running inside a docker container with nginx/uwsgi (https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask/).

The API works fine until it comes to the event-stream. It seems like something (probably nginx) is buffering the "yields" because nothing is received by any kind of client until the server finished the calculation and everything is sent together.

I tried to adapt the nginx settings (according to the docker image instructions) with an additional config (nginx_streaming.conf) file saying:

server {
  location / {
        include uwsgi_params;
        uwsgi_request_buffering off;
  }
}

dockerfile:

FROM tiangolo/uwsgi-nginx-flask:python3.6
COPY ./app /app
COPY ./nginx_streaming.conf /etc/nginx/conf.d/nginx_streaming.conf

But I am not really familiar with nginx settings and sure what I am doing here^^ This at least does not work.. any suggestions?

My server side implementation:

from flask import Flask
from flask import stream_with_context, request, Response
from werkzeug.contrib.cache import SimpleCache
cache = SimpleCache()
app = Flask(__name__)

from multiprocessing import Pool, Process
@app.route("/my-app")
def myFunc():
    global cache

    arg = request.args.get(<my-arg>)
    cachekey = str(arg)
    print(cachekey)

    result = cache.get(cachekey)
    if result is not None:
        print('Result from cache')
        return result
    else:
        print('object not in Cache...calculate...')
        def calcResult():
            yield 'worker thread started\n'

            with Pool(processes=cores) as parallel_pool:
                [...]

            yield 'Somewhere in the processing'
            temp_result = doSomethingWith(

            savetocache = cache.set(cachekey, temp_result, timeout=60*60*24) #timeout in seconds

            yield 'saved to cache with key:' + cachekey +'\n'
            print(savetocache, flush=True)
            yield temp_result

        return Response(calcResult(), content_type="text/event-stream")

if __name__ == "__main__":
    # Only for debugging while developing
    app.run(host='0.0.0.0', debug=True, port=80)
SumNeuron
  • 4,850
  • 5
  • 39
  • 107

3 Answers3

2

I ran into the same problem. Try changing

return Response(calcResult(), content_type="text/event-stream")

to

return Response(calcResult(), content_type="text/event-stream", headers={'X-Accel-Buffering': 'no'})
U. Rizwan
  • 21
  • 2
  • More idiomatically in flask -- which is what the OP is using, you add it to request inside the @app.after_quest hook. – cbz Aug 01 '19 at 20:57
0

Following the answer from @u-rizwan here, I added this to the /etc/nginx/conf.d/mysite.conf and it resolved the problem:

add_header  X-Accel-Buffering  no;

I have added it under location /, but it is probably a good idea to put it under the specific location of the event stream (I have a low traffic intranet use case here).

Note: Looks like nginx could be stripping this header by default if it comes from the application: https://serverfault.com/questions/937665/does-nginx-show-x-accel-headers-in-response

frnhr
  • 12,354
  • 9
  • 63
  • 90
0

encountered same issue, how it was not Nginx buffering. When I run the application without docker it works as expected.

Nyambaa
  • 38,988
  • 9
  • 29
  • 35