天天看點

如何建構高性能可視化架構?一個互動式實時資料引擎的架構設計

在分析 SecDB、Athena、Quartz 幾個實時金融與風險分析平台的時候,發現了 Perspective —— 一個 FinTech 開源基金會 FinOS 旗下開源的互動式分析和可視化元件庫,由摩根大通(J.P. Morgan Chase)公司開源出去的流式資料可視化元件庫。是以,從某種意義上來說也是《金融 Python 即服務:業務自助的資料服務模式》 的後續展開,也可以算是低延遲架構的後續探索。

起初,我隻是對其中使用的 ExprTk 感興趣,後來發現這個庫不簡單:使用了 C++、Rust、Python、JavaScript、TypeScript 等語言。混合語言的項目都特别好玩,于是乎,我便開始探索它了。而我原先感興趣的

x <='abc123'and(y in'AString')or('1x2y3z'!= z)

的解析與實作,也就先放在一邊了。

開始之前,先複制一下官方的介紹:

Perspective 是一個互動式分析和資料可視化元件,特别适合大型資料集或流資料。可以 使用它來建立使用者可配置的報告、儀表闆、Notebook 和應用程式,然後在浏覽器中獨立部署,或與 Python 和/或 Jupyterlab 協同部署。

簡單來說,就是可以提供實時圖形渲染,并支援 Jupyter 內建。如果是 Jupyter 的內建,那麼從某種來說,它是一種金融工作台,類似于先前定義的架構工作台。

PS:寫這樣的工具太過複雜了,是以先寫篇文章記錄一下,等未來有空的時候,再寫一個。

高性能可視化架構:Perspective 架構分析

初步繪制的 Perspective 架構圖如下所示:

如何建構高性能可視化架構?一個互動式實時資料引擎的架構設計

在 JavaScript 側,系統可以分為三層:

  • 資料引擎。使用 C++ 與一系列的資料結構庫等,進行封裝,并提供資料操作 API。通過 Emscripten 建構和封裝,以提供 WASM 接口。
  • wrapper 層。提供對于資料引擎的再次封裝,以使 API 更符合日常的程式設計習慣,諸如于 table、view 等,還提供

    worker

    websocket

    等封裝。
  • UI 元件。viewer 分為 d3fc、datagrid、openlayers 等不同的元件,大部分使用純 Rust 編寫,提供 Web Component API 等。

在 Python 側,除了相同的 UI 部分,還需要建構 Jupyter 插件:

  • 資料引擎。結合 pybind11 來提供 FFI (Foreign Function Interface,外部函數接口)能力。
  • wrapper 層。結合了 Python 資料科學生态中的 Pandas、Numpy 等工具,來進行資料轉換。
  • UI 層。結合 Lumino 對 UI 元件進行封裝。

其中,比較有意思的是 Apache Arrow,提供了跨語言的資料支援。

密集計算下沉:C++ 與 WASM 應對挑戰

對于将密集型計算下沉到 WASM 部分,相信大家都比較熟悉了。對于正常的 WASM 使用來說,需要平衡開發效率和運作效率,FFI 在調用的時候也存在性能損失。也是以,一種比較理想的方式是将資料操作,全部委托給 C++ 部分去實作。

如上面的架構圖所示,Perspective 的計算部分,主要是 Table 對象實作的,它是 Perspective中的基本資料容器。Table 是有類型的 —— 它們有一組不可變的列名,每個都有一個已知的類型。每當有對資料的處理時,都會通過 WASM 來處理。過濾與計算,在這裡也是一個非常有意思的問題,即上面說到的 ExptTk,便是用來做這部分計算用的。

值得注意的是,Perspective之是以采用 C++ 來建構 WebAssembly 的方式,大機率是因為原有的一部分基礎設施是基于 C++ 的。與此同時,原先采用的是 C++ 的 UI,以提供更好的性能。不過,Perspective 提供的 WASM 包,大概有 40M 左右,在初始化的時候相對慢了一點。

可是,又為什麼是 Table 呢?這就得從 Apache Arrow 提供的能力說起。

無序列化與記憶體分析:Apache Arrow

對于序列化的性能優化,相信大家都比較熟悉了。通常來說一次資料傳輸操作包括:

  1. 以某種格式序列化資料
  2. 通過網絡連接配接發送序列化資料
  3. 在接收端反序列化資料

于是乎,在很多系統中(如 ArchGuard),序列化就是系統的瓶頸。既然序列化會帶來問題,那麼就不應該有序列化。于是乎,我們就可以在上面的架構圖中,看到兩個工具:

  • Apache Arrow。一個直接針對資料分析需求的資料層,提供分析所需的資料類型的綜合集合。除了語言無關的标準化列式存儲格式之外,它還包含三個特性:零拷貝共享記憶體和基于 RPC 的資料移動、讀取和寫入檔案格式(如 CSV、Apache ORC 和 Apache Parquet)、記憶體分析和查詢處理。
  • FlatBuffers。同樣的,無需解析/解包即可通路序列化資料。

不過呢,FlatBuffers 隻是 Arrow 用來序列化實作 Arrow 二進制 IPC 協定所需的模式和其他中繼資料。随後,我們就可以使用 Table 來調用 Arrow 的 API 來進行計算。

Apache Arrow 的相關介紹可以見官方文檔:https://arrow.apache.org/

靈活的前端元件:無架構與渲染機制優化

簡單來說,隻要是以下的兩個特點:

  • 無架構。對于一個以渲染為主的項目來說,Perspective 不采用任何架構。從某種意義上來說,更小的包大小,也帶來了更好的性能。除此,作為一個純粹的 web components 元件,它可以非常容易與幾大主流架構結合到一起。
  • 虛拟渲染的 Table。在 Table 顯示上, Perspective 采用的是 JMPC 的 regular-table,同樣也是 Web Components 元件,可以直接引入項目使用。并且支援虛拟渲染,即僅顯示可視區域的資料,減少 DOM 節點以帶來更好的性能。

對于 Web Component 和 Custom Element 部分,相信大家都比較熟悉了。它們使用起來和正常的 HTML 差別不大,如下是一個不同 UI 元件之間的關系示例:

  1. <perspective-workspace>
  2. <perspective-viewer>
  3. <perspective-viewer-datagrid>
  4. <regular-table />
  5. </perspective-viewer-datagrid>
  6. <perspective-viewer-datagrid-toolbar />
  7. </perspective-viewer>
  8. <perspective-viewer ... />
  9. </perspective-workspace>

每一個元件分别在不同的工程中,倒是挺 componentless 的。一旦資料發生變化的時候,就會從 viewer 側,調用

update_and_render

進而更新 UI 部分的 render。

其它

參考材料:

  • 《Apache Arrow 和 Java:大資料傳輸快如閃電》
  • 《Perspective.js》官網

繼續閱讀