How to Change ip address and port number in bokeh dynamic plot

Hi,
I will really appreciate if i get help on this.
please guide me and let me know how to do below mention changes to get desirable output.
Can’t we change the port number as well as localhost while giving run of embed plot.
I am trying but getting failed.
I don’t want port number 5000 which i think is fix for tornado ,
I tried changing localhost with my system ip address but it didn’t work
Even i want my url to be http://172.16.X.XX:8006/bkapp/page2/

from jinja2 import Environment, FileSystemLoader
from tornado.web import RequestHandler

from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.server.server import Server
from bokeh.themes import Theme

from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

env = Environment(loader=FileSystemLoader('templates'))

class IndexHandler(RequestHandler):
def get(self):
template = env.get_template('embed.html')
script = server_document('[http://172.16.X.XX:8006/bkapp](http://172.16.x.xx:8006/bkapp)')
self.write(template.render(script=script, template="Tornado"))

def modify_doc(doc):
df = sea_surface_temperature.copy()
print(df.head())
source = ColumnDataSource(data=df)
print("source:",source)
print("type(source)",type(source))

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

def callback(attr, old, new):
    if new == 0:
        data = df
    else:
        data = df.rolling('{0}D'.format(new)).mean()
    source.data = ColumnDataSource(data=data).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")
```

(server = Server({'/bkapp': modify_doc}, num_procs=1, extra_patterns=[('/', IndexHandler)])
server.start()

if  **name**  == ' **main** ':
from bokeh.util.browser import view

```
print('Opening Tornado app with embedded Bokeh application on http://172.16.X.XX:8006/')

server.io_loop.add_callback(view, "http://172.16.X.XX:8006/")
```

server.io_loop.start()`)

Running the program by allowing ip and port number.
python dynamic1.py --address=172.16.X.XX --port=8006
Can u please let me know, how i can run by dynamic1.py at particular ip address origin and particular port with particular url address.
Thank you.

@sameerCoder

Your code includes the following statement to instantiate the server

