Protobuf: estructurar el código con Protocol Buffers

Una buena estructuración de los datos es básica a la hora de desarrollar programas y páginas web. Si los datos de un proyecto están bien estructurados, el resto de aplicaciones podrán leerlos de manera fácil y precisa. En Internet, esto es especialmente importante para los motores de búsqueda basados en texto, como Google, Bing o Yahoo, que pueden interpretar el contenido de las páginas web de manera óptima gracias al uso de unos marcadores adecuados y bien estructurados.

En general, conviene estructurar los datos en el desarrollo de software en particular si los programas o servicios han de intercambiar los datos a través de interfaces y es necesario procesarlos a gran velocidad, tanto en las aplicaciones web como de escritorio. En los siguientes párrafos, te contamos qué puede aportar el formato de serialización Protocol Buffers (Protobuf) a este proceso y en qué se diferencia este sistema de estructuración de JSONP, la alternativa más conocida.

¿Qué es Protocol Buffers?

Protocol Buffers (abreviado como Protobuf) es un formato de intercambio de datos desarrollado originalmente para uso interno en Google y lanzado al gran público por la empresa en 2008 como proyecto de código abierto (en parte, con licencia Apache 2.0). Este formato binario permite a las aplicaciones almacenar e intercambiar datos estructurados fácilmente, incluso si los programas están compilados en diferentes lenguajes. Entre los lenguajes de programación compatibles, se incluyen los siguientes:

  • C#
  • C++
  • Go
  • Objective-C
  • Java
  • Python
  • Ruby

Protobuf se utiliza en combinación con, entre otros, HTTP y RPC (Remote Procedure Call o llamada a procedimiento remoto) para llevar a cabo el proceso de comunicación cliente-servidor local y remota, en particular para describir las interfaces necesarias para ello. La estructura del protocolo también se denomina gRPC.

Para mostrar este video, se requieren cookies de terceros. Puede acceder y cambiar sus ajustes de cookies aquí.

¿Qué ventajas ofrece Protocol Buffers de Google?

Google se centró especialmente en dos factores durante el desarrollo de Protobuf: la simplicidad y el rendimiento. Se pretendía que este formato, que solo se utilizaba internamente en la propia empresa en aquel momento, sustituyera al formato XML, que presentaba características similares. Actualmente, Protobuf también compite con otras soluciones, como JSON(P) o FlatBuffers. Si analizamos los elementos y puntos fuertes de este sistema de estructuración y los comparamos con las demás opciones, Protocol Buffers se revela como el formato más adecuado para muchos proyectos por los siguientes motivos:

Esquemas claros y multiaplicación

La clave del éxito de cualquier aplicación es tener un sistema de base de datos bien organizado. Por ello, se suele prestar mucha atención a estructurarlo correctamente durante el desarrollo del software, incluidos los datos que contiene. Sin embargo, en cuanto los datos se envían a un servicio externo, las estructuras subyacentes se acaban perdiendo. Mediante Protobuf, como los datos se codifican una sola vez, nos aseguramos de que el proyecto los reenvía manteniendo la estructura original y sin romperla.

Compatibilidad con versiones anteriores y posteriores

Utilizar Protobuf nos ahorra la molestia de realizar comprobaciones de versión, lo que suele asociarse con el código “sucio” (es decir, código mal organizado y difícil de leer). Para mantener la compatibilidad, tanto con las versiones anteriores, como con las nuevas, Protocol Buffers utiliza campos numerados que sirven como puntos de referencia para los servicios que acceden a ellas. De esta manera, a la hora de publicar nuevas características y funciones, no siempre es necesario adaptar todo el código.

Flexibilidad y comodidad

Al codificar con Protobuf, se recurre automáticamente a modificadores (de tipo obligatorio, opcional o repetido) que facilitan considerablemente el trabajo de programación. Este sistema permite establecer la forma de la estructura de los datos a nivel de esquema, de manera que los datos de implementación de las clases utilizadas para los diferentes lenguajes de programación se regulan automáticamente. También es posible cambiar el estado en cualquier momento, por ejemplo, de “obligatorio” a “opcional”. Asimismo, Protobuf permite regular la transferencia de las estructuras de datos: la codificación de las estructuras genéricas de solicitud y respuesta garantiza que la transferencia de datos entre varios servicios sea fácil, flexible y segura.

