使用 Apache Fury 實現極快的序列化

banq發表於2024-07-09

在本文中,我們將瞭解Apache 軟體基金會下的一個孵化專案Apache Fury。該庫承諾提供極快的效能、強大的功能和多語言支援。

我們將研究該專案的一些基本功能並將其效能與其他框架進行比較。

使用 Apache Fury 進行序列化
序列化是軟體開發中的一個關鍵過程,可實現系統之間的高效資料交換。它允許應用程式共享狀態並透過它進行通訊。

Apache Fury 是一個序列化庫,旨在解決現有庫和框架的侷限性。它提供了一個高效能、易於使用的庫,用於跨各種程式語言序列化和反序列化資料。旨在高效處理複雜的資料結構和大量資料。Apache Fury 提供的主要功能包括:

  • 高效能:Apache Fury 針對速度進行了最佳化,確保在序列化和反序列化過程中的開銷最小。
  • 跨語言支援:支援多種程式語言,使其適用於不同的開發環境(Java/Python/C++/Golang/JavaScript/Rust/Scala/TypeScript)。
  • 複雜資料結構:能夠輕鬆處理複雜的資料模型。
  • 緊湊序列化:生成緊湊的序列化資料,降低儲存和傳輸成本。
  • GraalVM 原生映象支援:GraalVM 原生映象需要 AOT 編譯序列化,不需要反射/序列化 JSON 配置。

程式碼示例
首先,我們需要向我們的專案新增所需的依賴項,以便我們可以開始與 Fury 庫 API 進行互動:

<dependency>
    <groupId>org.apache.fury</groupId>
    <artifactId>fury-core</artifactId>
    <version>0.5.0</version>
</dependency>

首次嘗試 Fury 時,讓我們使用不同的資料型別和至少一個巢狀物件建立一個簡單的結構,以便我們可以在實際應用程式中模擬日常用例。為此,我們需要建立一個UserEvent類來表示稍後將被序列化的使用者事件的狀態:

public class UserEvent implements Serializable {
    private final String userId;
    private final String eventType;
    private final long timestamp;
    private final Address address;
    <font>// Constructor and getters<i>
}

為了給我們的事件物件引入更多的複雜性,讓我們使用名為Address 的 Java POJO 為地址定義一個巢狀結構:

public class Address implements Serializable {
    private final String street;
    private final String city;
    private final String zipCode;
    <font>// Constructor and getters<i>
}

一個重要的方面是 Fury 不需要類實現Serializable介面。但是,稍後我們將使用 Java 本機序列化程式,它確實需要它。接下來,我們應該啟動 Fury 上下文。

Fury 設定
現在,我們將瞭解如何設定 Fury,以便開始使用它:

class FurySerializationUnitTest {
    @Test
    void whenUsingFurySerialization_thenGenerateByteOutput() {
        Fury fury = Fury.builder()
          .withLanguage(Language.JAVA)
          .withAsyncCompilation(true)
          .build();
        fury.register(UserEvent.class);
        fury.register(Address.class);
        
        <font>// ...<i>
}

在此程式碼片段中,我們建立了 Fury 物件並將 Java 定義為要使用的協議,因為它最適合這種情況。但是,如前所述,Fury 支援跨語言序列化(例如使用Language.XLANG ) 。此外,我們將withAsyncCompilation選項設定為true,這允許使用 JIT(即時)在後臺編譯序列化程式,並且我們的應用程式可以繼續處理其他任務而無需等待編譯完成。它使用非阻塞編譯來實現此最佳化。

Fury 設定完成後,我們需要註冊可能被序列化的類。這很重要,因為 Fury 可以使用預生成的架構或後設資料來簡化序列化和反序列化過程。這樣就無需執行時反射,因為執行時反射可能很慢且佔用大量資源。

此外,註冊類有助於減少在序列化和反序列化期間動態確定類結構所帶來的開銷。這可以縮短處理時間。最後,從安全形度來看,這很重要,因為我們建立了一個允許序列化和反序列化的類的安全列表。

Fury 的登錄檔可防止意外或惡意序列化意外類,這可能會導致安全漏洞,例如反序列化攻擊。它還可以降低利用序列化機制或類本身漏洞的風險。反序列化任意或意外類可能會導致程式碼執行漏洞。

使用 Fury
現在 Fury 已配置完畢,我們可以使用此物件執行多個序列化和反序列化操作。它提供了許多 API,可以訪問序列化過程的底層和高層細節,但在我們的例子中,我們可以呼叫以下方法:

@Test
void whenUsingFurySerialization_thenGenerateByteOutput() { 
    <font>//... setup<i>
    byte[] serializedData = fury.serialize(event);
    UserEvent temp = (UserEvent) fury.deserialize(serializedData);
   
//...<i>
}

我們需要它來使用該庫執行這兩個基本操作並利用其巨大潛力。儘管如此,我們如何將它與 Java 中使用的其他知名序列化框架進行比較?接下來,我們將進行一些實驗來進行這樣的比較。

比較
首先,本教程並不打算對 Apache Fury 和其他框架進行廣泛的基準測試。話雖如此,為了瞭解該專案旨在實現的效能型別,讓我們看看不同的庫和框架在我們的示例用例中的表現如何。為了進行比較,我們使用了 Java Native 序列化、Avro 序列化和Protobuf協議緩衝區

  • 一開始,Protobuf 的表現優於 Fury,
  • 但後來,Fury 似乎表現更好,很可能是因為 JIT 編譯器的性質。

然而,正如我們所觀察到的,兩者都表現優異
 
在序列化過程的輸出方面,Protobuf 似乎效能略好一些,不過 Fury 和它之間的差異看起來相當小,所以我們可以說它們的效能也是相當的。

再次強調,這可能不適用於所有情況。這不是一項廣泛的基準測試,而是基於我們的用例的比較。儘管如此,Apache Fury 提供了出色的效能和易於使用的功能,這正是該專案的目標。

結論
在本教程中,我們瞭解了 Fury,這是一個序列化庫,它提供極快、跨語言、由 JIT(即時編譯)和零複製序列化和反序列化功能。此外,我們還了解了它與 Java 生態系統中使用的其他知名序列化框架相比的效能。

無論哪個庫或框架更快/更高效,Fury 處理複雜資料結構和提供跨語言支援的能力使其成為需要高速資料處理的現代應用程式的絕佳選擇。透過整合 Apache Fury,開發人員可以確保他們的應用程式以最小的開銷執行序列化和反序列化任務,從而提高整體效率和效能。

相關文章