首页 > 解决方案 > 散景服务器嵌入 Flask 应用程序“使用中的端口”OSError

问题描述

我正在尝试在 Flask 应用程序中嵌入 Bokeh 服务器,但我不断收到错误“OSError: [Errno 98] Address already in use”</p>

现在我知道了一个事实,在我在命令行上输入“flask run”之前,在默认的 Bokeh 服务器端口(5006)上没有运行其他进程,因为我使用 lsof -i 杀死了任何挂起的进程,然后 kill -9 PID。

我怀疑它与烧瓶尝试多次执行 bk_worker 函数有关,每次都具有相同的端口,由于线程或多处理?

当我从命令行使用“bokeh serve plot.py”执行我的绘图 python 脚本时,它运行良好,我可以在 http://localhost:5006/plot 访问绘图。但是,当我尝试从 Flask 应用程序中执行时,我得到了 OSError。

这是我在 init.py 中执行的代码。我尝试过使用 Bokeh 推荐的使用 Thread 的方法,以及直接调用 bk_worker ,因为 Flask 也做了一些多处理工作。无论哪种方式,对于已在使用的端口,我都会收到相同的 OSError。

#__init__.py

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)

def bk_worker():
    server = Server({'/plot': plot}, io_loop=IOLoop(), allow_websocket_origin=["localhost:{}".format(5000)])
    server.start()
    server.io_loop.start()

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

这是我在应用程序的 routes.py 中用于获取散景图的代码,然后我将其传递给 html 渲染。

#routes.py

@app.route('/bokeh_plot', methods=['GET', 'POST'])
def bokeh_plot():
    script = server_document('http://localhost:5006/plot')
    return render_template('bokeh_plot.html', script=script)

我将服务器链接到的绘图本身位于名为 plot.py 的文件中,格式如下。我知道这段代码有效,因为我可以使用 bokeh serve 命令提供它

#plot.py

def plot(doc):
    ...code to make plot...
    return plot

我的第一个想法是我没有在正确的 Flask 文件中运行 bk_worker 函数?或者我不了解我的端口配置方式?我看到的大多数示例都将整个应用程序运行在一个文件中,但我的应用程序有点复杂,所以我在 init.py 中执行了 bk_worker 函数。我还尝试将它放在调用它的“/plot”路由下的 routes.py 中。我的文件布局结构与Flask Mega 教程中的大致相同

环境:我正在使用 Ubuntu 20.04 LTS 在 Linux 的 Windows 子系统中进行开发。Python 版本:3.6.10 Bokeh 版本:2.3.0 Flask 版本:1.1.2

在https://discourse.bokeh.org/t/bokeh-server-embed-in-flask-application-port-in-use-oserror/7724上也提出了问题


更新

按照在 gunicorn 上使用 Flask 运行 Bokeh 的说明,我能够消除此错误。这可以处理多线程情况并创建多个端口,这就是 Flask 现在默认的方式,即使没有 gunicorn。我没有使用gunicorn。

https://github.com/bokeh/bokeh/blob/branch-2.4/examples/howto/server_embed/flask_gunicorn_embed.py

但是,即使这个错误现在消失了,我的情节仍然没有出现在网页上。我通过 os 环境变量将端口从我的 init.py 传递到我的 routes.py ,我不确定这是实现这一点的正确方法。我会继续排除故障,但我会感谢任何人对如何最好地处理这个问题的想法。


更新

事实证明,由于我在浏览器控制台中看到的 websocket 错误,这些图没有显示出来。为了让它工作,我创建了一个 settings.py 文件来保存被暴露的散景端口的全局变量。


#settings.py

def init():
    global port
    port = 0

在我的init .py 中,在使用上面的 Flask Gunicorn 方法实现 Bokeh 之后,我更新了端口处理以将变量拉入并为套接字赋予一个值。我还必须更新我的 websocket origins 行以允许来自该端口变量的流量。


#__init__.py

from app import settings

settings.init()
sockets, settings.port = bind_sockets("localhost", 0)

# New websocket origins line
bokeh_tornado = BokehTornado({'/plot': plot}, extra_websocket_origins=["127.0.0.1:5000", "127.0.0.1:"+str(settings.port)])

然后我可以在我的 routes.py 中引用端口来拉取服务器文档


#routes.py

plot = server_document('http://localhost:%d/plot' % int(settings.port))

我不确定这是否是实现这一目标的最佳方式,所以总是乐于接受有关更好方式的反馈,但至少让某些东西发挥作用是令人兴奋的。

标签: pythonflaskbokeh

解决方案


推荐阅读