Я занимаюсь разработкой простого приложения Bokeh, встроенного в Flask, и хочу развернуть его на Heroku. Я думаю, что я рассмотрел большинство рассуждений, связанных с этой проблемой на Guithub, Stackoverflow и дискурсе боке.
Я следую этому flask_gunicorn_embed.py , чтобы встроить боке в Flask.
каждый раз, когда я запускаю его, я получаю пустую страницу в http://bokehapp.herokuapp.com/app без проблем или ошибок в консоли браузера
Я думаю, что ошибка где-то в одной из следующих 3 строк:
> sockets, port = bind_sockets("localhost", 0)
> script = server_document('https://bokehapp.herokuapp.com:%d/bkapp' % port)
> bokeh_tornado = BokehTornado({'/bkapp': bkapp}, extra_websocket_origins=["bokehapp.herokuapp.com:8000", "*"])
вот мой код app.py:
try:
import asyncio
except ImportError:
raise RuntimeError("This example requries Python3 / asyncio")
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.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from bokeh.themes import Theme
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()
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))
bkapp = Application(FunctionHandler(modify_doc))
# 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("localhost", 0)
@app.route('/', methods=['GET'])
def bkapp_page():
script = server_document('https://bokehapp.herokuapp.com:%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=["bokehapp.herokuapp.com:8000", "*"])
bokeh_http = HTTPServer(bokeh_tornado)
bokeh_http.add_sockets(sockets)
server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
server.start()
server.io_loop.start()
from threading import Thread
Thread(target=bk_worker).start()
вот мой html код (код для вставки html):
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.css"/>
<link href="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.css" rel="stylesheet" type="text/css">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
<meta charset="utf-8">
<title>Embedding a Bokeh Server With {{ framework }}</title>
</head>
<body>
<div>
{{ div | safe }}
{{ script|safe }}
</div>
</body>
</html>
вот логи герою:
2020-01-24T18:45:13.848829+00:00 heroku[web.1]: Starting process with command `bokeh serve --port=XXXXX --allow-websocket-origin=bokehapp.herokuapp.com --address=0.0.0.0 --use-xheaders app.py`
2020-01-24T18:45:18.188153+00:00 app[web.1]: 2020-01-24 18:45:18,187 Starting Bokeh server version 1.4.0 (running on Tornado 4.4.2)
2020-01-24T18:45:18.190044+00:00 app[web.1]: 2020-01-24 18:45:18,189 User authentication hooks NOT provided (default user enabled)
2020-01-24T18:45:18.193171+00:00 app[web.1]: 2020-01-24 18:45:18,193 Bokeh app running at: http://0.0.0.0:25630/app
2020-01-24T18:45:18.193327+00:00 app[web.1]: 2020-01-24 18:45:18,193 Starting Bokeh server with process id: 4
2020-01-24T18:45:18.797915+00:00 heroku[web.1]: State changed from starting to up
2020-01-24T18:45:23.000000+00:00 app[api]: Build succeeded
2020-01-24T18:52:25.994188+00:00 heroku[router]: at=info method=GET path="/" host=bokehapp.herokuapp.com request_id=563bd8a0-XXXX-XXXX-987e-d7289d568b49 fwd="93.178.XX.XXX" dyno=web.1 connect=0ms service=5ms status=302 bytes=163 protocol=https
2020-01-24T18:52:27.369963+00:00 heroku[router]: at=info method=GET path="/app" host=bokehapp.herokuapp.com request_id=56c8d8b1-20a6-4d50-b84d-6a8f9854598b fwd="93.178.XX.XXX" dyno=web.1 connect=0ms service=1028ms status=200 bytes=2681 protocol=https
2020-01-24T18:52:27.355029+00:00 app[web.1]: 2020-01-24 18:52:27,354 User authentication hooks NOT provided (default user enabled)
2020-01-24T18:52:27.372464+00:00 app[web.1]: Exception in thread Thread-1:
2020-01-24T18:52:27.372468+00:00 app[web.1]: Traceback (most recent call last):
2020-01-24T18:52:27.372471+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/threading.py", line 917, in _bootstrap_inner
2020-01-24T18:52:27.372474+00:00 app[web.1]: self.run()
2020-01-24T18:52:27.372477+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/threading.py", line 865, in run
2020-01-24T18:52:27.372479+00:00 app[web.1]: self._target(*self._args, **self._kwargs)
2020-01-24T18:52:27.372481+00:00 app[web.1]: File "/app/app.py", line 381, in bk_worker
2020-01-24T18:52:27.372483+00:00 app[web.1]: server.io_loop.start()
2020-01-24T18:52:27.372485+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/tornado/ioloop.py", line 752, in start
2020-01-24T18:52:27.372487+00:00 app[web.1]: raise RuntimeError("IOLoop is already running")
2020-01-24T18:52:27.372489+00:00 app[web.1]: RuntimeError: IOLoop is already running
2020-01-24T18:52:27.372536+00:00 app[web.1]:
2020-01-24T18:52:31.719924+00:00 app[web.1]: 2020-01-24 18:52:31,719 WebSocket connection opened
2020-01-24T18:52:31.737203+00:00 app[web.1]: 2020-01-24 18:52:31,736 ServerConnection created
вот мои требования file:
gunicorn
pandas
numpy
flask
bokeh
Tornado==4.4.2
вот файл procfile:
web: bokeh serve --port=$PORT --allow-websocket-origin=bokehapp.herokuapp.com --address=0.0.0.0 --use-xheaders app.py
Я очень ценю помощь !!
Спасибо,
Зияд