散景服务器嵌入 Flask 应用程序“使用中的端口”OSError

时间:2021-04-29 19:02:11

标签: python flask bokeh

我正在尝试在 Flask 应用程序中嵌入 Bokeh 服务器,但我不断收到错误“OSError: [Errno 98] 地址已在使用中”

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

我怀疑这与flask由于线程或多处理而尝试多次执行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 Tutorial

中的大致相同

环境: 我正在使用 Ubuntu 20.04 LTS 在适用于 Linux 的 Windows 子系统中进行开发。 Python 版本:3.6.10 散景版本:2.3.0 烧瓶版本: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))

我不确定这是否是实现此目标的最佳方式,因此始终欢迎对更好方式的反馈,但至少让某些事情起作用是令人兴奋的。

0 个答案:

没有答案