(server = Server({'/bkapp': modify_doc}, num_procs=1, extra_patterns=[('/', IndexHandler)])

But that call doesn’t include the keyword arguments address or port which are used to specify the address and port, respectively, where the server should listen for HTTP requests. See the documentation here https://docs.bokeh.org/en/latest/docs/reference/server/server.html#bokeh.server.server.Server.

Additionally, you state that you’re running your program via the following to pass the address and port.

python dynamic1.py --address=172.16.X.XX --port=8006

I don’t see anywhere that your Python program actually does anything to parse those arguments. If you want to make them something that can be passed and configured from the command line, you’d need some sort of argument processing, e.g. the getopt module (or some of your own custom handler). https://docs.python.org/3/library/getopt.html

1 Like

I had run into issues for setting up server. Here are the flags I used:

bokeh serve python_filename.py --address 100.1.180.2 --port 5050 --allow-websocket-origin=100.1.180.2:5050

You can determine active TCP connections on a Windows server using:
$netstat -aon

What you are doing is using bokeh server,
if we are using bokeh server then ur command will run fine but this issue is not using bokeh server.
Thank you for comment @swamilikes2code

1 Like

@_jm
Hi,
u mean code should be modify like below

server = Server({'/bkapp': modify_doc}, 
num_procs=1,address="172.16.4.XX",port=6000,allow_websocket_origin=["172.16.4.XX:6000"], extra_patterns=[('/', IndexHandler)])
server.start() 
  .....
 class IndexHandler(RequestHandler):
def get(self):
    template = env.get_template('embed.html')
    script = server_document('172.16.4.xx:6000/bkapp')
    self.write(template.render(script=script, template="Tornado"))
    ....
server.io_loop.add_callback(view, "172.16.4.XX:6000/")
server.io_loop.start()

kindly let me know am i doing write as still its not working :frowning:
#problem loading page is coming.

@sameerCoder

The snippet you have provided is conceptually along the lines of what was suggested.

It is not clear with what you’re attempting to accomplish with the "XX" for the least significant byte of the host part of the IPv4 address.

Also, the port 6000 in your example is blocked by many systems, so you’ll need to make sure that’s not an issue in your setup. It is hard to tell with a general description of the problem as

Ultimately, I think the details will come down to how you want to embed bokeh (flask+gevent, flask+gunicorn, tornado, something else?).

To get help, I’d suggest you get a minimal app working in the intended environment / web server embedding and using the localhost as a starting point. Then modify it to have other-than-the-default address and port.

Post this making sure it is complete, working, minimal reproducible code. As you try to modify it to make it accessible to other computers on your private network (172.16.4.###) also include specific Java console errors that you receive in the browser.

For a browser like Chrome, you can get to the console via right-click and choosing the Inspect option. For other browsers, just search for how to get the JS console in a web search.

@_jm
Thanks for such a quick and wonderful comment.
I really appreciate it.
coming to task:-
By using Torando+flask i am able to do the port number change as per my port number , whereas still i am struggling with changing the ip address , kindly let me know in below plot where i am doing wrong.,

from flask import Flask, render_template

from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.server.server import Server
from bokeh.themes import Theme
from tornado.ioloop import IOLoop

from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

app = Flask(__name__)


def modify_doc(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('{0}D'.format(new)).mean()
        source.data = ColumnDataSource(data=data).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")


@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://localhost:5006/bkapp')
    return render_template("embed.html", script=script, template="Flask")


def bk_worker():
    # Can't pass num_procs > 1 in this configuration. If you need to run multiple
    # processes, see e.g. flask_gunicorn_embed.py
    server = Server({'/bkapp': modify_doc}, io_loop=IOLoop(), address='172.16.3.76',
                port=8006, allow_websocket_origin=['172.16.3.76:8006', 'localhost:5006'])
    server.start()
    server.io_loop.start()

from threading import Thread
Thread(target=bk_worker).start()

if __name__ == '__main__':
    print('Opening single process Flask app with embedded Bokeh application on http://localhost:8000/')
    print()
    print('Multiple connections may block the Bokeh app in this configuration!')
    print('See "flask_gunicorn_embed.py" for one way to run multi-process')
    app.run(port=8006)

How i can give access of my plot on this ip address 172.16.3.76.
javascript console u can find as screenshot


I will really appreciate your help on this.
Thanks alot.

@sameerCoder

In the screen capture of your browser, you have entered 127.0.0.1 in the address bar for the URL you’re trying to visit. Although there are low-level technical differences between using localhost and 127.0.0.1, these are, for all intents and purposes generally referencing to the same loopback address on your system.

As a starting point, you’ll want to actually put in the address where you think the server should be, 172.16.3.76:8006 in your example.

I don’t typically work with the flask development server, but prefer the Flask + Green Unicorn (gunicorn) model for something that is a little bit closer to a deployable solution. With that caveat, there might be a few other things that don’t work in your example by just navigating to a different address.

It is good that you’re using the Bokeh provided server embedded examples as a reference. That makes helping a bit easier. Here’s an example where I’ve modified three lines of their gunicorn embed file for the same sea-surface temperature smoothing example. And also added an import / configuration file to make changing the server address and port less hardcoded.

You can either use my file as a starting point for your use case or do a diff with the example in the bokeh github here https://github.com/bokeh/bokeh/blob/master/examples/howto/server_embed/flask_gunicorn_embed.pyto see if that gives pointers on how to get your example to work.

To use my example with the configuration file (attached to this thread). Edit the bind variable to reflect the IP address and/or port you want for the server. To access the page, that’s also what you’ll need to put in the clients web browser.

gunicorn --config gunicorn_config.py flask_gunicorn_embed:app

FILES

gunicorn_config.py

import multiprocessing
import urllib.parse

bind = "192.168.0.136:8008"
workers = multiprocessing.cpu_count() * 2 + 1

pr = urllib.parse.urlparse('//'+bind)
APP_SERVER_ADDR = pr.hostname
APP_SERVER_PORT = pr.port

flask_gunicorn_embed.py

try:
    import asyncio
except ImportError:
    raise RuntimeError("This example requries Python3 / asyncio")

from threading import Thread

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

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.embed import server_document
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 BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from bokeh.themes import Theme

from gunicorn_config import APP_SERVER_ADDR, APP_SERVER_PORT


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__)

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('{0}D'.format(new)).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")

# can't use shortcuts here, since we are passing to low level BokehTornado
bkapp = Application(FunctionHandler(bkapp))

# This is so that if this app is run using something like "gunicorn -w 4" then
# each process will listen on its own port
sockets, port = bind_sockets(APP_SERVER_ADDR, 0)

@app.route('/', methods=['GET'])
def bkapp_page():
    script = server_document('http://%s:%d/bkapp' % (APP_SERVER_ADDR,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=["localhost:8000", "%s:%d" % (APP_SERVER_ADDR,APP_SERVER_PORT)])
    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()

@_jm
Thanks for such quick reply,
Excellent example you have provide.let me try this example and let you know the output.
once again thank you.

@_jm
Hi,
I have tried ur example ,
i have done pip install guincorn
i replace the bind to my system ip address.
from terminal given command of gunicorn --config gunicorn_config.py flask_gunicorn_embed:app

In myterminal after giving above command i am getting below message
[2020-06-26 18:51:20 +0530] [27984] [INFO] Worker exiting (pid: 27984)
[2020-06-26 18:51:20 +0530] [27981] [INFO] Worker exiting (pid: 27981)
[2020-06-26 18:51:20 +0530] [27977] [INFO] Worker exiting (pid: 27977)
[2020-06-26 18:51:20 +0530] [27979] [INFO] Worker exiting (pid: 27979)
[2020-06-26 18:51:23 +0530] [28015] [INFO] Booting worker with pid: 28015
[2020-06-26 18:51:23 +0530] [28016] [INFO] Booting worker with pid: 28016
[2020-06-26 18:51:23 +0530] [28017] [INFO] Booting worker with pid: 28017
[2020-06-26 18:51:23 +0530] [28018] [INFO] Booting worker with pid: 28018
[2020-06-26 18:51:23 +0530] [28023] [INFO] Booting worker with pid: 28023
[2020-06-26 18:51:23 +0530] [28024] [INFO] Booting worker with pid: 28024
[2020-06-26 18:51:23 +0530] [28025] [INFO] Booting worker with pid: 28025
[2020-06-26 18:51:23 +0530] [28026] [INFO] Booting worker with pid: 28026
[2020-06-26 18:51:23 +0530] [28027] [INFO] Booting worker with pid: 28027
[2020-06-26 18:51:23 +0530] [28028] [INFO] Booting worker with pid: 28028
[2020-06-26 18:51:23 +0530] [28029] [INFO] Booting worker with pid: 28029
[2020-06-26 18:51:23 +0530] [28030] [INFO] Booting worker with pid: 28030
and in my run output box i am getting message as screenshot attached here.
capt1
web browser.

gunicorn --config gunicorn_config.py flask_gunicorn_embed:app in my web browser not working.
Can you let me know how to see the plot in web browser.
Thank you.

@sameerCoder

Not sure what is meant by output box message. Perhaps you pasted the gunicorn command in your web browser too? If so, sorry for the confusion introduced by the following statement.

What I meant is you need to put the address and port that corresponds to the IP address and port in the config file in your browser. E.g. 172.16.3.76:8006 in your earlier example. I was trying to explicitly account for the case where the localhost 127.0.0.1 was being used but you had changed where the app was actually being served.

Just think of the address and port in the webbrowser as any normal destination that a client wants to go to. Its got nothing to do with Bokeh or gunicorn from the client’s perspective.

Hi,
Yup man , Now its working fine. plot is coming with my ipaddress and port number as url in browser.
Thanks man,
Now i have to replace this sea temperature plot with my original plot.
**its working but i dont know how this gunicorn work and how this plot is coming **
is there any good resource so that i can understand the knowledge on this things very deep.
one more thing if i want my url to be 172.16.3.76/bkapp:8008, how this can be done.
urlmapping, as like secondwebpage , thirdwebpage in url.
Thank you.
I really appreciate your help.

@sameerCoder

The Green Unicorn (gunicorn) is just a Python HTTP server. You can read its documentation here. https://docs.gunicorn.org/en/stable/.

That said, it is just a matter of personal preference. The other solutions based on the Flask development server or tornado could work, I just am less familiar with those and their APIs. I personally prefer the gunicorn embedding because I like the multiple worker processes to leverage the several CPU cores on the the computer and the fact that I can easily register it as a service to run automatically and restart, if required, automatically via Linux systemd.

To reiterate though, the flask dev server, gevent (another option), tornado, etc. are just web servers that are embedding the bokeh application. The choice really comes down to requirements for the deployment and preferences.

Regarding the page URI 172.16.3.76/bkapp:8008, that’s non-standard and there’s not any way (at least that I know) to make it work.

The 172.16.3.76 is the IP address of the server and the 8008 is the port. The required syntax is that these are paired together separated by a colon. The IPv4 address tells the client where it wants to go and the port lets the receiving computer at that address know which application should get the data coming into the computer. A port number 80 is the default HTTP port, 443 is the default HTTPS port, and the example here is saying I’ve got another application (a custom web server) running at port 8008 that should get all traffic assigned to that port.

The /bkapp is the web page route on that server. It’s possible to have different pages, sure, like 172.16.3.76/register, 172.16.3.76/login, etc.

@_jm Thanks for such a Excellent Knowledge share.
You are really a nice person.

I will look into gunicorn document to explore more.
since gunicorn is HTTP server, we can’t have our url as https:IP_address:8008.
Thank you.

@_jm
can you open the below link

may be u can give some suggestion on above issue on Flask+guincorn app.
Thank you.

As we know that Gunicorn in for unix only
but there is an alternate of Gunicorn for windows system - Waitress
pip install waitress
for more info check below link
https://www.docketrun.com/blog/waitress-alternative-flask-gunicorn/#:~:text=Waitress%20is%20an%20Alternative%20of%20Flask%20and%20Gunicorn%20for%20windows&text=It%20has%20no%20dependencies%20except,to%20run%20on%20PyPy%201.6.