- Apache Fory Rust là khung tuần tự hóa đa ngôn ngữ cung cấp hiệu năng tuần tự hóa siêu nhanh và quản lý tham chiếu tự động
- Dựa trên kỹ thuật zero-copy và tính an toàn kiểu của Rust, nó tự động xử lý tham chiếu vòng, đối tượng trait và tiến hóa schema
- Hỗ trợ trao đổi dữ liệu giữa nhiều ngôn ngữ như Rust, Python, Java, Go mà không cần file IDL hay sinh mã
- Theo kết quả benchmark, đạt tốc độ xử lý nhanh hơn 10–20 lần so với JSON và Protobuf
- Có giá trị ứng dụng cao trong các môi trường hiệu năng cao như microservice, pipeline dữ liệu và hệ thống thời gian thực
Thế lưỡng nan của tuần tự hóa và sự xuất hiện của Apache Fory Rust
- Các phương thức tuần tự hóa truyền thống có giới hạn là phải đánh đổi một trong ba yếu tố: tốc độ, tính linh hoạt hoặc khả năng tương thích ngôn ngữ
- Định dạng nhị phân viết tay thì nhanh nhưng dễ vỡ khi schema thay đổi
- JSON/Protobuf linh hoạt nhưng phải chịu overhead hiệu năng hơn 10 lần
- Các giải pháp hiện có thiếu hỗ trợ cho các tính năng đặc thù của ngôn ngữ
- Apache Fory Rust đồng thời đạt được hiệu năng và tính linh hoạt, đồng thời không cần IDL hay quản lý schema thủ công
Các đặc điểm chính
-
1. Hỗ trợ đa ngôn ngữ thực sự
- Chia sẻ cùng một giao thức nhị phân với Java, Python, C++, Go...
- Dữ liệu được tuần tự hóa trong Rust có thể được giải tuần tự trực tiếp trong Python
- Không có file schema, sinh mã hay vấn đề lệch phiên bản, giúp đơn giản hóa trao đổi dữ liệu giữa các microservice đa ngôn ngữ
-
2. Tự động xử lý tham chiếu vòng và tham chiếu dùng chung
- Tự động theo dõi và bảo toàn cấu trúc tham chiếu vòng, thứ mà đa số framework đều thất bại
- Ngay cả khi cùng một đối tượng được tham chiếu nhiều lần, nó chỉ được tuần tự hóa một lần, vẫn giữ nguyên tính đồng nhất tham chiếu
- Phù hợp với cơ sở dữ liệu đồ thị, ORM và các mô hình miền phức tạp
-
3. Tuần tự hóa đối tượng trait
- Hỗ trợ tuần tự hóa đối tượng trait như
Box của Rust
- Có thể đăng ký kiểu đa hình bằng macro
register_trait_type!
- Hỗ trợ nhiều dạng như
Box, Rc, Arc, dyn Any
- Có thể xây dựng hệ thống plugin, tập hợp không đồng nhất và kiến trúc mở rộng
-
4. Tiến hóa schema (chế độ tương thích)
- Chế độ Compatible cho phép thay đổi schema giữa các phiên bản dịch vụ
- Có thể thêm/xóa trường, thay đổi thứ tự trường, chuyển đổi kiểu tùy chọn
- Không thể thay đổi kiểu
- Hữu ích cho triển khai không gián đoạn và sự tiến hóa độc lập của microservice
Nền tảng kỹ thuật
-
Thiết kế giao thức
- Cấu trúc:
| fory header | reference meta | type meta | value data |
- Áp dụng số nguyên độ dài biến thiên, metadata nén, theo dõi tham chiếu và bố cục little-endian
- Cải thiện hiệu năng nhờ loại bỏ trùng lặp đối tượng dùng chung và nén metadata kiểu
-
Sinh mã tại thời điểm biên dịch
- Loại bỏ overhead runtime bằng sinh mã dựa trên macro thay vì reflection
- Macro
#[derive(ForyObject)] tự động tạo hàm tuần tự hóa và giải tuần tự hóa
- Đảm bảo an toàn kiểu, giảm kích thước binary, hỗ trợ tự động hoàn thành trong IDE
-
Cấu trúc kiến trúc
fory/: API cấp cao
fory-core/: engine tuần tự hóa (buffer I/O, đăng ký kiểu, nén meta...)
fory-derive/: định nghĩa procedural macro
- Cấu trúc module hóa giúp tăng khả năng bảo trì và mở rộng
Kết quả benchmark
- Tốc độ xử lý nhanh hơn 10–20 lần so với JSON và Protobuf
- Ví dụ:
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
- Trong mọi test case, Fory ghi nhận hiệu năng cao nhất
Kịch bản ứng dụng
-
Trường hợp sử dụng phù hợp
- Microservice đa ngôn ngữ: trao đổi dữ liệu không cần file schema
- Pipeline dữ liệu hiệu năng cao: xử lý hàng triệu bản ghi mỗi giây
- Mô hình miền phức tạp: hỗ trợ tham chiếu vòng và cấu trúc đa hình
- Hệ thống thời gian thực: độ trễ dưới 1ms, giải tuần tự zero-copy
-
Cân nhắc các lựa chọn thay thế
- Khi cần định dạng dễ đọc với con người → JSON/YAML
- Khi cần định dạng lưu trữ dài hạn → Parquet
- Với cấu trúc dữ liệu đơn giản → serde + bincode
Bắt đầu
-
Cài đặt
-
Ví dụ tuần tự hóa cơ bản
- Đăng ký struct bằng
#[derive(ForyObject)], sau đó dùng serialize() / deserialize()
- Duy trì tính nhất quán dữ liệu bằng đăng ký type ID
-
Tuần tự hóa đa ngôn ngữ
- Kích hoạt chế độ tương thích đa ngôn ngữ bằng thiết lập
compatible(true).xlang(true)
- Hỗ trợ đăng ký theo ID hoặc theo tên (
register_by_namespace, register_by_name)
Các kiểu được hỗ trợ
- Kiểu cơ bản: bool, số nguyên, số thực dấu chấm động, String
- Tập hợp: Vec, HashMap, BTreeMap, HashSet, Option
- Con trỏ thông minh: Box, Rc, Arc, RcWeak, ArcWeak, RefCell, Mutex
- Ngày/giờ: các kiểu của chrono
- Đối tượng do người dùng định nghĩa: ForyObject, ForyRow
- Đối tượng trait: Box/Rc/Arc, Rc/Arc
Lộ trình
-
Có trong v0.13
- Sinh mã tĩnh, định dạng Row zero-copy, theo dõi tham chiếu vòng, tuần tự hóa đối tượng trait, chế độ tương thích schema
-
Tính năng dự kiến
- Tuần tự hóa tham chiếu đa ngôn ngữ, cập nhật Row từng phần
Các lưu ý cho production
- An toàn luồng: sau khi hoàn tất đăng ký có thể chia sẻ bằng
Arc (Send + Sync)
- Xử lý lỗi: dựa trên
Result, phân biệt rõ các lỗi như không khớp kiểu, thiếu buffer...
Tài liệu và cộng đồng
Kết luận
- Apache Fory Rust là khung tuần tự hóa thế hệ mới loại bỏ sự đánh đổi giữa hiệu năng, tính linh hoạt và khả năng tương thích ngôn ngữ
- Tự động hóa dựa trên macro, hỗ trợ đối tượng trait và xử lý tham chiếu vòng giúp tối đa hóa hiệu quả phát triển
- Có thể ứng dụng ngay trong microservice, pipeline dữ liệu và hệ thống thời gian thực
2 bình luận
Hiệu năng này có hợp lý không?
Ý kiến Hacker News
Giá như họ tập trung cải thiện tooling cho các công nghệ sẵn có như W3C EXI (Binary XML) thay vì tạo thêm một định dạng mới
Chỉ nhanh thôi thì chưa đủ; các định dạng không có hệ sinh thái như Aeron/SBE rất khó được phổ biến. XML thì đã có sẵn hệ sinh thái đó
Ngoài ra, nó cũng không biểu diễn tự nhiên được các đồ thị đối tượng phức tạp như tham chiếu dùng chung hoặc tham chiếu vòng
Định dạng Fory được thiết kế ngay từ đầu để giải quyết các vấn đề này, đồng thời hỗ trợ tương thích liên ngôn ngữ và tiến hóa schema
Tức là nên thiết kế phần mã hóa trước rồi mở rộng ngược ra ngôn ngữ hay client
Tôi nghi ngờ benchmark này có công bằng hay không
Xem liên kết mã nguồn, nếu không phải struct của Fory thì trong quá trình tuần tự hóa có kèm cả bước chuyển đổi to/from
Ở bước chuyển đổi này sẽ phát sinh sao chép chuỗi và cấp phát lại mảng
Trong hệ thống thực tế, tonic cung cấp bộ đệm 8KB nên sẽ hiệu quả hơn
Vec::default()đơn thuầnTrên CPU Xeon Gold 6136 thì trông như nhanh hơn 10 lần, nhưng nếu bỏ chuyển đổi to/from, bỏ sao chép Vec và cấp phát sẵn bộ đệm 8KB thì thực tế chỉ khoảng 3 lần
Benchmark nên được viết lại theo kiểu tower service/codec mà hoàn toàn không có mã nào liên quan riêng tới Fory
Fory đang dùng writer pool trong lúc test
Xem mã liên quan
Về lâu dài, để duy trì tương thích liên ngôn ngữ thì tôi nghĩ cần có hợp đồng được đặc tả dựa trên IDL
Cách tiếp cận đi từ ngôn ngữ sang tuần tự hóa thì ban đầu tiện, nhưng theo thời gian sẽ dễ bị ảnh hưởng bởi thay đổi trong runtime ngôn ngữ
Dự án chỉ dùng một ngôn ngữ có thể giữ mọi thứ đơn giản mà không cần IDL, nhưng từ ba ngôn ngữ trở lên thì IDL đóng vai trò là nguồn chân lý duy nhất
Apache Fory dự định sẽ bổ sung hỗ trợ IDL theo dạng tùy chọn, để các nhóm có thể chọn cách tiếp cận ưu tiên ngôn ngữ hoặc ưu tiên schema tùy tình huống
Tôi tò mò họ duy trì shared type liên ngôn ngữ như thế nào khi không có schema
Với ngôn ngữ có kiểu, schema được suy ra từ định nghĩa lớp; còn với ngôn ngữ không có kiểu thì chú thích được ghi trực tiếp trong mã
Có thể xem ví dụ Python tại đây
Xem bài blog liên quan
Tôi muốn biết vì sao nên dùng Fory thay vì các định dạng không cần deserialization như CapnProto hay Flatbuffers
Nếu cần nén thì cứ dùng zstd là được
Dù vậy, hỗ trợ ngôn ngữ rộng và tính dễ dùng của Fory vẫn rất ấn tượng
Ở Python tôi vẫn thích dill hơn — vì nó còn tuần tự hóa được cả code object
liên kết dill
Xem mã benchmark
liên kết ví dụ
pyfory cho tỷ lệ nén cao hơn cloudpickle 3 lần và có tính năng kiểm toán bảo mật để ngăn chặn các cuộc tấn công giải tuần tự hóa độc hại
Liên kết benchmark bị 404, nhưng tôi đã tìm được liên kết đúng
Hơi tiếc khi họ đổi tên từ “Fury” thành “Fory”
Fury là cái tên quá hợp cho một framework tuần tự hóa tốc độ cao
Phần lớn giao thức nhị phân đều cố gắng giảm kích thước dữ liệu
Protobuf dùng nén số nguyên (varint, zigzag)
Nếu chỉ so TPS đơn thuần thì cách “không làm gì cả” bằng cách gửi nguyên struct C lúc nào cũng sẽ thắng
Họ đưa ra bảng so sánh trên nhiều bộ dữ liệu khác nhau
Tôi thắc mắc liệu giới hạn 4096 kiểu của Fory có đủ hay không
Xem mã liên quan
Trên thực tế gần như chưa thấy trường hợp nào định nghĩa hơn 4096 thông điệp giao thức
Liên kết benchmark Rust trả về lỗi 404
Trong trang gốc tài liệu cũng không tìm thấy thư mục benchmark