Os buffers de protocolo fornecem um mecanismo extensível que é neutro em relação à linguagem e à plataforma, para serializar dados estruturados de maneira compatível com versões futuras e versões anteriores. Ele é como JSON, mas é menor e mais rápido e gera vinculações de idioma nativo.
Buffers de protocolo são uma combinação da linguagem de definição (criada nos
arquivos .proto
), do código gerado pelo compilador proto para
a interface com os dados, bibliotecas de execução de linguagens específicas e o formato de serialização para dados
gravados em um arquivo (ou enviados por uma conexão de rede).
Quais problemas os buffers de protocolo resolvem?
Os buffers de protocolo fornecem um formato de serialização para pacotes de dados estruturados e digitados de até alguns megabytes de tamanho. O formato é adequado para tráfego de rede temporário e armazenamento de dados de longo prazo. Os buffers de protocolo podem ser estendidos com novas informações sem invalidar os dados existentes ou exigir que o código seja atualizado.
Os buffers de protocolo são o formato de dados mais usado no Google. Eles são usados extensivamente em comunicações entre servidores, bem como para arquivamento de dados em disco. As mensagens e os serviços do buffer de protocolo são descritos por
arquivos .proto
criados por engenheiros. Veja a seguir um exemplo de message
:
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
}
O compilador proto é invocado no momento da compilação em arquivos .proto
para gerar código
em várias linguagens de programação, que serão abordadas em
Compatibilidade entre linguagens mais adiante neste tópico, para manipular
o buffer de protocolo correspondente. Cada classe gerada contém acessadores simples
para cada campo e métodos para serializar e analisar toda a estrutura
de e para bytes brutos. Veja abaixo um exemplo que usa esses
métodos gerados:
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
Como os buffers de protocolo são amplamente usados em todos os tipos de serviços no Google, os dados neles podem permanecer por algum tempo, e por isso, é essencial manter a compatibilidade com versões anteriores. Os buffers de protocolo permitem o suporte contínuo a mudanças, incluindo a adição de novos campos e a exclusão de campos existentes, para qualquer buffer de protocolo sem interromper os serviços atuais. Para saber mais sobre esse tópico, consulte Como atualizar definições proto sem atualizar o código.
Quais são os benefícios de usar buffers de protocolo?
Os buffers de protocolo são ideais para qualquer situação em que você precise serializar dados estruturados, semelhantes a registros, de maneira neutra em termos de linguagem, de plataforma e extensíveis. Eles são mais usados para definir protocolos de comunicação (junto com o gRPC) e para armazenamento de dados.
Algumas das vantagens de usar buffers de protocolo incluem:
- Armazenamento de dados compacto
- Análise rápida
- Disponibilidade em muitas linguagens de programação
- Funcionalidade otimizada por meio de classes geradas automaticamente
Compatibilidade entre linguagens
As mesmas mensagens podem ser lidas por código escrito em qualquer linguagem de programação
compatível. É possível fazer com que um programa Java em uma plataforma capture dados de um
sistema de software, serialize-o com base em uma definição .proto
e extraia
valores específicos desses dados serializados em um aplicativo Python separado
em execução em outra plataforma.
As seguintes linguagens têm suporte direto no compilador de buffers de protocolo, o protoc:
As linguagens abaixo são aceitas pelo Google, mas o código-fonte dos projetos está nos repositórios do GitHub. O compilador protoc usa plug-ins para as seguintes linguagens:
Outras linguagens não são diretamente compatíveis com o Google, mas com outros projetos do GitHub. Esses idiomas são abordados em Complementos de terceiros para buffers de protocolo.
Suporte entre projetos
É possível usar buffers de protocolo entre projetos definindo tipos message
em
arquivos .proto
que residem fora da base de código de um projeto específico. Se estiver
definindo tipos message
ou enumerações que você acha que vão ser amplamente usados fora
da sua equipe imediata, é possível os colocar em um arquivo próprio sem dependências.
Alguns exemplos de definições de protótipos amplamente usados no Google são
timestamp.proto
e
status.proto
.
Como atualizar definições proto sem atualizar o código
Os produtos de software são compatíveis com versões anteriores, mas é menos
comum que eles sejam compatíveis. Contanto que você siga algumas
práticas simples ao atualizar as definições de .proto
, o código antigo vai ler novas mensagens sem problemas, ignorando todos os campos
recém-adicionados. Para o código antigo, os campos excluídos terão o valor padrão, e os campos repetidos excluídos ficarão vazios. Para informações sobre o que são
campos "repetidos", consulte Sintaxe de definição de buffers de protocolo mais adiante
neste tópico.
O novo código também vai ler as mensagens antigas de forma transparente. Novos campos não estarão presentes nas mensagens antigas. Nesses casos, os buffers de protocolo fornecem um valor padrão razoável.
Quando os buffers de protocolo não são adequados?
Os buffers de protocolo não se encaixam em todos os dados. Especificamente, faça o seguinte:
- Os buffers de protocolo tendem a presumir que mensagens inteiras podem ser carregadas na memória de uma só vez e não são maiores que um gráfico de objetos. Para dados que excedem alguns megabytes, considere uma solução diferente. Ao trabalhar com dados maiores, é possível que você acabe tendo várias cópias dos dados devido às cópias serializadas, o que pode causar picos surpreendentes no uso da memória.
- Quando os buffers de protocolo são serializados, os mesmos dados podem ter muitas serializações binárias diferentes. Não é possível comparar duas mensagens por igualdade sem analisá-las totalmente.
- As mensagens não são compactadas. Embora as mensagens possam ser compactadas ou compactadas com gzip como qualquer outro arquivo, algoritmos de compactação com finalidade específica, como os usados por JPEG e PNG, produzirão arquivos muito menores para dados do tipo apropriado.
- As mensagens de buffer de protocolo são menos eficientes tanto em tamanho quanto velocidade para muitos usos científicos e de engenharia que envolvem grandes matrizes multidimensionais com números de pontos flutuantes. Para esses aplicativos, os FITS e formatos semelhantes têm menos sobrecarga.
- Os buffers de protocolo não são bem aceitos em linguagens não orientadas a objetos, conhecidas pela computação científica, como Fortran e IDL.
- As mensagens do buffer de protocolo não descrevem automaticamente os dados deles, mas têm um esquema totalmente reflexivo que pode ser usado para implementar a autodescrição. Ou seja, não é possível interpretar totalmente um objeto sem acesso
ao arquivo
.proto
correspondente. - Buffers de protocolo não são um padrão formal de nenhuma organização. Por isso, eles não são adequados para uso em ambientes com requisitos legais ou de outros tipos que são criados com base em padrões.
Quem usa buffers de protocolo?
Muitos projetos disponíveis externamente usam buffers de protocolo, incluindo:
Como os buffers de protocolo funcionam?
O diagrama a seguir mostra como usar buffers de protocolo para trabalhar com dados.
Figura 1. Fluxo de trabalho de buffers de protocolo
O código gerado por buffers de protocolo fornece métodos utilitários para recuperar dados de arquivos e streams, extrair valores individuais dos dados, verificar se existem, serializar dados de volta em um arquivo ou stream e outras funções úteis.
As amostras de código a seguir mostram um exemplo desse fluxo em Java. Conforme mostrado
anteriormente, esta é uma definição de .proto
:
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
}
A compilação desse arquivo .proto
cria uma classe Builder
que pode ser usada para
criar novas instâncias, como no código Java a seguir:
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
Em seguida, é possível desserializar dados usando os métodos de buffer de protocolo criados em outras linguagens, como C++:
Person john;
fstream input(argv[1], ios::in | ios::binary);
john.ParseFromIstream(&input);
int id = john.id();
std::string name = john.name();
std::string email = john.email();
Sintaxe de definição de buffers de protocolo
Ao definir arquivos .proto
, é possível especificar que um campo seja optional
ou repeated
(proto2 e proto3) ou singular
(proto3). A opção para definir um campo como required
está ausente no proto3 e não é recomendado no proto2. Para
saber mais sobre isso, consulte "Obrigatório para sempre" em Como especificar regras de
campo.
Depois de definir a opcionalidade/repetição de um campo, especifique o tipo de dados. Os buffers de protocolo aceitam os tipos de dados primitivos comuns, como números inteiros, booleanos e flutuantes. Para ver a lista completa, consulte Tipos de valores de escalonamento.
Um campo também pode ser de:
- Um tipo
message
, para que você possa aninhar partes da definição, como para repetir conjuntos de dados. - Um tipo
enum
, para que você possa especificar um conjunto de valores. - Um tipo
oneof
, que pode ser usado quando uma mensagem tem muitos campos opcionais e, no máximo, um campo será definido ao mesmo tempo. - Um tipo
map
para adicionar pares de chave-valor à definição.
No proto2, as mensagens podem permitir que extensões definam campos fora da própria mensagem. Por exemplo, o esquema de mensagens internas da biblioteca protobuf permite extensões para opções personalizadas e específicas do uso.
Para mais informações sobre as opções disponíveis, consulte o guia de linguagem de proto2 ou proto3.
Depois de definir a opcionalidade e o tipo de campo, atribua um número de campo. Os números de campo não podem ser reutilizados ou reutilizados. Se você excluir um campo, reserve o número dele para evitar que alguém o reutilize acidentalmente.
Suporte adicional a tipos de dados
Os buffers de protocolo aceitam muitos tipos de valores escalares, incluindo números inteiros que usam codificação de comprimento variável e tamanhos fixos. Também é possível criar seus próprios tipos de dados compostos definindo mensagens que, por sua vez, podem ser atribuídas a um campo. Além dos tipos de valor simples e composto, vários tipos comuns são publicados.
Tipos comuns
Duration
é um período assinado de duração fixa, como 42 segundos.Timestamp
é um ponto no tempo independente de fusos horários ou agendas, como 2017-01-15T01:30:15.01Z.Interval
é um intervalo de tempo independente do fuso horário ou da agenda, como 2017-01-15T01:30:15.01Z - 2017-01-16T02:30:15.01Z.Date
é uma data inteira, como 19/09/2025.DayOfWeek
é um dia da semana, como segunda-feira.TimeOfDay
é uma hora do dia, como 10:42:23.LatLng
é um par de latitude/longitude, como 37,386051 e -122,083855.Money
é um valor com o tipo de moeda, como 42 BRL.PostalAddress
é um endereço postal, como 1600 Amphitheatre Parkway, Mountain View, CA 94043 EUA.Color
é uma cor no espaço de cores RGBA.Month
é um mês do ano, como abril.
Filosofia de código aberto de buffers de protocolo
Os buffers de protocolo foram de código aberto em 2008 como uma forma de fornecer aos desenvolvedores fora do Google os mesmos benefícios que temos para eles internamente. Apoiamos a comunidade de código aberto com atualizações regulares na linguagem à medida que fazemos essas mudanças para atender aos requisitos internos. Embora aceitemos solicitações de envio selecionadas de desenvolvedores externos, nem sempre podemos priorizar solicitações de recursos e correções de bugs que não estão em conformidade com as necessidades específicas do Google.
Outros recursos
- GitHubs de buffers de protocolo (link em inglês)