HomeAuthorsContact
gRPC - Présentation

gRPC - Présentation

By Frédéric Schmidt
July 12, 2020
3 min read

gRPC, did you say gRPC?

Presentation of the gRPC protocol and the “buffer protocol” in English protocol buffers.

The Protocol buffer defines the data structures that will be exchanged between customers and the service. It also allows you to define interface contracts. All in a simple text file with the .proto extension. I went a little further into the details of it in a future section.

gRPC is developed by the company Google it is a Framework allowing easy inter-platform communication in distributed systems.

RPC for Remote Procedure Call.

gRPC uses the HTTP/2 protocol and the Protocol Buffer for describing interface contracts.

First Step

In a client application with gRPC, it calls directly to a method in an application that is on another server in a completely transparent way for it. The client application is thinking of making a local call to this method.

One of the big advantages is the ease in creating distributed applications. As in RPC technology, we need to define all the methods, input and return parameters, we are talking about interface contract and messages.

Server side

On the server side, it implements the interface contract and makes it available through a gRPC application that will respond to requests from different clients.

Client side

On the customer side, it only has the interface contract, we will talk about stub to make calls to the different methods.

Availability

The server part, like the client part, can be found in different environments, written in different languages that have a gRPC implementation and be able to discuss easily between them. One can timagine a gRPC server written in C# which dialogue with clients written in GO, Java, Ruby etc…

gRPC being a Framework developed by Google, we will find in the APIs of Google functions allowing the creation and consumption of gRPC service.

Architecture

Presentation of exchanges between clients and service .

img

Reference https://grpc.io/

Buffer Protocol or Protocol Buffers

Introduction

Base gRPC uses the Buffer Protocol. I will use the term English and therefore Protocol Buffers to talk about this technology which will be easier for understanding.

Protocol Buffers was created by Google in 2001. It is a serialization format for data structured by a description language. Originally developed for C++, JAVA and Python, it is available on other platforms.

Thanks to its description language it will allow remote applications to be able to exchange data between them. This data description language will make it possible to define the different data structures, messages that will be exchanged between the customer and the service.

It will also make it possible to define the service contract that will be available to customers, all of it in a text file with the extension *.proto*.

Principle

It will therefore be enough for the developer to define in a proto file all messages and service contracts. Then using a transpilator to translate into a target development language that will be used for invoked by the client or service to dialogue and transmit data to each other.

The messages will then be serialized in a compressed binary format. Keeping backward and backward compatibility. But it will be impossible to recover the names of properties, types, etc… because it is not self-descriptive.

Examples

Example of a proto file defining a message and its generation in GO language.

syntax = "proto3";

option go_package = "fredericschmidt.fr/protobuf/sample1";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//  protoc-gen-go v1.25.0-devel
//  protoc        v3.12.3
// source: sample1.proto

package sample1

import (
    proto "github.com/golang/protobuf/proto"
    protoreflect "google.golang.org/protobuf/reflect/protoreflect"
    protoimpl "google.golang.org/protobuf/runtime/protoimpl"
    reflect "reflect"
    sync "sync"
)

const (
    // Verify that this generated code is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
    // Verify that runtime/protoimpl is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4

type SearchRequest struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Query         string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
    PageNumber    int32  `protobuf:"varint,2,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"`
    ResultPerPage int32 `protobuf:"varint,3,opt,name=result_per_page,json=resultPerPage,proto3"
json:"result_per_page,omitempty"`
}

func (x *SearchRequest) Reset() {
    *x = SearchRequest{}
    if protoimpl.UnsafeEnabled {
        mi := &file_sample1_proto_msgTypes[0]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *SearchRequest) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*SearchRequest) ProtoMessage() {}

