gpt4 book ai didi

python - 防止无限回调循环部署带有 Gunicorn 的 Dash 应用程序,其中存在 `dcc.Interval` 和 `dcc.Location`(即多页应用程序)组件

转载 作者:行者123 更新时间:2023-12-04 12:52:58 27 4
gpt4 key购买 nike

我正在使用 Dash 2.0,我有 interval.pyapps文件夹:

import numpy as np
import dash
import pandas as pd
import plotly.express as px
from dash.dependencies import Output, Input
from dash import dcc
from dash import html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets) # call flask server


app.layout = html.Div(className='row', children=[
html.Div(children=[dcc.Graph(id='a-graph', style={'display': 'flex'}),
dcc.Graph(id='b-graph', style={'display': 'flex'}),
dcc.Interval(id='interval-component', interval=1000)])])


@app.callback(
[
Output('a-graph', 'figure'),
Output('b-graph', 'figure')
],
[
Input('interval-component', 'n_intervals')
]
)
def update_graphs(n):
print(f'callback happened: {n}')
df = pd.DataFrame({"time":pd.Series(pd.date_range("1-nov-2021","2-nov-2021", freq="S")).sample(30), "bacteria_count":np.random.randint(0,500, 30), "bacteria_type":np.random.choice(list("AB"),30)})

df["epoch_time_ms"] = df["time"].astype(int) / 1000
df = df.sort_values("time")
a_fig = px.line(df, x="time", y="bacteria_count", line_shape="hv", markers=True, color='bacteria_type')
a_fig.update_traces(mode="markers+lines", hovertemplate=None)
a_fig.update_layout(hovermode='x unified')

b_fig = px.line(df, x="epoch_time_ms", y="bacteria_count", line_shape="hv", markers=True, color='bacteria_type')
b_fig.update_traces(mode="markers+lines", hovertemplate=None)
b_fig.update_layout(hovermode='x unified')
return a_fig, b_fig


if __name__ == '__main__':
app.run_server(debug=True)
update_graphs()如果我运行 python3 interval.py,就会按预期定期调用.
但是,如果我添加这样的索引页( index.py ):
from dash import dcc
from dash import html
from dash.dependencies import Input, Output

from app import app
from apps import interval

app.layout = html.Div([
# represents the URL bar, doesn't render anything
dcc.Location(id='url', refresh=False),

dcc.Link('Top | ', href='/'),
dcc.Link('Interval | ', href='/apps/interval'),

# content will be rendered in this element
html.Div(id='page-content')
])

server = app.server


@app.callback(Output('page-content', 'children'),
Input('url', 'pathname'))
def display_page(pathname):
if pathname == '/apps/interval':
return interval.app.layout
else:
return ''


if __name__ == '__main__':
app.run_server(debug=True)
app.py :
import dash

app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server
然后我开始使用 gunicorn index:server -b :8086 .即使我加载了间隔页面,它也开始没有任何错误。但是,回调永远不会被调用。
然后,我尝试替换 app = dash.Dash(__name__, external_stylesheets=...)interval.py与从应用程序导入应用程序。一旦我点击 /apps/interval索引页上的链接,进入无限循环 -- 继续打印 Top | Interval在我的浏览器上。
任何的想法?
谢谢!

最佳答案

使用 gunicorn 部署 Dash 应用程序与破折号 dcc.Interval † 以及 dcc.Location ⁂(即多页应用程序)功能存在*

* Refs to Dash Docs for mentioned components:


Interval dcc (dash core components) components allow for auto source data updating (and thus auto-updates for certain components on a page) at specified intervals.

Location dcc components allow for access to the current href/pathname/URL and thus enabling of multi-page Dash apps.


