Dav*_* Co 6 python python-3.x plotly-dash
我是 Dash 的高手,我正在尝试从 websocket feed 更新 DashTable。当没有太多 feed 时,代码似乎可以工作,但是一旦有,Chrome 就会开始向我的服务器发送获取请求(来自 dash_update_component)的垃圾邮件
有什么办法可以使其性能更高吗?
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import json
import pandas as pd
from dash import callback, Dash, dash_table
from dash.dependencies import Input, Output, State
from dash_extensions import WebSocket
symbols = ["BTCUSDT"]
columns = ["symbol", "bid_volume", "bid_price", "ask_volume", "ask_price"]
def create_data():
data = {}
for col in columns:
if col == "symbol":
data[col] = symbols
else:
data[col] = [None] * len(symbols)
return data
df = pd.DataFrame(data=create_data())
# Create example app.
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
dash_table.DataTable(df.to_dict('records'), [{"name": i, "id": i} for i in df.columns], id='tbl', editable=True),
dcc.Input(id="input", autoComplete="off"), html.Div(id="message"),
WebSocket(url="wss://fstream.binance.com/ws/", id="ws")
])
# Write to websocket.
@app.callback(Output("ws", "send"), [Input("input", "value")])
def send(value):
sub_msg = {
"method": "SUBSCRIBE",
"params": [],
"id": 1
}
for ins in symbols:
sub_msg["params"] += ([f"{ins.lower()}@bookTicker"])
return json.dumps(sub_msg, indent=0)
# Read from websocket.
@app.callback(Output('tbl', 'data'), [Input("ws", "message")])
def on_feed(message):
if "data" not in message:
return dash.no_update
else:
data = json.loads(message["data"])
print(data)
symbol = data["s"]
row_idx = df.index[df['symbol'] == symbol].tolist()[0]
df.loc[row_idx, columns] = [symbol, data["B"], data["b"], data["a"], data["A"]]
return df.to_dict('records')
if __name__ == '__main__':
app.run_server(debug=True)
Run Code Online (Sandbox Code Playgroud)
为了提高性能,您可以做的一件事是将回调转换为客户端回调:
symbols = ["ETHBUSD", "BNBUSDT", "BTCUSDT"]
# ...
app.clientside_callback(
"""
function(value) {
const symbols = ['ETHBUSD', 'BTCUSDT', 'BNBUSDT']
const subMsg = {
'method': 'SUBSCRIBE',
'params': [],
'id': 1
};
for (const ins of symbols) {
subMsg.params.push(`${ins.toLowerCase()}@bookTicker`);
}
return JSON.stringify(subMsg);
}
""",
Output("ws", "send"),
Input("input", "value"),
prevent_initial_call=True
)
app.clientside_callback(
"""
function(message, tableData) {
if (message === undefined) {
return window.dash_clientside.no_update;
}
data = JSON.parse(message['data']);
if (data === undefined) {
return window.dash_clientside.no_update;
}
const newTableData = tableData;
row = newTableData.find(row => row.symbol === data['s']);
if (row !== undefined) {
row['bid_volume'] = data['B'];
row['bid_price'] = data['b'];
row['ask_volume'] = data['a'];
row['ask_price'] = data['A'];
}
return tableData;
}
""",
Output("tbl", "data"),
Input("ws", "message"),
State("tbl", "data"),
prevent_initial_call=True
)
Run Code Online (Sandbox Code Playgroud)
客户端回调在客户端中使用 JavaScript 执行代码,而不是在服务器上使用 Python 执行。
https://dash.plotly.com/performance
如果您查看网络选项卡,您可以看到现在不再发出与 websocket 相关的请求。
在这种情况下,我们仍然会为传入的每条有效消息更新表。您可能还想通过使用来限制这一点dcc.Interval:
dcc.Interval(id="interval", interval=500) # interval fires every 500 ms
# ...
app.clientside_callback(
"""
function(nIntervals, message, tableData) {
if (message === undefined) {
return window.dash_clientside.no_update;
}
data = JSON.parse(message['data']);
if (data === undefined) {
return window.dash_clientside.no_update;
}
const newTableData = tableData;
row = newTableData.find(row => row.symbol === data['s']);
if (row !== undefined) {
row['bid_volume'] = data['B'];
row['bid_price'] = data['b'];
row['ask_volume'] = data['a'];
row['ask_price'] = data['A'];
}
return tableData;
}
""",
Output("tbl", "data"),
Input("interval", "n_intervals"),
State("ws", "message"),
State("tbl", "data"),
prevent_initial_call=True
)
Run Code Online (Sandbox Code Playgroud)
您可以尝试调整间隔。较低的间隔使结果更加“实时”/准确,但较高的间隔意味着需要执行的更新较少。
| 归档时间: |
|
| 查看次数: |
1643 次 |
| 最近记录: |