Bokeh服务器嵌入Flask应用程序“端口正在使用”操作错误

2024-06-17 12:01:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在Flask应用程序中嵌入一个Bokeh服务器,但不断出现错误“OSError:[Errno 98]地址已在使用”

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

我怀疑这与flask由于线程或多处理而多次尝试执行bk_worker函数有关,每次都使用相同的端口

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

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

#__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中使用的代码,用于获取bokeh图,然后将其传递到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的文件中,格式如下。我知道这段代码是有效的,因为我可以使用bokehserve命令为它提供服务

#plot.py

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

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

环境: 我正在使用Ubuntu20.04 LTS为Linux开发Windows子系统。 Python版本:3.6.10 Bokeh版本:2.3.0 烧瓶版本:1.1.2

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


更新

通过遵循gunicorn上运行Bokeh with Flask的说明,我能够消除这个错误。这将处理多线程情况并创建多个端口,即使没有gunicorn,Flask现在也默认这样做。我没有使用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文件来保存正在公开的bokeh端口的全局变量


#settings.py

def init():
    global port
    port = 0

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


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

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


Tags: 端口py应用程序flasksettingsserverplotinit