func (x *SearchRequest) ProtoReflect() protoreflect.Message {
    mi := &file_sample1_proto_msgTypes[0]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
func (*SearchRequest) Descriptor() ([]byte, []int) {
    return file_sample1_proto_rawDescGZIP(), []int{0}
}

func (x *SearchRequest) GetQuery() string {
    if x != nil {
        return x.Query
    }
    return ""
}

func (x *SearchRequest) GetPageNumber() int32 {
    if x != nil {
        return x.PageNumber
    }
    return 0
}

func (x *SearchRequest) GetResultPerPage() int32 {
    if x != nil {
        return x.ResultPerPage
    }
    return 0
}

var File_sample1_proto protoreflect.FileDescriptor

var file_sample1_proto_rawDesc = []byte{
    0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
    0x6e, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
    0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
    0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6e,
    0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x61, 0x67,
    0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
    0x52, 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x50, 0x65, 0x72, 0x50, 0x61, 0x67, 0x65, 0x42,
    0x25, 0x5a, 0x23, 0x66, 0x72, 0x65, 0x64, 0x65, 0x72, 0x69, 0x63, 0x73, 0x63, 0x68, 0x6d, 0x69,
    0x64, 0x74, 0x2e, 0x66, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73,
    0x61, 0x6d, 0x70, 0x6c, 0x65, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
    file_sample1_proto_rawDescOnce sync.Once
    file_sample1_proto_rawDescData = file_sample1_proto_rawDesc
)

func file_sample1_proto_rawDescGZIP() []byte {
    file_sample1_proto_rawDescOnce.Do(func() {
        file_sample1_proto_rawDescData = protoimpl.X.CompressGZIP(file_sample1_proto_rawDescData)
    })
    return file_sample1_proto_rawDescData
}

var file_sample1_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_sample1_proto_goTypes = []interface{}{
    (*SearchRequest)(nil), // 0: SearchRequest
}
var file_sample1_proto_depIdxs = []int32{
    0, // [0:0] is the sub-list for method output_type
    0, // [0:0] is the sub-list for method input_type
    0, // [0:0] is the sub-list for extension type_name
    0, // [0:0] is the sub-list for extension extendee
    0, // [0:0] is the sub-list for field type_name
}

func init() { file_sample1_proto_init() }
func file_sample1_proto_init() {
    if File_sample1_proto != nil {
        return
    }
    if !protoimpl.UnsafeEnabled {
        file_sample1_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*SearchRequest); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
    }
    type x struct{}
    out := protoimpl.TypeBuilder{
        File: protoimpl.DescBuilder{
            GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
            RawDescriptor: file_sample1_proto_rawDesc,
            NumEnums:      0,
            NumMessages:   1,
            NumExtensions: 0,
            NumServices:   0,
        },
        GoTypes:           file_sample1_proto_goTypes,
        DependencyIndexes: file_sample1_proto_depIdxs,
        MessageInfos:      file_sample1_proto_msgTypes,
    }.Build()
    File_sample1_proto = out.File
    file_sample1_proto_rawDesc = nil
    file_sample1_proto_goTypes = nil
    file_sample1_proto_depIdxs = nil
}

Example of proto file defining a service contract and messages that will be exchanged at the call of the different methods and of course the source in GO language after transpilation.

syntax = "proto3";

option go_package = "fredericschmidt.fr/protobuf/sample2";

service Greeter {
    rpc SayHello(HelloRequest) returns (HelloReply) {}
}

message HelloReply {
    string message = 1;
}

