Apache Arrow Flight 簡介:快速資料傳輸的框架


已發布 2019 年 10 月 13 日
作者 Wes McKinney (wesm)
翻譯 日本語

在過去 18 個月裡,Apache Arrow 社群一直忙於設計和實作 Flight,這是一個新的通用型客戶端-伺服器框架,旨在簡化透過網路介面進行大型資料集的高效能傳輸。

Flight 最初專注於透過 gRPC(Google 流行的基於 HTTP/2 的通用 RPC 函式庫和框架)優化 Arrow 欄狀格式(即「Arrow 記錄批次」)的傳輸。雖然我們專注於與 gRPC 的整合,但作為一個開發框架,Flight 並不打算僅限於 gRPC。

Flight 與其他資料傳輸框架最大的區別功能之一是平行傳輸,允許資料同時串流傳輸到伺服器叢集或從伺服器叢集串流傳輸出來。這使開發人員能夠更輕鬆地建立可擴展的資料服務,以服務不斷增長的客戶群。

在 0.15.0 Apache Arrow 版本中,我們在 C++(帶有 Python 綁定)和 Java 中提供了可隨時使用的 Flight 實作。這些函式庫適用於願意接受 API 或協定變更的 Beta 使用者,因為我們將繼續完善 Flight 內部的一些底層細節。

動機

許多人都經歷過透過網路存取大型資料集所帶來的痛苦。有許多不同的傳輸協定和工具可用於從遠端資料服務讀取資料集,例如 ODBC 和 JDBC。在過去 10 年中,基於檔案的資料倉儲(採用 CSV、Avro 和 Parquet 等格式)已變得流行,但這也帶來了挑戰,因為原始資料必須先傳輸到本機主機才能進行反序列化。

自 Apache Arrow 專案開始以來,我們所做的工作為加速多種方式的資料傳輸帶來了令人興奮的前景。Arrow 欄狀格式具有可以幫助我們的關鍵功能

  • 它是一種表格資料的「線上」表示形式,接收時無需反序列化
  • 其自然模式是「串流批次」,較大的資料集一次傳輸一批列(在 Arrow 術語中稱為「記錄批次」)。在這篇文章中,我們將討論「資料串流」,這些是使用專案二進位協定的 Arrow 記錄批次序列
  • 該格式與語言無關,現在已在 11 種語言中提供函式庫支援,並且還在不斷增加。

ODBC 等標準協定的實作通常實作它們自己的自訂線上二進位協定,這些協定必須編組到每個函式庫的公共介面和從公共介面編組出來。ODBC 或 JDBC 函式庫的效能因案例而異。

我們針對 Flight 的設計目標是為資料服務建立一個新的協定,該協定使用 Arrow 欄狀格式作為線上資料表示形式以及提供給開發人員的公共 API。透過這樣做,我們減少或消除了與資料傳輸相關的序列化成本,並提高了分散式資料系統的整體效率。此外,已經為了其他目的而使用 Apache Arrow 的兩個系統可以極其高效地相互傳輸資料。

Flight 基礎知識

Arrow Flight 函式庫提供了一個開發框架,用於實作可以發送和接收資料串流的服務。Flight 伺服器支援幾種基本類型的請求

  • Handshake:一個簡單的請求,用於確定客戶端是否已授權,並且在某些情況下,建立一個實作定義的會話令牌,以用於未來的請求
  • ListFlights:傳回可用資料串流的列表
  • GetSchema:傳回資料串流的綱要
  • GetFlightInfo:傳回感興趣資料集的「存取計畫」,可能需要消耗多個資料串流。此請求可以接受包含自訂序列化命令,例如,您的特定應用程式參數。
  • DoGet:將資料串流發送到客戶端
  • DoPut:從客戶端接收資料串流
  • DoAction:執行實作特定的動作並傳回任何結果,即,廣義的函式呼叫
  • ListActions:傳回可用動作類型的列表

