Question

Python bokeh with flask and gunicorn err_connection_refused

Hi everyone,

I am trying to set up an bokeh application with flask and gunicorn. I am working on a debian system on an AWS server with the IP 35.XXX.XXX.153. I used the example from git and edited it in the way that I think it should work. The problem is, when I run the gunicorn command (gunicorn3 -w 1 --bind 0.0.0.0:8000 wsgi:app) I get an error in the browser terminal:

(index):30 GET http:// 35.XXX.XXX.153:5006/bkapp/autoload.js?bokeh-autoload-element=4755&bokeh-app-path=/bkapp&bokeh-absolute-url=http:// 35.XXX.XXX.153:5006/bkapp net::ERR_CONNECTION_REFUSED

I have no clue why I get a ERR_CONNECTION_REFUSED.

Here is my code: flaskapp.py

import asyncio

from threading import Thread

from flask import Flask, render_template
from flask_cors import CORS
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.client import pull_session
from bokeh.core.validation import silence
from bokeh.core.validation.warnings import FIXED_SIZING_MODE
from bokeh.embed import server_document, autoload_static, server_session
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
from bokeh.server.server import Server
from bokeh.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from bokeh.themes import Theme

import data.DataHead as DataHead

if __name__ == '__main__':
    print('This script is intended to be run with gunicorn. e.g.')
    print()
    print('    gunicorn -w 4 flask_gunicorn_embed:app')
    print()
    print('will start the app on four processes')
    import sys
    sys.exit()


app = Flask(__name__)
CORS(app)

def bkapp(doc):
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data=df)

    plot = figure(x_axis_type='datetime', y_range=(0, 25), y_axis_label='Temperature (Celsius)',
                  title="Sea Surface Temperature at 43.18, -70.43")
    plot.line('time', 'temperature', source=source)

    def callback(attr, old, new):
        if new == 0:
            data = df
        else:
            data = df.rolling(f"{new}D").mean()
        source.data = ColumnDataSource.from_df(data)

    slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
    slider.on_change('value', callback)

    doc.add_root(column(slider, plot))

    doc.theme = Theme(filename="theme.yaml")
        
bkapp = Application(FunctionHandler(bkapp))

sockets, port = bind_sockets("localhost", 5006)

@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://35.XXX.XXX.153:%d/bkapp' % port)
    return render_template("embed.html", script=script, template="Flask")

def bk_worker():
    asyncio.set_event_loop(asyncio.new_event_loop())
    bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["*"])
    bokeh_http = HTTPServer(bokeh_tornado)
    bokeh_http.add_sockets(sockets)

    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
    server.start()
    server.io_loop.start()

t = Thread(target=bk_worker)
t.daemon = True
t.start()

wsgi.py

from flaskapp import app

if __name__ == "__main__":
    app.run()

embed.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    {{ bokeh_css }}
    {{ bokeh_js }}
    <link rel="stylesheet" href="./static/css/styles.css" type="text/css"/>
    <link rel="shortcut icon" href="./static/favicon/favicon.ico" type="image/x-icon">
  </head>
  <body>
    <div class="head">
        <a href="http://"><img src="./static/images/logo.png" width="93px" height="36px"/></a>
     </div>
    <div class="body">
      {{ script|safe }}
    </div>
  </body>
</html>

On the debian server I forwarded the ports 80, 443, 5006-5015, 5432, 8000, 8080.

Maybe someone has an idea why I am getting this error and how I can resolve it. Thank you!


Submit an answer

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Sign In or Sign Up to Answer

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

I got one step further. With the help of bobbys answer and some other changes, it is running now with one worker. Now I would like to change it to 4 workers. So i changed the command to gunicorn3 -w 4 --bind 0.0.0.0:8000 wsgi:app and as well changed the binded port from 5006 to 0, so it takes a random free port. sockets, port = bind_sockets("0.0.0.0", 0) I need to do that, so that all four workers run on different ports. If I open the application now, I get the following error in the broswer console.

:34163/bkapp/autoload.js?bokeh-autoload-element=4755&bokeh-app-path=/bkapp&bokeh-absolute-url=http://35.157.219.135:34163/bkapp:1 Failed to load resource: net::ERR_CONNECTION_TIMED_OUT

Can someone tell me, why I get a time out error? It is working fine with one worker and a set port. Thanks a lot!

I am one step further. I got it running with the help of Bobby and some here and there things.

Now it is running with one worker and a defined port in bind_sockets("0.0.0.0", 5006). If I want to user 4 workers, I can’t set a port, because they all need different ports. So I changed the port to 0 bind_sockets("0.0.0.0", 0), so that it takes a free port. But then I will get an error in the browser console:

:34163/bkapp/autoload.js?bokeh-autoload-element=4755&bokeh-app-path=/bkapp&bokeh-absolute-url=http://35.XXX.XXX.153:34163/bkapp:1 Failed to load resource: net::ERR_CONNECTION_TIMED_OUT

Can someone help me with that error?

Hi there,

Your Flask app is binding on localhost:

bind_sockets("localhost", 5006)

This means that you will only be able to access the service locally and not from any external source.

What you could do is use 0.0.0.0 instead of localhost, that way the service will be exposed to the world and you will be able to access it on port 5006.

Hope that this helps!

Best,

Bobby