9 Punkte von GN⁺ 2025-11-01 | 2 Kommentare | Auf WhatsApp teilen
  • Apache Fory Rust ist ein sprachübergreifendes Serialisierungs-Framework, das ultraschnelle Serialisierungsleistung und automatische Referenzverwaltung bietet
  • Basierend auf Rusts Zero-Copy-Technik und Typsicherheit verarbeitet es zyklische Referenzen, Trait-Objekte und Schema-Evolution automatisch
  • Unterstützt den Datenaustausch zwischen Rust, Python, Java, Go und weiteren Sprachen ohne IDL-Dateien oder Code-Generierung
  • Benchmark-Ergebnisse zeigen eine mehr als 10–20x höhere Verarbeitungsgeschwindigkeit im Vergleich zu JSON und Protobuf
  • Besonders wertvoll in Hochleistungsumgebungen wie Microservices, Datenpipelines und Echtzeitsystemen

Das Dilemma der Serialisierung und das Aufkommen von Apache Fory Rust

  • Bestehende Serialisierungsverfahren haben die Einschränkung, dass man meist auf Geschwindigkeit, Flexibilität oder Sprachkompatibilität verzichten muss
    • Manuell erstellte Binärformate sind schnell, aber anfällig bei Schemaänderungen
    • JSON/Protobuf sind flexibel, verursachen aber einen Performance-Overhead von mehr als dem 10-Fachen
    • Bestehende Lösungen unterstützen sprachspezifische Funktionen nur unzureichend
  • Apache Fory Rust vereint Performance und Flexibilität und macht IDL sowie manuelle Schemaverwaltung überflüssig

Hauptmerkmale

  • 1. Echte sprachübergreifende Unterstützung

    • Dasselbe Binärprotokoll wird in Java, Python, C++, Go und anderen Sprachen gemeinsam genutzt
    • In Rust serialisierte Daten können in Python direkt deserialisiert werden
    • Keine Schema-Dateien, keine Code-Generierung, keine Probleme mit Versionsabweichungen – das vereinfacht den Datenaustausch zwischen mehrsprachigen Microservices
  • 2. Automatische Verarbeitung zyklischer und gemeinsamer Referenzen

    • Zyklische Referenzstrukturen, an denen die meisten Frameworks scheitern, werden automatisch verfolgt und erhalten
    • Selbst wenn dasselbe Objekt mehrfach referenziert wird, wird es nur einmal serialisiert, wobei die Referenzidentität erhalten bleibt
    • Geeignet für Graphdatenbanken, ORMs und komplexe Domänenmodelle
  • 3. Serialisierung von Trait-Objekten

    • Unterstützt die Serialisierung von Trait-Objekten wie Rusts Box
    • Mit dem Makro register_trait_type! lassen sich polymorphe Typen registrieren
    • Unterstützt verschiedene Formen wie Box, Rc, Arc, dyn Any
    • Ermöglicht Plugin-Systeme, heterogene Collections und erweiterbare Architekturen
  • 4. Schema-Evolution (Kompatibilitätsmodus)

    • Im Compatible-Modus sind Schemaänderungen zwischen Service-Versionen erlaubt
      • Felder können hinzugefügt, entfernt, umsortiert oder in Option-Typen umgewandelt werden
      • Typänderungen sind nicht möglich
    • Nützlich für Deployments ohne Downtime und die unabhängige Evolution von Microservices

Technische Grundlage

  • Protokolldesign

    • Struktur: | fory header | reference meta | type meta | value data |
    • Verwendet Ganzzahlen variabler Länge, komprimierte Metadaten, Referenz-Tracking und ein Little-Endian-Layout
    • Die Entfernung von Duplikaten bei gemeinsam genutzten Objekten und die Komprimierung von Typ-Metadaten steigern die Performance
  • Code-Generierung zur Compile-Zeit

    • Makrobasierte Code-Generierung statt Reflection eliminiert Runtime-Overhead
    • Das Makro #[derive(ForyObject)] erzeugt automatisch Serialisierungs- und Deserialisierungsfunktionen
    • Sorgt für Typsicherheit, minimale Binärgröße und IDE-Autovervollständigung
  • Architektur

    • fory/: High-Level-API
    • fory-core/: Serialisierungs-Engine (Buffer-I/O, Typregistrierung, Metadaten-Komprimierung usw.)
    • fory-derive/: Definition prozeduraler Makros
    • Die modulare Struktur verbessert Wartbarkeit und Erweiterbarkeit

Benchmark-Ergebnisse

  • Im Vergleich zu JSON und Protobuf mehr als 10–20x höhere Verarbeitungsgeschwindigkeit
  • Beispiele:
    • simple_struct(small) → Fory 35,729,598 TPS / JSON 10,167,045 / Protobuf 8,633,342
    • person(medium) → Fory 3,839,656 TPS / JSON 337,610 / Protobuf 369,031
  • In allen Testfällen erzielte Fory die beste Performance