我們利用 gRPC 優雅的「雙向」串流支援(建立在 HTTP/2 串流之上),允許客戶端和伺服器在請求被服務時同時相互發送資料和元資料。

一個簡單的 Flight 設定可能包含一個單一伺服器,客戶端連接到該伺服器並發出 DoGet 請求。

Flight Simple Architecture

最佳化透過 gRPC 的資料吞吐量

雖然使用像 gRPC 這樣的通用訊息傳遞函式庫除了顯而易見的好處之外還有許多特定的好處(利用 Google 在這個問題上所做的所有工程),但還需要做一些工作來提高傳輸大型資料集的效能。例如,許多類型的 gRPC 使用者只處理相對較小的訊息。

使用 gRPC 的最佳支援方式是在 Protocol Buffers(又名「Protobuf」).proto 檔案中定義服務。用於 gRPC 的 Protobuf 外掛程式產生 gRPC 服務存根,您可以使用這些存根來實作您的應用程式。RPC 命令和資料訊息使用 Protobuf 線路格式序列化。因為我們使用「vanilla gRPC 和 Protocol Buffers」,所以不了解 Arrow 欄狀格式的 gRPC 客戶端仍然可以與 Flight 服務互動並以不透明的方式處理 Arrow 資料。

Flight 中主要的資料相關 Protobuf 類型稱為 FlightData。一般來說,讀取和寫入 Protobuf 訊息並非免費,因此我們在 C++ 和 Java 的 gRPC 中實作了一些底層最佳化,以執行以下操作

  • 產生 FlightData 的 Protobuf 線路格式,包括正在發送的 Arrow 記錄批次,而無需經過任何中間記憶體複製或序列化步驟。
  • FlightData 的 Protobuf 表示形式重建 Arrow 記錄批次,而無需任何記憶體複製或反序列化。事實上,我們攔截編碼的資料酬載,而不允許 Protocol Buffers 函式庫接觸它們。

從某種意義上說,我們是「魚與熊掌兼得」。具有這些最佳化的 Flight 實作將具有更好的效能,而天真的 gRPC 客戶端仍然可以與 Flight 服務通訊並使用 Protobuf 函式庫來反序列化 FlightData(儘管會有一些效能損失)。

就絕對速度而言,在我們的 C++ 資料吞吐量基準測試中,我們在未啟用 TLS 的 localhost 上看到了超過 2-3GB/s 的端對端 TCP 吞吐量。此基準測試顯示大約 4 秒內傳輸了約 12 GB 的資料

$ ./arrow-flight-benchmark --records_per_stream 100000000
Bytes read: 12800000000
Nanos: 3900466413
Speed: 3129.63 MB/s

從此我們可以得出結論,Flight 和 gRPC 的機制增加的額外負擔相對較小,並且表明 Flight 的許多實際應用程式將受到網路頻寬的瓶頸限制。

水平可擴展性:平行和分割的資料存取

許多分散式資料庫類型系統都使用一種架構模式,其中客戶端請求的結果透過「協調器」路由並發送到客戶端。除了將資料集多次傳輸到客戶端的明顯效率問題外,它還為存取非常大的資料集帶來了可擴展性問題。

我們希望 Flight 能夠使系統建立水平可擴展的資料服務,而無需處理此類瓶頸。使用 GetFlightInfo RPC 對資料集發出的客戶端請求會傳回一個 端點 列表,每個端點都包含一個伺服器位置和一個 票證,用於在 DoGet 請求中將該票證發送到伺服器以取得完整資料集的一部分。為了存取整個資料集,必須消耗所有端點。雖然 Flight 串流不一定是有序的,但我們提供了應用程式定義的元資料,可用於序列化排序資訊。

