In conversation with ProtocolBuffers

  • It’s language-agnostic.
  • Using proto-buffs, Code can be generated for any language.
  • Data is binary and efficiently serialised.
  • It’s easy and convenient for transporting a lot of data.
  • It’s allows for easy API evolution using Rules.
  • Supported scalar type NUMBERS are as following: int32, int64, double, float, sint32, sint64, uint32, uint64, fixed32, fixed64, sfixed32, sfixed64. Pl note here that, fixed32 uses 4 bytes constantly, whereas int32 and sint32 uses variable encoding, wherein, if it can use less space, it shall use for small values.
  • Supported scalar type BOOLEAN are: It’s represented as ‘bool’ in protobuf.
  • Supported scalar type STRING are: It’s represented as ‘string’ in protobuf. It should always contain the UTF-8 encoded or 7-bit-ASCII text.
  • Supported scalar type BYTES are: It’s represented as ‘bytes’ in protobuf. It represents sequence of the byte-array. e.g. → We can use this type to represent an Image.
  • Supported LIST type: It’s represented as ‘repeated’ in protobuf. It represents list of the corresponding scalar-type. e.g. → We can use this type to represent list-of-phone-numbers which Human holds.
  • Supported Enum type: It’s represented as ‘enum’ in protobuf. It represents a variable, whose all the values are pre-known. e.g. → We can use this type to represent color of the eye of the Human.
  • Here, we have a “Greeting” message.
  • We get a predefined “GreetingRequest” message and similarly a “GreetingResponse”.
  • At very bottom, we have a “GreetingService” that accepts GreetingRequest and returns the GreetingResponse.
syntax = "proto3";/*
Human represents a User of our system.
*/
message Human {
int32 age = 1;
string first_name = 2;
string last_name = 3;
bytes small_picture = 4;
bool is_profile_verified = 5;
float height = 6;
repeated string phone_numbers = 7;
enum EyeColor {
UNKNOWN_COLOR = 0;
GREEN = 1;
BROWN = 2;
BLACK = 3;
}
EyeColor eyecolor = 8; my.Date.Date birthday = 9; message Address {
string address_line_1 = 0;
string address_line_2 = 1;
string zip_code = 2 ;
string city = 3;
string country = 4;
}
repeated Address addressOfHuman = 10;
}
  • We are creating the DATE ‘message’ in an separate file called as date.proto and then importing this DATE type of message to the aforesaid ‘Human’ message.
  • We generally define the packages, in which our ‘protocol-buffer-messages’ lives. After the code gets compiled, it shall be placed at the indicated package.
syntax = "proto3";package my.Date;message Date {
// Year of date. Must be from 1 to 9999, or 0 if specifying a date without
// a year.
int32 year = 1;

// Month of year. Must be from 1 to 12.
int32 month = 2;

// Day of month. Must be from 1 to 31 and valid for the year and month, or 0
// if specifying a year/month where the day is not significant.
int32 day = 3;
}
map<string, CustomValue> = 2
syntax = "proto3";import "google/protobuf/timestamp.proto";package example.simple;message SimpleMessage {
int32 id = 1;
google.protobuf.Timestamp created_date = 2;
}
  • Range of values that TAG can take is: {1 TO 53,68,70,911}.
  • The values 19,000 TO 19,999 can’t be used, as these are reserved by Google.
  • V. V. Imp. Point→ Tag-numbers 1 to 15 uses ONE byte of space, post the message is encoded. So, preferably use these tag-numbers for frequently populated fields.
  • Tag-numbers from 16 to 2047 uses TWO bytes of space.
syntax = "proto3";package example.simple;message SimpleMessage {
int32 id = 1;
bool is_simple = 2;
string name = 3;
repeated int32 sample_list = 4;
}
public static void main(String[] args) throws IOException {
System.out.println("Write message to file..");
SimpleMessage.Builder simpleMessageBuilder = SimpleMessage.newBuilder();
simpleMessageBuilder
.setId(4567)
.setIsSimple(true)
.setName("Honesty is the best policy.")
.addAllSampleList(Arrays.asList(1,2,3));
SimpleMessage simpleMessage = simpleMessageBuilder.build();
FileOutputStream fileOutputStream = new
FileOutputStream("simpleMessage_bin");
simpleMessage.writeTo(fileOutputStream);
}
public static void main(String[] args) throws IOException {
System.out.println("Reading message now...!");
FileInputStream fileInputStream = new FileInputStream("simpleMessage_bin");
SimpleMessage messageAsReadFromInputStream = SimpleMessage.parseFrom(fileInputStream);
System.out.println(messageAsReadFromInputStream);
}
syntax = "proto3";
package example.enumerations;
message WeekDay {
int32 id = 1;
DayOfTheWeek day_of_the_week = 2;
}
enum DayOfTheWeek {
UNKNOWN = 0;
MONDAY = 1;
TUESDAY = 2;
WEDNESDAY = 3;
THURSDAY = 4;
FRIDAY = 5;
SATURDAY = 6;
SUNDAY = 7;
}
WeekDay.Builder weekDayBuilder = WeekDay.newBuilder();
weekDayBuilder
.setId(1)
.setDayOfTheWeek(DayOfTheWeek.SATURDAY);

WeekDay weekDayBuiltIs = weekDayBuilder.build();
System.out.println(weekDayBuiltIs);
}
option java_package = "com.example.options";
option java_multiple_files = true;
  • We might have a scenario, where one application is writing the Message Object in enhanced format and some application is still reading the old format of the ‘Message’.
  • Other scenario is vice-versa.
  • Numeric tags for any existing fields must not be changed. Now, when we add newer fields, older-code would just ignore those newer fields.
  • Vice-versa, if newer code reads any Message(which was being written using older-code), it would initiate the differing fields with default values.
  • The renaming of the fields is very simple, until and unless tag-numbers are not being changed for renamed fields. Only Tag-Number is important for the Protobuf.
  • Fields in the .proto files can be deleted as long as the tag-numbers(belonging to the deleted fields) are not being re-used by the newly added fields. To avoid this by happening accidentally, it’s better to still keep the deleted fields, but we can suffix them with _OBSOLETE OR another option is by reserving the deleted fields :-
  • A Service is a set of end-points exposed by server.
  • Below is how services(i.e. RPC call) can be made to sit on top of Messages.
  • The SearchRequest takes in ‘person_id’ in request and returns SearchResponse containing the (‘person_id’ and ‘person_name’).
  • For every RPC call, we need to define a request-message and a response-message. See below, we have defined a “SearchService” as follows :-
  • First, Protobuf formats are more efficient in terms of the payload-size. We know all of this data has to travel over the network eventually and it can help us to save the lot of Bandwidth.
  • Second, parsing JSON is more CPU-intensive (since its human-readable) as compared to the protobuf parsing (since its little more closer to the way, memory understands the data). Thus, using Protobuf means faster and more efficient communications and hence it comes as first choice for mobile devices which have slower CPU.
{
"age":29,
"firstname": "Aditya",
"lastname":"Goel"
}
message Person {
int32 age = 1;
string first_name = 2;
string last_name = 3;
}
  • The API can be defined in a simple and easy manner.
  • The definition is different from implementation.
  • Lot of boiler-plate code can be auto-generated by proto-compiler.
  • Since the payload is binary, its lot more efficient to receive/send on network and de-serialize/serialize on CPU.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store