Menos código repetitivo

El código repetitivo (en inglés, boilerplate code) tiene un papel más o menos decisivo en la programación, dependiendo del tipo y la complejidad del proyecto. Básicamente, se trata de bloques de código reutilizables que se deben implementar muchas veces en el programa y que, por lo general, se introducen casi sin alteraciones. Por ejemplo, este tipo de código suele utilizarse para preparar el uso de funciones de bibliotecas. Aunque el código repetitivo es especialmente habitual en los lenguajes JavaScript, PHP, HTML y CSS, este no ayuda en absoluto al rendimiento de las aplicaciones web. Un esquema adecuado de Procotol Buffers ayuda a reducir el código repetitivo y, por lo tanto, a mejorar el rendimiento del proyecto a largo plazo.

Fácil interoperabilidad entre lenguajes

En el estándar actual, no es lo más habitual escribir las aplicaciones en un solo lenguaje, sino combinar módulos o partes de programas escritos en los lenguajes más diversos. Protobuf simplifica considerablemente la interacción entre todos los elementos del código: si se añaden elementos nuevos, cuyo lenguaje difiere del lenguaje actual del proyecto, el sistema simplemente los traduce al lenguaje de destino correspondiente utilizando el generador de código apropiado, por lo que el esfuerzo que debemos dedicarle se reduce al mínimo. Obviamente, el requisito previo es que los lenguajes que utilicemos sean compatibles con Protobuf ―ya sea de forma predeterminada, como los que ya hemos mencionado, o mediante un complemento de otro proveedor.

Protobuf vs. JSON: comparativa de ambos formatos

Cuando desarrolló Protocol Buffers, Google pretendía convertirlo en una alternativa a XML (del inglés extensible markup language, o lenguaje de marcado extensible). Lo cierto es que logró superarlo de muchas maneras: estructurar los datos con Protobuf no solo tiende a ser más fácil, sino que también garantiza obtener una estructura de datos entre tres y diez veces más pequeña y entre 20 y 100 veces más rápida que una estructura XML comparable, según afirma la propia compañía.

Protocol Buffers también suele compararse con el lenguaje de marcado JSON (JavaScript Object Notation) de JavaScript, aunque cabe mencionar que ambas tecnologías fueron diseñadas con objetivos diferentes: JSON es un formato de texto sencillo basado en JavaScript para intercambiar mensajes, siendo compatible con prácticamente todos los lenguajes de programación habituales. La funcionalidad de Protobuf abarca más que el formato de texto, porque esta tecnología de Google también ofrece varias reglas y herramientas para definir e intercambiar los mensajes. Además, Protobuf también supera a JSON en términos de rendimiento si analizamos el envío de los mensajes en general. En cualquier caso, ambos sistemas de estructuración tienen sus ventajas e inconvenientes, como verás en la siguiente tabla comparativa:

  Protobuf JSON
Desarrollador Google Douglas Crockford
Función Formato de marcado para datos estructurados (almacenamiento y transmisión) y biblioteca Formato de marcado para datos estructurados (almacenamiento y transmisión)
Formato binario No
Estandarización No
Legible para el ser humano En parte
Comunidad/documentación Comunidad pequeña; manuales en línea extensibles Comunidad enorme, buena documentación oficial y diversos tutoriales en línea, etc.

Como habrás deducido, si necesitas un formato de serialización bien documentado que almacene y transfiera los datos estructurados de forma legible para el ser humano, deberás recurrir a JSON en lugar de Protocol Buffers, especialmente en los casos en que la parte del lado del servidor de la aplicación esté escrita en JavaScript y una gran parte de los datos sea procesada directamente por los navegadores de manera predeterminada. Sin embargo, cuando haya que favorecer la flexibilidad y el rendimiento de la estructura de los datos, Protobuf probablemente sea la solución más adecuada y eficiente.

Tutorial: ejemplo práctico de Protobuf con Java