這種多端點模式具有許多優點

  • 端點可以由客戶端平行讀取。
  • 服務於 GetFlightInfo 「規劃」請求的服務可以將工作委派給同級服務,以利用資料局部性或僅僅是為了幫助負載平衡。
  • 分散式叢集中的節點可以承擔不同的角色。例如,節點子集可能負責規劃查詢,而其他節點專門滿足資料串流(DoGetDoPut)請求。

以下是具有分割服務角色的多節點架構的範例圖

Flight Complex Architecture

動作:使用應用程式業務邏輯擴展 Flight

雖然 GetFlightInfo 請求支援在請求資料集時發送不透明的序列化命令,但客戶端可能需要能夠要求伺服器執行其他類型的操作。例如,客戶端可能會請求將特定資料集「釘選」在記憶體中,以便更快地服務來自其他客戶端的後續請求。

因此,Flight 服務可以選擇性地定義由 DoAction RPC 執行的「動作」。動作請求包含正在執行的動作名稱和包含進一步所需資訊的可選序列化資料。動作的結果是不透明二進位結果的 gRPC 串流。

一些範例動作

  • 元資料探索,超出內建 ListFlights RPC 提供的功能
  • 設定會話特定的參數和設定

請注意,伺服器不需要實作任何動作,並且動作不需要傳回結果。

加密和身份驗證

Flight 使用 gRPC 內建的 TLS / OpenSSL 功能開箱即用地支援加密。

對於身份驗證,客戶端和伺服器都有可擴展的身份驗證處理程式,允許簡單的身份驗證方案(如使用者和密碼)以及更複雜的身份驗證(如 Kerberos)。Flight 協定帶有內建的 BasicAuth,因此可以開箱即用地實作使用者/密碼身份驗證,而無需自訂開發。

中介軟體和追蹤

gRPC 具有「攔截器」的概念,這使我們能夠開發開發人員定義的「中介軟體」,它可以為傳入和傳出的請求提供檢測或遙測。用於此類檢測的一個框架是 OpenTracing

請注意,中介軟體功能是該專案最新的領域之一,目前僅在專案的主分支中可用。

gRPC,但不僅僅是 gRPC

我們使用符合 RFC 3986 的 URI 指定 DoGet 請求的伺服器位置。例如,TLS 安全的 gRPC 可以像 grpc+tls://$HOST:$PORT 這樣指定。

雖然我們認為使用 gRPC 作為 Flight 伺服器的「命令」層是有意義的,但我們可能希望支援 TCP 以外的資料傳輸層,例如 RDMA。雖然使之成為可能需要一些設計和開發工作,但我們的想法是可以使用 gRPC 來協調 get 和 put 傳輸,這些傳輸可以在 TCP 以外的協定上執行。

入門指南和後續步驟

Flight 使用者的文件正在進行中,但函式庫本身已經足夠成熟,可以供願意接受未來一年內一些小的 API 或協定變更的 Beta 使用者使用。

試用 Flight 最簡單的方法之一是使用 Python API,因為可以完全在 Python 中定義自訂伺服器和客戶端,而無需任何編譯。您可以在 Arrow 程式碼庫中看到 Python 中的範例 Flight 客戶端和伺服器

在實際應用中,Dremio 開發了一個 基於 Arrow Flight 的 連接器,該連接器已被證明可以 提供比 ODBC 高 20-50 倍的效能。對於 Apache Spark 使用者,Arrow 貢獻者 Ryan Murray 建立了一個 資料來源實作,以連接到啟用 Flight 的端點。

就 Flight 的「後續步驟」而言,對非 gRPC(或非 TCP)資料傳輸的支援可能是一個有趣的研究和開發方向。從這裡開始,Flight 的許多工作將是建立面向使用者的啟用 Flight 的服務。由於 Flight 是一個開發框架,我們預計面向使用者的 API 將利用 API 外觀層,該外觀層隱藏了許多通用的 Flight 詳細資訊以及與 Flight 在自訂資料服務中的特定應用程式相關的詳細資訊。