Einsatzszenarien

  • Geeignete Anwendungsfälle

    • Mehrsprachige Microservices: Datenaustausch ohne Schema-Dateien
    • Hochleistungs-Datenpipelines: Verarbeitung von Millionen Datensätzen pro Sekunde
    • Komplexe Domänenmodelle: Unterstützung für zyklische Referenzen und polymorphe Strukturen
    • Echtzeitsysteme: Latenz unter 1 ms, Zero-Copy-Deserialisierung
  • Wann Alternativen sinnvoll sind

    • Wenn ein menschenlesbares Format benötigt wird → JSON/YAML
    • Wenn ein Format für Langzeitspeicherung benötigt wird → Parquet
    • Für einfache Datenstrukturen → serde + bincode

Erste Schritte

  • Installation

    • In Cargo.toml hinzufügen:
      [dependencies]  
      fory = "0.13"  
      
  • Beispiel für grundlegende Serialisierung

    • Struktur mit #[derive(ForyObject)] registrieren und danach serialize() / deserialize() verwenden
    • Die Registrierung einer Typ-ID sorgt für Datenkonsistenz
  • Sprachübergreifende Serialisierung

    • Mit compatible(true).xlang(true) den mehrsprachigen Kompatibilitätsmodus aktivieren
    • Unterstützt Registrierung auf ID- oder Namensbasis (register_by_namespace, register_by_name)

Unterstützte Typen

  • Primitive Typen: bool, Ganzzahlen, Fließkommazahlen, String
  • Collections: Vec, HashMap, BTreeMap, HashSet, Option
  • Smart Pointer: Box, Rc, Arc, RcWeak, ArcWeak, RefCell, Mutex
  • Datum/Uhrzeit: chrono-Typen
  • Benutzerdefinierte Objekte: ForyObject, ForyRow
  • Trait-Objekte: Box/Rc/Arc, Rc/Arc

Roadmap

  • Verfügbar in v0.13

    • Statische Code-Generierung, Zero-Copy-Row-Format, Tracking zyklischer Referenzen, Serialisierung von Trait-Objekten, schema-kompatibler Modus
  • Geplante Funktionen

    • Sprachübergreifende Referenz-Serialisierung, partielle Row-Updates

Hinweise für den Produktionseinsatz

  • Thread-Sicherheit: Nach abgeschlossener Registrierung per Arc gemeinsam nutzbar (Send + Sync)
  • Fehlerbehandlung: Auf Result basierend, mit expliziter Unterscheidung von Fehlern wie Typkonflikten oder unzureichendem Buffer

Dokumentation und Community

  • Offizielle Dokumentation: fory.apache.org/docs
  • API-Dokumentation: docs.rs/fory
  • Community: GitHub, Slack und Issue Tracker
  • Open Source unter der Apache License 2.0

Fazit

  • Apache Fory Rust ist ein Serialisierungs-Framework der nächsten Generation, das den Zielkonflikt zwischen Performance, Flexibilität und Sprachkompatibilität auflöst
  • Makrobasierte Automatisierung, Unterstützung für Trait-Objekte und Verarbeitung zyklischer Referenzen maximieren die Entwicklungseffizienz
  • Sofort nutzbar für Microservices, Datenpipelines und Echtzeitsysteme

2 Kommentare

 
qlghwp123 2025-11-01

