Python Dash Plotly Websockets

jaql4c8m  于 2023-05-08  发布在  Python
关注(0)|答案(1)|浏览(249)

我是Dash的n00b,我正在尝试从WebSocket提要更新DashTable。当feed不太多的时候,代码似乎可以工作,但是一旦有了feed,Chrome就会开始向我的服务器发送垃圾邮件,发送fetch请求(来自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)

ghhkc1vu

ghhkc1vu1#

要提高性能,你可以做的一件事是将回调转换为clientside callbacks

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                                                                 
)

客户端回调在客户端以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
)

您可以尝试调整间隔。较低的间隔使结果更“真实的”/准确,但较高的间隔意味着需要执行的更新较少。

相关问题