Gunicorn部署含dcc.Interval与dcc.Location的Dash多页应用回调死循环问题
问题原因
回调不触发的原因
你最初的代码中,interval.py里的update_graphs回调是注册在interval.py独立实例化的Dash对象上的,而多页应用入口index.py使用的是app.py中定义的全局Dash实例,两个实例完全独立,全局实例并未注册interval.py中的回调,因此回调无法被触发。
无限循环的原因
你将interval.py中的独立Dash实例替换为导入全局app后,interval.app就指向了全局的Dash对象,而全局Dash对象的layout是你在index.py中定义的包含导航、dcc.Location、page-content容器的顶层布局。此时你在display_page回调中返回interval.app.layout,等于把整个顶层布局又嵌套进了page-content容器里,页面加载后会重复触发路径检测、嵌套布局,形成无限渲染循环。
解决方案
步骤1:修改interval.py
删除interval.py中独立实例化Dash对象的代码,导入全局app,将子页面布局定义为独立的layout变量,回调直接注册到全局app上即可,修改后代码如下:
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 # 导入全局app from app import app external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] # 子页面布局定义为独立变量,不要赋值给app.layout 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上 @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__': local_app = dash.Dash(__name__, external_stylesheets=external_stylesheets) local_app.layout = layout for callback in app.callback_map.values(): local_app.callback(**callback['outputs'])(callback['callback']) local_app.run_server(debug=True)
步骤2:修改index.py中的页面返回逻辑
将display_page回调中返回interval.app.layout的代码,改为返回interval.layout即可,修改后对应部分代码如下:
@app.callback(Output('page-content', 'children'), Input('url', 'pathname')) def display_page(pathname): if pathname == '/apps/interval': # 直接返回子页面的独立layout变量,不要调用全局app的layout return interval.layout else: return ''
修改完成后重启服务,即可正常触发回调,也不会出现无限循环问题。
内容的提问来源于stack exchange,提问作者HCSF
相关产品推荐
相关产品推荐