[最小] 代码设置
所以我让这个工作,根据你提供的东西 - 尽管你肯定会有更多的工作要做,我离开你的地方(希望这是有道理的;有多种方法可以做到这一点)。顺便说一句,我设置了 max_intervals=5dcc.Interval组件,否则它只会无休止地更新图形(也许这就是您想要的行为,但是,设置此限制只会有助于简化此演示)。
这是我得到的文件结构,然后是每个文件的确切内容。
.
|-- app
| |-- __init__.py
| |-- gunicorn
| | `-- logs
| | `-- 20211101
| | `-- 20211101.access.log
| `-- interval.py
|-- index.py
`-- launch_gunicorn.sh

4 directories, 5 files

坚持使用您最关注的方法,即本质上将布局、回调和应用程序都声明在一个文件中——“ interval.py”。然后,只有一个额外的 Python 文件“ index.py”(再次与您的帖子保持一致)控制应用程序的多页面布局处理,也是 gunicorn 来自的文件。将获得对 Dash 应用程序对象(特别是应用程序的服务器子属性;即,在这种情况下,即 app.interval.app.server)的所需引用。最后,我创建了另外三分之一的额外文件,它只是一个 bash launch_gunicorn.sh有助于执行应用程序的非开发/调试(即生产 [虽然可能也想添加 nginx 但这是另一个问题])部署的 shell 脚本。
1) app/interval.py
import dash
import numpy as np
import pandas as pd
import plotly.express as px

from dash import dcc
from dash import html
from dash.dependencies import Input
from dash.dependencies import Output

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

interval_layout = html.Div(
className="row",
children=[
html.Div(
children=[
html.Div(id="interval-update"),
dcc.Graph(id="a-graph", style={"display": "flex"}),
dcc.Graph(id="b-graph", style={"display": "flex"}),
dcc.Interval(
id="interval-component", interval=1000, max_intervals=5
),
]
)
],
)


@app.callback(
[
Output("a-graph", "figure"),
Output("b-graph", "figure"),
Output("interval-update", "children"),
],
[Input("interval-component", "n_intervals")],
)
def update_graphs(n):
app.logger.info(f"callback happened: {n}")
df = pd.DataFrame(
{
"time": pd.Series(
pd.date_range("1-nov-2021", "2-nov-2021", freq="S")
).sample(30),
"bacteria_count": np.random.randint(0, 500, 30),
"bacteria_type": np.random.choice(list("AB"), 30),
}
)

df["epoch_time_ms"] = df["time"].astype(int) / 1000
df = df.sort_values("time")
a_fig = px.line(
df,
x="time",
y="bacteria_count",
line_shape="hv",
markers=True,
color="bacteria_type",
)
a_fig.update_traces(mode="markers+lines", hovertemplate=None)
a_fig.update_layout(hovermode="x unified")

b_fig = px.line(
df,
x="epoch_time_ms",
y="bacteria_count",
line_shape="hv",
markers=True,
color="bacteria_type",
)
b_fig.update_traces(mode="markers+lines", hovertemplate=None)
b_fig.update_layout(hovermode="x unified")
interval_update = [html.H1(f"Interval Update - {n}")]
return a_fig, b_fig, interval_update


if __name__ == "__main__":
app.run_server(debug=True)


2) index.py
""" ## Primary Application Deployment Script
------------------------------------

> 𝕯𝖊𝖕𝖑𝖔𝖞𝖒𝖊𝖓𝖙
_____

ᵂʳⁱᵗᵗᵉⁿ ᵇʸ
[your name]


Purpose / Overview:
------------------
This script serves as the de facto WSGI Production-level deployment module (in this case, fed to Gunicorn).

For Development-level deployment (DEBUG),
simply run this script, like so:

$ python index.py

: : : ::::::: : : :

"""
import logging

from dash import dcc
from dash import html
from dash import no_update

from dash.dependencies import Input
from dash.dependencies import Output
from dash.dependencies import State

from dash.exceptions import PreventUpdate

from app import interval
from app.interval import app

app.layout = html.Div(
[
# represents the URL bar, doesn't render anything
dcc.Location(id="url", refresh=False),
dcc.Link("Top | ", href="/"),
dcc.Link("Interval | ", href="/apps/interval"),
# content will be rendered in this element
html.Div(id="page-content"),
]
)


@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
if pathname == "/apps/interval":
return interval.interval_layout
elif pathname == "/":
return no_update
else:
return html.Div(
[html.H1("404 Page Not Found")],
style={"margin": "10%", "textAlign": "center"},
)


if __name__ == "__main__":
app.run_server(debug=True)


############################################
# # DEPLOY* MODE: PRODUCTION # #
# # [*WSGI import (e.g. Gunicorn+Nginx)] # #
############################################
if __name__ != "__main__":
logger = logging.getLogger(__name__)
logging.getLogger("matplotlib.font_manager").disabled = True
logging.basicConfig(
format="%(asctime)s %(name)-12s %(module)s.%(funcName)s %(processName)s %(levelname)-8s %(relativeCreated)d %(message)s",
level=logging.INFO,
)
gunicorn_logger = logging.getLogger("gunicorn.error")
app.logger.handlers = logger.handlers
app.logger.setLevel(logger.level)
app.logger.info(
"Initializing dash-webapp-template (App) `app.server` for handoff..."
)
server = app.server


3) launch_gunicorn.sh
#!/bin/bash

# Gunicorn Launch script for Flask-based Dash app,
# ran concurrently, & in parallel.

TODAY=$(date "+%Y%m%d")
TIMESTAMP=$(date | tr -d "[[:punct:]]" | tr -d ' ')

PORT=${1:-9001}
NUM_WORKERS=${2:-4}
THREADS=${3:-8}

GUNICORN_PROD_LOGS="./app/gunicorn/logs/${TODAY}"
mkdir -p $GUNICORN_PROD_LOGS

gunicorn \
-b 0.0.0.0:$PORT \
-w $NUM_WORKERS \
--worker-class gthread \
--threads $THREADS \
--name "dash-webapp-demo${TODAY}" \
--max-requests 100 \
--max-requests-jitter 10 \
--access-logfile "${GUNICORN_PROD_LOGS}/${TODAY}.access.log" \
index:server

通过 Gunicorn 部署应用程序
启动后 launch_gunicorn.sh ,您应该会在终端中看到以下输出:
[...]$ ./launch_gunicorn.sh 
[2021-11-01 22:51:28 -0700] [23576] [INFO] Starting gunicorn 20.1.0
[2021-11-01 22:51:28 -0700] [23576] [INFO] Listening at: http://0.0.0.0:9001 (23576)
[2021-11-01 22:51:28 -0700] [23576] [INFO] Using worker: gthread
[2021-11-01 22:51:28 -0700] [23580] [INFO] Booting worker with pid: 23580
[2021-11-01 22:51:28 -0700] [23581] [INFO] Booting worker with pid: 23581
[2021-11-01 22:51:28 -0700] [23582] [INFO] Booting worker with pid: 23582
[2021-11-01 22:51:28 -0700] [23583] [INFO] Booting worker with pid: 23583
2021-11-01 22:51:30,464 app.interval index.<module> MainProcess INFO 2686 Initializing dash-webapp-template (App) `app.server` for handoff...
2021-11-01 22:51:30,489 app.interval index.<module> MainProcess INFO 2712 Initializing dash-webapp-template (App) `app.server` for handoff...
2021-11-01 22:51:30,560 app.interval index.<module> MainProcess INFO 2783 Initializing dash-webapp-template (App) `app.server` for handoff...
2021-11-01 22:51:30,621 app.interval index.<module> MainProcess INFO 2844 Initializing dash-webapp-template (App) `app.server` for handoff...
这是因为我已将 worker 数量设置为默认值 4。您可以在 launch_gunicorn.sh 中更改此设置。文件,或者,作为参数传递。如您所见,该脚本接受三个可选参数:分别是端口、工作线程数和线程数。
然后我们会看到以下非常基本的主应用程序页面:
main app page
点击“间隔”链接后:
interval 5
这是通过 Dash 进行的第五次也是最后一次自动“间隔”页面更新 dcc.Interval主组件 interval.py应用程序布局 + 回调(您采用了多合一方法 - 效果很好!)文件。
在主动部署期间监控应用程序
我们可以在终端中看到记录的以下更新:
2021-11-01 23:11:26,140 app.interval interval.update_graphs MainProcess INFO     175805 callback happened: None
2021-11-01 23:11:26,190 numexpr.utils utils._init_num_threads MainProcess INFO 175854 NumExpr defaulting to 4 threads.
2021-11-01 23:11:27,270 app.interval interval.update_graphs MainProcess INFO 176935 callback happened: 1
2021-11-01 23:11:28,131 app.interval interval.update_graphs MainProcess INFO 177796 callback happened: 2
2021-11-01 23:11:29,129 app.interval interval.update_graphs MainProcess INFO 178793 callback happened: 3
2021-11-01 23:11:30,136 app.interval interval.update_graphs MainProcess INFO 179801 callback happened: 4
2021-11-01 23:11:31,133 app.interval interval.update_graphs MainProcess INFO 180797 callback happened: 5

而在 app/gunicorn/logs/[today's date]/[today's date].access.log文件自动创建,你会发现信息,如:
127.0.0.1 - - [01/Nov/2021:23:08:43 -0700] "GET / HTTP/1.1" 200 1781 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:43 -0700] "GET /_dash-layout HTTP/1.1" 200 462 "http://0.0.0.0:9001/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:43 -0700] "GET /_dash-dependencies HTTP/1.1" 200 357 "http://0.0.0.0:9001/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:44 -0700] "POST /_dash-update-component HTTP/1.1" 204 0 "http://0.0.0.0:9001/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:53 -0700] "GET /_dash-component-suites/dash/dcc/dash_core_components-shared.js.map HTTP/1.1" 200 26038 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:53 -0700] "GET /_dash-component-suites/dash/html/dash_html_components.min.js.map HTTP/1.1" 200 687871 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:53 -0700] "GET /_dash-component-suites/dash/dash_table/bundle.js.map HTTP/1.1" 200 154018 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:08:53 -0700] "GET /_dash-component-suites/dash/dcc/dash_core_components.js.map HTTP/1.1" 200 1996412 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:26 -0700] "POST /_dash-update-component HTTP/1.1" 200 651 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:26 -0700] "GET /_dash-component-suites/dash/dcc/async-graph.js HTTP/1.1" 304 0 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:26 -0700] "GET /_dash-component-suites/dash/dcc/async-graph.js.map HTTP/1.1" 200 75008 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:26 -0700] "GET /_dash-component-suites/dash/dcc/async-plotlyjs.js HTTP/1.1" 200 3594037 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:27 -0700] "POST /_dash-update-component HTTP/1.1" 200 16924 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:27 -0700] "POST /_dash-update-component HTTP/1.1" 200 16915 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:28 -0700] "POST /_dash-update-component HTTP/1.1" 200 16909 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:29 -0700] "POST /_dash-update-component HTTP/1.1" 200 16911 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:30 -0700] "POST /_dash-update-component HTTP/1.1" 200 16903 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
127.0.0.1 - - [01/Nov/2021:23:11:31 -0700] "POST /_dash-update-component HTTP/1.1" 200 16915 "http://0.0.0.0:9001/apps/interval" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
显示服务器收到的请求。
最后,运行例如 htop并过滤(在这种情况下,我只是过滤了“破折号”,因为在 gunicorn 命令选项中的 launch_gunicorn.sh 文件中,有配置可以为您的 gunicorn 工作进程提供特定名称;见下文)我们可以看到实际的枪械 worker :
gunicorn workers
Lmk 如果有任何问题/问题🙂

关于python - 防止无限回调循环部署带有 Gunicorn 的 Dash 应用程序,其中存在 `dcc.Interval` 和 `dcc.Location`(即多页应用程序)组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69785448/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com