message HelloRequest {
    string message = 1;
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//  protoc-gen-go v1.25.0-devel
//  protoc        v3.12.3
// source: sample2.proto

package sample2

import (
    proto "github.com/golang/protobuf/proto"
    protoreflect "google.golang.org/protobuf/reflect/protoreflect"
    protoimpl "google.golang.org/protobuf/runtime/protoimpl"
    reflect "reflect"
    sync "sync"
)

const (
    // Verify that this generated code is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
    // Verify that runtime/protoimpl is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4

type HelloReply struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}

func (x *HelloReply) Reset() {
    *x = HelloReply{}
    if protoimpl.UnsafeEnabled {
        mi := &file_sample2_proto_msgTypes[0]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *HelloReply) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*HelloReply) ProtoMessage() {}

func (x *HelloReply) ProtoReflect() protoreflect.Message {
    mi := &file_sample2_proto_msgTypes[0]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
func (*HelloReply) Descriptor() ([]byte, []int) {
    return file_sample2_proto_rawDescGZIP(), []int{0}
}

func (x *HelloReply) GetMessage() string {
    if x != nil {
        return x.Message
    }
    return ""
}

type HelloRequest struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}

func (x *HelloRequest) Reset() {
    *x = HelloRequest{}
    if protoimpl.UnsafeEnabled {
        mi := &file_sample2_proto_msgTypes[1]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *HelloRequest) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*HelloRequest) ProtoMessage() {}

func (x *HelloRequest) ProtoReflect() protoreflect.Message {
    mi := &file_sample2_proto_msgTypes[1]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
func (*HelloRequest) Descriptor() ([]byte, []int) {
    return file_sample2_proto_rawDescGZIP(), []int{1}
}

func (x *HelloRequest) GetMessage() string {
    if x != nil {
        return x.Message
    }
    return ""
}

var File_sample2_proto protoreflect.FileDescriptor

var file_sample2_proto_rawDesc = []byte{
    0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
    0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a,
    0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
    0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
    0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
    0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
    0x65, 0x32, 0x33, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x08,
    0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
    0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,
    0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x66, 0x72, 0x65, 0x64, 0x65, 0x72,
    0x69, 0x63, 0x73, 0x63, 0x68, 0x6d, 0x69, 0x64, 0x74, 0x2e, 0x66, 0x72, 0x2f, 0x70, 0x72, 0x6f,
    0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x32, 0x62, 0x06, 0x70,
    0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
    file_sample2_proto_rawDescOnce sync.Once
    file_sample2_proto_rawDescData = file_sample2_proto_rawDesc
)

func file_sample2_proto_rawDescGZIP() []byte {
    file_sample2_proto_rawDescOnce.Do(func() {
        file_sample2_proto_rawDescData = protoimpl.X.CompressGZIP(file_sample2_proto_rawDescData)
    })
    return file_sample2_proto_rawDescData
}

var file_sample2_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_sample2_proto_goTypes = []interface{}{
    (*HelloReply)(nil),   // 0: HelloReply
    (*HelloRequest)(nil), // 1: HelloRequest
}
var file_sample2_proto_depIdxs = []int32{
    1, // 0: Greeter.SayHello:input_type -> HelloRequest
    0, // 1: Greeter.SayHello:output_type -> HelloReply
    1, // [1:2] is the sub-list for method output_type
    0, // [0:1] is the sub-list for method input_type
    0, // [0:0] is the sub-list for extension type_name
    0, // [0:0] is the sub-list for extension extendee
    0, // [0:0] is the sub-list for field type_name
}

func init() { file_sample2_proto_init() }
func file_sample2_proto_init() {
    if File_sample2_proto != nil {
        return
    }
    if !protoimpl.UnsafeEnabled {
        file_sample2_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*HelloReply); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_sample2_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*HelloRequest); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
    }
    type x struct{}
    out := protoimpl.TypeBuilder{
        File: protoimpl.DescBuilder{
            GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
            RawDescriptor: file_sample2_proto_rawDesc,
            NumEnums:      0,
            NumMessages:   2,
            NumExtensions: 0,
            NumServices:   1,
        },
        GoTypes:           file_sample2_proto_goTypes,
        DependencyIndexes: file_sample2_proto_depIdxs,
        MessageInfos:      file_sample2_proto_msgTypes,
    }.Build()
    File_sample2_proto = out.File
    file_sample2_proto_rawDesc = nil
    file_sample2_proto_goTypes = nil
    file_sample2_proto_depIdxs = nil
}

The proto file examples are in the version as you will see in its first instruction.

I won’t go into the details of the protocol buffers because it would be outside the scope and the main topic of this post.

I won’t go into the details of the GO language either. I’m writing about that language.

Tools

For the transpilation of the proto file to the target development language I used the protoc utility which is available on the Google site regarding the protocol buffers.

This tool is not generated in GO language directly you have to use another utility also develop, but not that, by Google that will be called by protoc when generating.

This utility calls protoc-gen-go for the one developed by Google.

Summary

We have just made our first foray into the gRPC domain of Google* and also very, very lightly into the Protocol buffers.

The next post will focus on the Core of gRPC, with a concrete example.

References

Here are some references on the subject gRPC and Protocol Buffers from Google.

gRPC Official Site

Protocol Buffers Official Site

Transpilator GitHub

GitHub from Google’s extension to transpiler in GO language


Tags

#Framework#protocol#grpc#rpc
Frédéric Schmidt

Frédéric Schmidt

Software Architect

I'm Software Developer & Architect. I'm more than 20 years experiences in differents IT service companies and software editors. I like studies some new technologies and share this passion altroughts my technical blog.

Expertise

Architecture
Development
Modelisation
Technical writer

Social Media

githublinkedin

Related Posts

gRPC - Démonstration
Software Architecture
gRPC - Démonstration
May 21, 2020
5 min
© 2022, All Rights Reserved.

Quick Links

Contact Us

About me

Social Media