El uso de Protocol Buffers puede marcar la diferencia en muchos proyectos de software, pero, como suele ser el caso, primero conviene conocer y saber aplicar las características especiales y los trucos sintácticos de esta tecnología de serialización. En el tutorial que figura a continuación, te enseñaremos a dar tus primeros pasos con Protobuf y podrás hacerte una idea de cómo se estructura la sintaxis y el intercambio de mensajes en este sistema: desde la definición de tu propio formato mediante un archivo .proto hasta la compilación de las estructuras de Protocol Buffers. Como ejemplo, te proponemos una aplicación sencilla: una agenda de contactos en Java, que puede leer los datos de contacto de un archivo y escribirlos en otro. A cada registro de la agenda se le asignan los parámetros “nombre”, “ID”, “dirección de correo electrónico” y “número de teléfono”.

Definir nuestro propio formato de datos en el archivo .proto

En primer lugar, debes describir todas las estructuras de datos que desees implementar con Protocol Buffers en el archivo .proto, el archivo de configuración estándar de este formato de serialización. Para cada estructura que quieras serializar en este archivo ―es decir, introducir siguiendo una estructura― no tienes más que añadir un mensaje (en inglés, message). Después, debes especificar los nombres y tipos para cada campo de este mensaje y añadir el modificador o modificadores que quieras. Es obligatorio asignar un modificador a cada campo.

Para nuestra agenda de contactos en Java, esta podría ser una posible asignación de las estructuras de datos en el archivo .proto:

syntax = "proto3";

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

La sintaxis de Protocol Buffers recuerda mucho a la de C++ o a la de Java: en primer lugar, se especifica la correspondiente versión de Protobuf, en este caso, proto3, seguida de la descripción del paquete de software cuyos datos se desea estructurar, que siempre incluye un nombre único (aquí, tutorial). En nuestro ejemplo de código, se añaden también las dos opciones específicas de Java java_package (paquete de Java en el que se guardan las clases generadas) y java_outer_classname (que define el nombre bajo el cual se designan las clases).

A continuación, figuran los mensajes de Protobuf, que pueden estar compuestos por un número ilimitado de campos. Entre ellos, disponemos de los tipos de datos típicos como bool, int32, float, double o string, algunos de los cuales también se utilizan en el ejemplo. Como ya hemos mencionado, a cada campo se le debe asignar al menos uno de los siguientes tres modificadores:

  • required: indica que es obligatorio asignar un valor al campo. En caso contrario, el estado del mensaje es uninitialized, es decir, sin inicializar o sin enviar.
  • optional: en los campos opcionales, es posible indicar un valor, aunque no es necesario. Si no hay ningún valor, se utiliza uno definido como estándar. En el código de nuestro tutorial, por ejemplo, se introduce el valor predeterminado HOME (número de teléfono fijo) para el tipo de número de teléfono.
  • repeated: los campos con este modificador pueden repetirse un número indefinido de veces (incluso ninguna).

En el foro para desarrolladores de Google, encontrarás información detallada sobre cómo definir tu propio formato de datos con Protocol Buffers.

Compilar nuestro propio esquema de Protobuf

Una vez hayas definido las estructuras de datos en el archivo .proto, debes generar las clases necesarias para la lectura y la escritura de los mensajes de Protobuf. Para ello, aplica el compilador de Protocol Buffers (Protoc) al archivo de configuración. Si aún no lo has instalado, puedes descargar la versión actualizada en el repositorio oficial de GitHub. Descomprime el archivo .zip en la carpeta que quieras y, a continuación, inicia el compilador (ubicado en la carpeta “bin”) haciendo doble clic.

Nota

Asegúrate de descargar la edición correcta del compilador de Protobuf, ya que Protoc está disponible para arquitecturas de 32 y 64 bits (Windows, Linux o macOS.

Finalmente, debes especificar lo siguiente:

  • Directorio fuente en el que se encuentra el código del programa (en nuestro ejemplo, la carpeta “SRC_DIR”)
  • Directorio de destino en el que se guardará el código generado (aquí, la carpeta “DST_DIR”)
  • Ruta del archivo .proto

Como tu intención es generar clases Java, utiliza también la opción --java_out (también hay opciones similares disponibles para el resto de lenguajes compatibles). El comando de compilación completo se vería así:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
Consejo

Google proporciona un tutorial detallado de Protobuf con Java, que también explica cómo transferir mensajes a través de Protocol Buffers (lectura/escritura), en un apartado dedicado en Google Developers, la página para desarrolladores de proyectos de software que ofrece el gigante informático. Allí también figuran las instrucciones para trabajar con el resto de lenguajes compatibles, como C++, Go o Python.