Ist diese Leistung überhaupt plausibel?

 
GN⁺ 2025-11-01
Hacker-News-Kommentare
  • Ich wünschte, man würde sich eher darauf konzentrieren, das Tooling für bestehende Technologien wie W3C EXI (Binary XML) zu verbessern, statt neue Formate zu schaffen.
    Nur schnell zu sein reicht nicht aus, und Formate ohne Ökosystem wie Aeron/SBE lassen sich nur schwer verbreiten. XML hat dieses Ökosystem bereits.

    • Binary-XML-Encoding ist in bestimmten Situationen nützlich, aber nicht so effizient wie moderne binäre Serialisierungsformate.
      Außerdem lassen sich komplexe Objektdiagramme wie gemeinsam genutzte Referenzen oder zyklische Referenzen nicht natürlich darstellen.
      Das Fory-Format wurde von Anfang an so entworfen, dass es diese Probleme löst und zugleich sprachübergreifende Kompatibilität sowie Schema-Evolution unterstützt.
    • Ich weiß nicht, ob W3C EXI oder ASN.1 BER besser ist, aber ich halte einen DOP-(Data-Oriented-Programming-)Ansatz für richtig.
      Das heißt, man sollte zuerst das Encoding entwerfen und es dann rückwärts auf Sprachen oder Clients ausweiten.
  • Ich bezweifle, dass der Benchmark fair ist.
    Im Code-Link sieht man, dass bei Nicht-Fory-Structs im Serialisierungsprozess to/from-Konvertierungen enthalten sind.
    In diesem Umwandlungsprozess kommt es zu String-Kopien oder einer Neuallokation von Arrays.
    In realen Systemen stellt tonic einen 8-KB-Puffer bereit und wäre damit effizienter als ein simples Vec::default().

    • Dieser Benchmark ist nicht fair.
      Auf einer Xeon-Gold-6136-CPU sieht es nach einer 10-fachen Verbesserung aus, aber wenn man to/from-Konvertierungen und Vec-Kopien entfernt und einen 8-KB-Puffer vorab allokiert, liegt der tatsächliche Wert eher bei etwa 3x.
      Der Benchmark sollte im tower-service/codec-Stil komplett ohne Fory-spezifischen Code neu geschrieben werden.
      Fory verwendet im Test einen Writer-Pool.
      Siehe zugehöriger Code.
  • Ich denke, um langfristig sprachübergreifende Kompatibilität zu erhalten, braucht man einen spezifizierten Vertrag auf IDL-Basis.
    Ein Ansatz, der von der Sprache zur Serialisierung ausgeht, ist anfangs bequem, wird aber mit der Zeit anfällig für Veränderungen in den Sprach-Runtimes.

    • Je mehr Sprachen hinzukommen, desto wichtiger wird ein offizielles Schema.
      Ein Single-Language-Projekt kann auch ohne IDL einfach bleiben, aber ab drei oder mehr Sprachen dient IDL als Single Source of Truth.
      Apache Fory will optional IDL-Unterstützung hinzufügen, damit Teams je nach Situation einen sprachzentrierten oder schemazentrierten Ansatz wählen können.
  • Ich frage mich, wie man ohne Schema gemeinsame Typen sprachübergreifend beibehält.

    • Es gibt eine Typzuordnungstabelle.
      In typisierten Sprachen wird das Schema aus der Klassendefinition abgeleitet, in nicht typisierten Sprachen werden Annotationen direkt in den Code geschrieben.
      Ein Python-Beispiel findet sich hier.
    • Es ist verwirrend, dass polyglotte Teams als zentraler Anwendungsfall genannt werden, zugleich aber behauptet wird, keine Schema-Datei zu benötigen.
      Siehe zugehöriger Blogpost.
    • Ich bin skeptisch, ob dieser Ansatz langfristig gut funktioniert.
    • Es gibt zu wenig Erklärung dazu, wie stark das Ganze tatsächlich in Produktion eingesetzt wird, um Vertrauen zu schaffen.
  • Ich frage mich, warum man Fory statt Zero-Serialization-Formaten wie CapnProto oder Flatbuffers verwenden sollte.
    Wenn Kompression nötig ist, kann man einfach zstd verwenden.
    Trotzdem sind Forys breite Sprachunterstützung und die einfache Nutzbarkeit beeindruckend.
    In Python bevorzuge ich weiterhin dill — weil sich damit sogar Code-Objekte serialisieren lassen.
    dill-Link

    • Laut Benchmark im Vergleich zu dill ist Fory 20- bis 40-mal schneller und erreicht bis zu 7x höhere Kompressionsraten.
      Siehe Benchmark-Code.
    • Apache Fory kann auch als Ersatz für pickle/cloudpickle verwendet werden und unterstützt die Serialisierung von Code-Objekten wie lokalen Funktionen oder Klassen.
      Beispiel-Link
      pyfory erreicht gegenüber cloudpickle eine 3x höhere Kompressionsrate und verhindert mit einer Security-Audit-Funktion bösartige Deserialisierungsangriffe.
  • Der Benchmark-Link lieferte zwar 404, aber ich habe den funktionierenden Link gefunden.

  • Schade, dass der Name von „Fury“ zu „Fory“ geändert wurde.
    Fury war ein perfekt passender Name für ein schnelles Serialisierungs-Framework.

    • „Fury“ war ursprünglich ein Name, den ich selbst vergeben hatte, daher hing ich daran, aber er musste leider geändert werden.
  • Die meisten binären Protokolle versuchen, die Datengröße zu verringern.
    Protobuf verwendet Integer-Kompression (varint, zigzag).
    Wenn man nur rohe TPS vergleicht, gewinnt zwangsläufig immer ein „Do-Nothing“-Ansatz, bei dem C-Structs einfach unverändert übertragen werden.

    • Fory unterstützt ebenfalls Integer-Kompression, und die Datengröße ist fast identisch mit Protobuf.
      Dazu werden Vergleichstabellen für verschiedene Datensätze gezeigt.
    • Es gibt zwei Schema-Kompatibilitätsmodi, aber binäre Kompatibilität zwischen Minor-Versionen wird nicht garantiert.
  • Ich frage mich, ob Forys 4096-Typen-Limit ausreicht.
    Siehe zugehöriger Code.

    • Für alle Fälle reicht das wohl nicht, aber bei Bedarf ließe es sich erweitern.
      Ich habe tatsächlich kaum Fälle gesehen, in denen mehr als 4096 Protokollnachrichten definiert wurden.
  • Der Rust-Benchmark-Link führt zu einem 404-Fehler.
    Im Dokumentations-Root war kein Benchmark-Verzeichnis zu finden.