python - 散景服务器嵌入 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))
我不确定这是否是实现这一目标的最佳方式,所以总是乐于接受有关更好方式的反馈,但至少让某些东西发挥作用是令人兴奋的。
解决方案
推荐阅读
- nginx - Nginx 作为 Prestashop 的反向代理
- r - ggplot(geom_bar)不根据数值对y轴进行排序
- python - 标准差图像分析
- python - y轴标题中的乳胶无法正常工作
- django - 如何在 Django 中使用 post 上传?
- html - 调整页面大小时显示 felx 问题
- database - SpringBatch 应用程序定期从数据库中提取数据
- php - Paypal IPN 未执行许可证创建代码
- r - 在 R 中,使用 if 循环和 agrep 来分配值
- python - 在进程运行时,如何在 Python 中只中断一个 for 循环而不丢失进度?