拆解 Streamlit 的神秘資料流
前言
最近公司在推行用 Streamlit 來建構專案的 prototype,讓資料科學家可以快速建立可互動的 UI。我自己試玩了一下覺得很不錯,只要寫點 Python code 就能快速生出漂亮的 UI 互動元件,自己刻可能都要花上一點時間。
在做 side project 的過程中,開始對它的架構產生興趣:在前端操作完 UI 元件後,後端是怎麼接收這個事件並做出回應的呢?打開 Debugger Tool 翻遍了所有 request 都沒看到疑似交換資料的 API call。
也沒看到 HTML 有被重新請求,那合理懷疑是走 WebSocket 了。後來問了 ChatGPT 才確認是 WebSocket 搭配 Protobuf 的實作。
眼見為憑,自己還是要動手驗證看看。
拿自己的 Streamlit Side Project 來實驗
快速寫一個 Streamlit 元件:
1 | |
用 Streamlit 啟動:
1 | |
可以立即得到一個精美的 Streamlit 網頁,同時打開 Dev Tool 觀測 WebSocket 的建立。
這邊可以看到有一個 HTTP 101 Upgrade 使用 WebSocket 的請求,代表瀏覽器已經和後端的伺服器建立了連線。
接著我們試著輸入一段文字送給後端,並觀察 WebSocket 傳送的訊息,可以得到一個 base64 編碼的二進位檔案。
補充:後來才知道現在瀏覽器已經可以直接查看 WebSocket 的訊息了,以前都不知道。手上版本的 Firefox 沒辦法這樣查看,後來就改成用 Chrome 來實驗。
Decode Protobuf 訊息
去找了目前版本的 Streamlit 原始碼中定義的 schema,自己轉檔成 Python。因為對 Protobuf 不夠熟,試了很久都失敗。
最後直接把 base64 字串貼到線上工具 protobufpal 解出如下圖的 JSON 格式。
1 | |
拿到這串二進位資料後,用 Python 轉回 UTF-8 字串:
就得到當初我輸入進 Streamlit UI 的文字了!
結論
本來是想快速建立一個 Streamlit 專案來體驗看看,順便體驗一下 Cursor 和 Uvicorn 等沒用過的開發工具,順手做了實驗驗證一下 ChatGPT 給的答案。
- Protobuf 的運作機制還不夠熟,也許以後可以自己實作玩一次。
- 了解到 WebSocket 搭配 Protobuf 具有幾個優點:
- 瀏覽器和後端可以雙向溝通,透過 WebSocket 可以即時把資料送到後端。
- 可以從 Dev Tool 看到訊息的大小都是以 B 計算,非常輕量。