- Apache Fory Rust é um framework de serialização cross-language que oferece desempenho de serialização ultrarrápido e gerenciamento automático de referências
- Com base na tecnologia zero-copy e na segurança de tipos do Rust, lida automaticamente com referências circulares, objetos de trait e evolução de schema
- Suporta troca de dados entre várias linguagens, como Rust, Python, Java e Go, sem arquivos IDL nem geração de código
- Segundo os benchmarks, registrou velocidade de processamento 10 a 20 vezes maior que JSON e Protobuf
- Tem alto potencial de uso em ambientes de alto desempenho, como microsserviços, pipelines de dados e sistemas em tempo real
O dilema da serialização e o surgimento do Apache Fory Rust
- Os métodos tradicionais de serialização tinham a limitação de precisar abrir mão de um entre velocidade, flexibilidade e compatibilidade entre linguagens
- Formatos binários manuais são rápidos, mas frágeis diante de mudanças de schema
- JSON/Protobuf são flexíveis, mas têm overhead de desempenho superior a 10x
- Soluções existentes têm suporte insuficiente para recursos específicos de cada linguagem
- O Apache Fory Rust garante desempenho e flexibilidade ao mesmo tempo, sem exigir IDL nem gerenciamento manual de schema
Principais recursos
-
1. Verdadeiro suporte cross-language
- Compartilha o mesmo protocolo binário entre Java, Python, C++, Go e outras linguagens
- Dados serializados em Rust podem ser desserializados diretamente em Python
- Sem arquivos de schema, geração de código ou problemas de incompatibilidade de versão, simplificando a troca de dados entre microsserviços multilíngues
-
2. Tratamento automático de referências circulares e compartilhadas
- Rastreia e preserva automaticamente estruturas com referências circulares, nas quais a maioria dos frameworks falha
- Mesmo que o mesmo objeto seja referenciado várias vezes, ele é serializado apenas uma vez, preservando a identidade da referência
- Adequado para bancos de dados em grafo, ORM e modelos de domínio complexos
-
3. Serialização de objetos de trait
- Suporta serialização de objetos de trait como
Box no Rust
- É possível registrar tipos polimórficos com a macro
register_trait_type!
- Suporta vários formatos, como
Box, Rc, Arc e dyn Any
- Permite implementar sistemas de plugins, coleções heterogêneas e arquiteturas extensíveis
-
4. Evolução de schema (modo compatível)
- O modo Compatible permite mudanças de schema entre versões de serviço
- É possível adicionar, remover e reordenar campos, além de converter tipos opcionais
- Mudança de tipo não é permitida
- Útil para deploy sem downtime e evolução independente de microsserviços
Base técnica
-
Design do protocolo
- Estrutura:
| fory header | reference meta | type meta | value data |
- Aplica inteiros de tamanho variável, metadados compactados, rastreamento de referências e layout little-endian
- Melhora o desempenho com eliminação de duplicação de objetos compartilhados e compressão de metadados de tipo
-
Geração de código em tempo de compilação
- Elimina overhead em runtime com geração de código baseada em macros em vez de reflection
- A macro
#[derive(ForyObject)] gera automaticamente funções de serialização e desserialização
- Garante segurança de tipos, tamanho mínimo de binário e suporte a autocompletar da IDE
-
Estrutura da arquitetura
fory/: API de alto nível
fory-core/: engine de serialização (buffer I/O, registro de tipos, compressão de metadados etc.)
fory-derive/: definição de macros procedurais
- Estrutura modularizada para melhor manutenção e extensibilidade
Resultados de benchmark
- Velocidade de processamento 10 a 20 vezes maior que JSON e Protobuf
- Exemplos:
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
- Em todos os casos de teste, o Fory registrou o melhor desempenho
Cenários de uso
-
Casos de uso adequados
- Microsserviços multilíngues: troca de dados sem arquivos de schema
- Pipelines de dados de alto desempenho: processamento de milhões de registros por segundo
- Modelos de domínio complexos: suporte a referências circulares e estruturas polimórficas
- Sistemas em tempo real: latência abaixo de 1 ms, desserialização zero-copy
-
Consideração de alternativas
- Quando for necessário um formato legível por humanos → JSON/YAML
- Quando for necessário um formato para armazenamento de longo prazo → Parquet
- Para estruturas de dados simples → serde + bincode
Primeiros passos
-
Instalação
-
Exemplo básico de serialização
- Registre a struct com
#[derive(ForyObject)] e use serialize() / deserialize()
- Mantenha a consistência dos dados registrando o ID do tipo
-
Serialização cross-language
- Ative o modo de compatibilidade multilíngue com
compatible(true).xlang(true)
- Suporta registro baseado em ID ou nome (
register_by_namespace, register_by_name)
Tipos suportados
- Tipos primitivos: bool, inteiros, ponto flutuante, String
- Coleções: Vec, HashMap, BTreeMap, HashSet, Option
- Smart pointers: Box, Rc, Arc, RcWeak, ArcWeak, RefCell, Mutex
- Data/hora: tipos do chrono
- Objetos definidos pelo usuário: ForyObject, ForyRow
- Objetos de trait: Box/Rc/Arc, Rc/Arc
Roadmap
-
Disponível no v0.13
- Geração estática de código, formato Row zero-copy, rastreamento de referências circulares, serialização de objetos de trait, modo de compatibilidade de schema
-
Recursos planejados
- serialização cross-language de referências, atualizações parciais de Row
Considerações para produção
- Segurança de thread: após concluir o registro, pode ser compartilhado com
Arc (Send + Sync)
- Tratamento de erros: baseado em
Result, com distinção explícita entre erros como incompatibilidade de tipo e buffer insuficiente
Documentação e comunidade
Conclusão
- O Apache Fory Rust é um framework de serialização de próxima geração que elimina o compromisso entre desempenho, flexibilidade e compatibilidade entre linguagens
- Com automação baseada em macros, suporte a objetos de trait e tratamento de referências circulares, maximiza a eficiência de desenvolvimento
- Pode ser aplicado imediatamente em microsserviços, pipelines de dados e sistemas em tempo real
2 comentários
Esse desempenho faz sentido?
Opiniões do Hacker News
Eu gostaria que houvesse mais foco em melhorar o tooling de tecnologias existentes, como W3C EXI (Binary XML), em vez de criar um novo formato
Ser apenas rápido não basta, e formatos sem ecossistema, como Aeron/SBT, têm dificuldade para se espalhar. XML já tem esse ecossistema
Além disso, ela não representa naturalmente grafos de objetos complexos, como referências compartilhadas ou referências cíclicas
O formato Fory foi projetado desde o início para resolver esses problemas, ao mesmo tempo em que oferece compatibilidade entre linguagens e evolução de esquema
Ou seja, é preferível projetar primeiro a codificação e depois expandi-la de volta para linguagens ou clientes
Tenho dúvidas se o benchmark é justo
Pelo link do código, quando não se trata de uma struct do Fory, o processo de serialização inclui conversões to/from
Nesse processo ocorrem cópias de strings e realocação de arrays
Em sistemas reais, o tonic fornece um buffer de 8 KB, então deve ser mais eficiente do que um simples
Vec::default()Em um Xeon Gold 6136 parece haver um ganho de 10x, mas removendo as conversões to/from e a cópia de
Vec, e pré-alocando um buffer de 8 KB, na prática fica mais perto de 3xO benchmark deveria ser reescrito em um estilo de tower service/codec sem nenhum código específico do Fory
O Fory está usando um writer pool durante os testes
Veja o código relacionado
Acho que, para manter compatibilidade entre linguagens no longo prazo, é necessário um contrato especificado com base em IDL
A abordagem que começa na linguagem e vai para a serialização é conveniente no início, mas com o tempo fica vulnerável a mudanças no runtime da linguagem
Projetos de linguagem única podem se manter simples sem IDL, mas a partir de três linguagens ou mais o IDL passa a funcionar como fonte única da verdade
O Apache Fory pretende adicionar suporte opcional a IDL, para que equipes possam escolher uma abordagem language-first ou schema-first de acordo com sua situação
Tenho curiosidade sobre como mantêm tipos compartilhados entre linguagens sem esquema
Em linguagens tipadas, o esquema é inferido a partir da definição da classe; em linguagens não tipadas, a abordagem é adicionar anotações diretamente no código
Um exemplo em Python pode ser visto aqui
Veja o post relacionado no blog
Gostaria de entender por que eu usaria o Fory em vez de formatos sem desserialização como CapnProto ou Flatbuffers
Se for preciso compressão, basta usar zstd
Ainda assim, o amplo suporte a linguagens e a facilidade de uso do Fory são impressionantes
No Python, eu ainda prefiro dill — porque ele consegue serializar até objetos de código
Link do dill
Veja o código do benchmark
Link com exemplos
O pyfory mostra uma compressão 3x melhor que o cloudpickle e oferece recursos de auditoria de segurança para evitar ataques maliciosos de desserialização
O link do benchmark dava 404, mas encontrei o link correto
É uma pena terem mudado o nome de “Fury” para “Fory”
Fury era um nome perfeito para um framework de serialização rápida
A maioria dos protocolos binários tenta reduzir o tamanho dos dados
O Protobuf usa compressão de inteiros (varint, zigzag)
Se comparar apenas TPS puro, a abordagem de “não fazer nada” de enviar a struct C como está sempre acaba vencendo
Foi apresentada uma tabela comparativa em vários conjuntos de dados
Fico pensando se o limite de 4096 tipos do Fory é suficiente
Veja o código relacionado
Na prática, quase nunca vi casos com mais de 4096 mensagens de protocolo definidas
O link do benchmark de Rust retorna 404
No diretório raiz da documentação não foi possível encontrar o diretório de benchmarks