- Apache Fory Rust es un framework de serialización multiplataforma entre lenguajes que ofrece rendimiento de serialización ultrarrápido y gestión automática de referencias
- Basado en la tecnología zero-copy y la seguridad de tipos de Rust, maneja automáticamente referencias circulares, objetos de trait y evolución de esquemas
- Permite el intercambio de datos entre Rust, Python, Java, Go y otros lenguajes sin archivos IDL ni generación de código
- Según los benchmarks, registró una velocidad de procesamiento 10~20 veces superior a JSON y Protobuf
- Tiene un alto valor de uso en entornos de alto rendimiento como microservicios, pipelines de datos y sistemas en tiempo real
El dilema de la serialización y la llegada de Apache Fory Rust
- Los métodos tradicionales de serialización tenían la limitación de que había que sacrificar velocidad, flexibilidad o compatibilidad entre lenguajes
- Los formatos binarios hechos a mano son rápidos, pero frágiles ante cambios de esquema
- JSON/Protobuf son flexibles, pero tienen un overhead de rendimiento de más de 10 veces
- Las soluciones existentes tienen soporte limitado para funciones propias de cada lenguaje
- Apache Fory Rust consigue al mismo tiempo rendimiento y flexibilidad, y elimina la necesidad de IDL y de gestión manual de esquemas
Características principales
-
1. Verdadero soporte multiplataforma entre lenguajes
- Comparte el mismo protocolo binario entre Java, Python, C++, Go y otros
- Los datos serializados en Rust pueden deserializarse directamente en Python
- Sin archivos de esquema, generación de código ni problemas de desajuste de versiones, simplifica el intercambio de datos entre microservicios multilenguaje
-
2. Manejo automático de referencias circulares y compartidas
- Rastrea y conserva automáticamente estructuras de referencias circulares con las que la mayoría de los frameworks fallan
- Aunque se haga referencia al mismo objeto varias veces, solo se serializa una vez, manteniendo la identidad de referencia
- Adecuado para bases de datos de grafos, ORM y modelos de dominio complejos
-
3. Serialización de objetos de trait
- Soporta la serialización de objetos de trait como
Box en Rust
- Permite registrar tipos polimórficos con la macro
register_trait_type!
- Soporta diversas formas como
Box, Rc, Arc, dyn Any
- Permite implementar sistemas de plugins, colecciones heterogéneas y arquitecturas extensibles
-
4. Evolución de esquemas (modo compatible)
- El modo Compatible permite cambios de esquema entre versiones del servicio
- Se pueden agregar, eliminar o reordenar campos, así como convertir tipos opcionales
- No se permite cambiar tipos
- Es útil para despliegues sin downtime y la evolución independiente de microservicios
Base técnica
-
Diseño del protocolo
- Estructura:
| fory header | reference meta | type meta | value data |
- Aplica enteros de longitud variable, metadatos comprimidos, seguimiento de referencias y layout little-endian
- Mejora el rendimiento mediante deduplicación de objetos compartidos y compresión de metadatos de tipos
-
Generación de código en tiempo de compilación
- Elimina el overhead en tiempo de ejecución con generación de código basada en macros en lugar de reflection
- La macro
#[derive(ForyObject)] genera automáticamente funciones de serialización y deserialización
- Ofrece seguridad de tipos, tamaño mínimo del binario y soporte de autocompletado en el IDE
-
Arquitectura
fory/: API de alto nivel
fory-core/: motor de serialización (buffer I/O, registro de tipos, compresión de metadatos, etc.)
fory-derive/: definición de macros procedurales
- La estructura modular mejora la mantenibilidad y la extensibilidad
Resultados de benchmarks
- Velocidad de procesamiento 10~20 veces superior frente a JSON y Protobuf
- Ejemplos:
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
- En todos los casos de prueba, Fory registró el mejor rendimiento
Escenarios de uso
-
Casos adecuados
- Microservicios multilenguaje: intercambio de datos sin archivos de esquema
- Pipelines de datos de alto rendimiento: procesamiento de millones de registros por segundo
- Modelos de dominio complejos: soporte para referencias circulares y estructuras polimórficas
- Sistemas en tiempo real: latencia inferior a 1 ms y deserialización zero-copy
-
Cuándo considerar alternativas
- Si se necesita un formato legible por humanos → JSON/YAML
- Si se necesita un formato para almacenamiento a largo plazo → Parquet
- Para estructuras de datos simples → serde + bincode
Primeros pasos
-
Instalación
-
Ejemplo básico de serialización
- Registrar la estructura con
#[derive(ForyObject)] y luego usar serialize() / deserialize()
- Mantener la consistencia de los datos mediante el registro de type IDs
-
Serialización entre lenguajes
- Activar el modo de compatibilidad multilenguaje con
compatible(true).xlang(true)
- Soporta registro basado en ID o en nombre (
register_by_namespace, register_by_name)
Tipos soportados
- Básicos: bool, enteros, punto flotante, String
- Colecciones: Vec, HashMap, BTreeMap, HashSet, Option
- Smart pointers: Box, Rc, Arc, RcWeak, ArcWeak, RefCell, Mutex
- Fecha/hora: tipos de chrono
- Objetos definidos por el usuario: ForyObject, ForyRow
- Objetos de trait: Box/Rc/Arc, Rc/Arc
Hoja de ruta
-
Disponible en v0.13
- Generación estática de código, formato Row zero-copy, seguimiento de referencias circulares, serialización de objetos de trait y modo de compatibilidad de esquema
-
Funciones previstas
- Serialización de referencias entre lenguajes y actualizaciones parciales de Row
Consideraciones para producción
- Seguridad de hilos: después de completar el registro, se puede compartir con
Arc (Send + Sync)
- Manejo de errores: basado en
Result, con distinción explícita de errores como incompatibilidad de tipos o falta de buffer
Documentación y comunidad
Conclusión
- Apache Fory Rust es un framework de serialización de nueva generación que elimina el compromiso entre rendimiento, flexibilidad y compatibilidad entre lenguajes
- Automatización basada en macros, soporte para objetos de trait y manejo de referencias circulares maximizan la eficiencia de desarrollo
- Puede aprovecharse de inmediato en microservicios, pipelines de datos y sistemas en tiempo real
2 comentarios
¿Este rendimiento tiene sentido?
Opiniones en Hacker News
Ojalá se enfocaran en mejorar el tooling de tecnologías existentes como W3C EXI (Binary XML) en lugar de crear otro formato nuevo
No basta con que solo sea rápido; un formato sin ecosistema, como Aeron/SBT, tiene difícil expandirse. XML ya cuenta con ese ecosistema
Además, no puede representar de forma natural grafos de objetos complejos como referencias compartidas o referencias circulares
El formato Fory fue diseñado desde el inicio para resolver estos problemas y, al mismo tiempo, soportar compatibilidad entre lenguajes y evolución de esquemas
Es decir, lo ideal es diseñar primero la codificación y luego extenderla de regreso hacia lenguajes o clientes
Tengo dudas de que el benchmark sea justo
Viendo el código, cuando no se usa una struct de Fory, el proceso de serialización incluye una conversión to/from
En esa conversión se producen copias de strings y reasignaciones de arreglos
En un sistema real, tonic provee un buffer de 8 KB, así que sería más eficiente que simplemente usar Vec::default()
En un CPU Xeon Gold 6136 parece haber una mejora de 10x, pero si se eliminan las conversiones to/from y la clonación de Vec, y además se preasigna un buffer de 8 KB, en realidad queda más cerca de 3x
El benchmark debería reescribirse con un estilo tower service/codec sin nada de código específico de Fory
Fory está usando un writer pool durante las pruebas
Ver código relacionado
A largo plazo, creo que para mantener compatibilidad entre lenguajes se necesita un contrato especificado basado en IDL
Un enfoque que parte del lenguaje hacia la serialización es cómodo al inicio, pero con el tiempo se vuelve vulnerable a cambios en el runtime del lenguaje
Un proyecto de un solo lenguaje puede mantenerse simple sin IDL, pero a partir de tres o más lenguajes el IDL funciona como fuente única de verdad
Apache Fory planea agregar soporte opcional para IDL, de modo que cada equipo pueda elegir entre un enfoque language-first o schema-first según su situación
Me pregunto cómo mantienen tipos compartidos entre lenguajes sin esquema
En lenguajes tipados, el esquema se infiere a partir de la definición de clase, y en lenguajes no tipados se agregan anotaciones directamente en el código
Un ejemplo en Python puede verse aquí
Ver esta entrada del blog
Me pregunto por qué usar Fory en vez de formatos de serialización cero como CapnProto o Flatbuffers
Si hace falta compresión, se puede usar zstd
Aun así, su amplio soporte de lenguajes y facilidad de uso son impresionantes
En Python sigo prefiriendo dill, porque incluso puede serializar objetos de código
Enlace a dill
Ver el código del benchmark
Ejemplo aquí
pyfory muestra una tasa de compresión 3 veces mayor que cloudpickle y, con su función de auditoría de seguridad, previene ataques maliciosos de deserialización
El enlace del benchmark daba 404, pero encontré el enlace correcto
Es una lástima que hayan cambiado el nombre de “Fury” a “Fory”
Fury era un nombre perfecto para un framework de serialización rápida
La mayoría de los protocolos binarios buscan reducir el tamaño de los datos
Protobuf usa compresión de enteros (varint, zigzag)
Si solo comparas TPS puro, el enfoque de “no hacer nada” de mandar structs de C tal cual siempre va a ganar
Se muestra una tabla comparativa con varios datasets
Me pregunto si el límite de 4096 tipos de Fory es suficiente
Ver código relacionado
De hecho, casi nunca he visto casos con más de 4096 mensajes de protocolo definidos
El enlace del benchmark de Rust devuelve 404
En la raíz de la documentación no pude encontrar el directorio de benchmarks