gRPC GoLang Server
What Is gRPC?
gRPC is a robust open-source RPC (Remote Procedure Call) framework used to build scalable and fast APIs. It allows the client and server applications to communicate transparently and develop connected systems. Many leading tech firms have adopted gRPC, such as Google, Netflix, Square, IBM, Cisco, & Dropbox. This framework relies on HTTP/2, protocol buffers, and other modern technology stacks to ensure maximum API security, performance, and scalability.
Basic gRPC Concepts
Protocol Buffers
Protocol buffers, or Protobuf, is Google’s serialization/deserialization protocol that enables the easy definition of services and auto-generation of client libraries. gRPC uses this protocol as their Interface Definition Language (IDL) and serialization toolset. Its current version is proto3, which has the latest features and is easier to use.
gRPC services and messages between clients and servers are defined in proto files. The Protobuf compiler, protoc, generates client and server code that loads the .proto file into the memory at runtime and uses the in-memory schema to serialize/deserialize the binary message. After code generation, each message is exchanged between the client and remote service.
The entire flow shows that Protobuf offers some great benefits over JSON and XML. Parsing with Protobuf requires fewer CPU resources since data is converted into a binary format, and encoded messages are lighter in size. So, messages are exchanged faster, even in machines with a slower CPU, such as mobile devices.
Streaming
Streaming is another key concept of gRPC, where many processes can take place in a single request. The multiplexing capability (sending multiple responses or receiving multiple requests together over a single TCP connection) of HTTP/2 makes it possible. Here are the main types of streaming:
- Server-streaming RPCs – The client sends a single request to the server and receives back a stream of - data sequences. The sequence is preserved, and server messages continuously stream until there are -no messages left.
- Client-streaming RPCs – The client sends a stream of data sequences to the server, which then processes and returns a single response to the client. Once again, gRPC guarantees message sequencing within an independent RPC call.
- Bidirectional-streaming RPCs – It is two-way streaming where both client and server sends a sequence of messages to each other. Both streams operate independently; thus, they can transmit messages in any sequence. The sequence of messages in each stream is preserved
HTTP/2
gRPC is developed on HTTP/2, which was published in 2015 to overcome the HTTP/1.1 limitations. While it is compatible with HTTP/1.1, HTTP/2 brings many advanced capabilities, such as:
- Binary Framing Layer – Unlike HTTP/1.1, HTTP/2 request/response is divided into small messages and framed in binary format, making message transmission efficient. With binary framing, the HTTP/2 protocol has made request/response multiplexing possible without blocking network resources.
- Streaming – Bidirectional full-duplex streaming in which the client can request and the server can respond simultaneously.
- Flow Control – Flow control mechanism is used in HTTP/2, enabling detailed control of memory used to buffer in-flight messages.
- Header Compression – Everything in HTTP/2, including headers, is encoded before sending, significantly improving overall performance. Using the HPACK compression method, HTTP/2 only shares the value different from the previous HTTP header packets.
- Processing – With HTTP/2, gRPC supports both synchronous and asynchronous processing, which can be used to perform different types of interaction and streaming RPCs.
All these features of HTTP/2 enable gRPC to use fewer resources, resulting in reduced response times between apps and services running in the cloud and longer battery life for a client running mobile devices.
For seeing the difference between http2 loading then http1 can visit the link http2v/shttp1.1
gRPC Architecture
In the following gRPC architecture diagram, we have the gRPC client and server sides. In gRPC, every client service includes a stub (auto-generated files), similar to an interface containing the current remote procedures. The gRPC client makes the local procedure call to the stub with parameters to be sent to the server. The client stub then serializes the parameters with the marshaling process using Protobuf and forwards the request to the local client-time library in the local machine.
The OS makes a call to the remote server machine via HTTP/2 protocol. The server’s OS receives the packets and calls the server stub procedure, which decodes the received parameters and executes the respective procedure invocation using Protobuf. The server stub then sends back the encoded response to the client transport layer. The client stub gets back the result message and unpacks the returned parameters, and the execution returns to the caller.
gRPC Code Example
Let's have a look at the example how to implement gRPC in go
Prerequisites
Install Go:
https://golang.org/doc/install
Install protoc : gRPC uses protobuf to communicate, in order to generate relevant files, you will need to install protoc :
brew install protobuf // For MacOS
for ubuntu visit this site : install protoc in ubuntu
install protoc-gen-go : as we use go in this post, you will need the go-support for protobuf
go get -u github.com/golang/protobuf/protoc-gen-go
install grpc : the grpc package for go
go get -u google.golang.org/grpc
(Note: make sure your GOPATH is correctly set up so that your packages and project will be under GOPATH/src )
create a directory with name hello and under that folder have 3 sub directory helloclient,helloproto,helloserver in the following manner :
Under the helloproto folder all the proto file will reside(files with .proto extension) with following content:
syntax = "proto3";
package hello;
option go_package = "github.com/Shivam/grpc/hello/helloproto";
message helloRequest {
string name = 1;
}
message helloResponse {
string greeting = 1;
}
service HelloService {
rpc Hello(helloRequest) returns (helloResponse) ;
}
After creating the proto we need to generate .pb files using the following command :
protoc -Ihello/helloproto --go_out=. --go_opt=github.com/Shivam/grpc --go-grpc_out=. --go_grpc_opt=github.com/Shivam/grpc hello/helloproto/*.proto
this will generate 2 files like this :
after generating these files now we will create the server our service in the helloserver directory create the main.go and hello.go file likethis :
main.go - here we are listening at the given address and registering our service
package main
import (
pb "github.com/gaurav/grpc/hello/helloproto"
"google.golang.org/grpc"
"log"
"net"
)
var addr = "127.0.0.1:8080"
type Server struct {
pb.HelloServiceServer
}
func main() {
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("unable listen : %v", err)
}
log.Printf("listening at address:%v", addr)
s := grpc.NewServer()
pb.RegisterHelloServiceServer(s, &Server{})
if err = s.Serve(lis); err != nil {
log.Fatalf("not able to register rpc:%v", err)
}
}
hello.go - here we are providing our service definition
package main
import (
"context"
pb "github.com/gaurav/grpc/hello/helloproto"
"log"
)
func (s *Server) Hello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
log.Printf("Request", in)
return &pb.HelloResponse{
Greeting: "hello " + in.Name,
}, nil
}
now will create the client for our server. In the helloclient directory create main.go and hello.go :
main.go - here we are just trying to connect with our server (helloserver must be running) and created a function doGreet to call the rpc server function we have created in the helloserver.
the function grpc.WithTransportCredentials(insecure.NewCredentials())
we are using this because grpc connection requires ssl to connect
package main
import (
pb "github.com/gaurav/grpc/hello/helloproto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
)
func main() {
conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("not to able connect:%v", err)
}
defer conn.Close()
c := pb.NewHelloServiceClient(conn)
doGreet(c)
}
hello.go
package main
import (
"context"
pb "github.com/gaurav/grpc/hello/helloproto"
"log"
)
func doGreet(c pb.HelloServiceClient) {
res, err := c.Hello(context.Background(), &pb.HelloRequest{
Name: "gaurav",
})
if err != nil {
log.Fatalf("unable to get response %v", err)
}
log.Printf("Greeting: %v", res.Greeting)
}
to see the output just run client the output will come like this:
Greeting: Hello gaurav
Strengths of gRPC
gRPC provides a new take on the old RPC design method by offering many benefits in certain operations. Some of the gRPC strengths which have been increasing its adoption are as follows:
Performance
By different evaluations, gRPC offers up to 10x faster performance and API-security than REST+JSON communication as it uses Protobuf and HTTP/2. Protobuf serializes the messages on the server and client sides quickly, resulting in small and compact message payloads. HTTP/2 scales up the performance ranking via server push, multiplexing, and header compression. Server push enables HTTP/2 to push content from server to client before getting requested, while multiplexing eliminates head-of-line blocking. HTTP/2 uses a more advanced compression method to make the messages smaller, resulting in faster loading.
Streaming
gRPC supports client- or server-side streaming semantics, which are already incorporated in the service definition. This makes it much simpler to build streaming services or clients. A gRPC service supports different streaming combinations through HTTP/2:
Unary (no streaming)
Client-to-server streaming
Server-to-client streaming
Bi-directional streaming
Code Generation
The prime feature of gRPC methodology is the native code generation for client/server applications. gRPC frameworks use protoc compiler to generate code from the .proto file. Code generation is used in command of the Protobuf format for defining both message formats and service endpoints. It can produce server-side skeletons and client-side network stubs, which saves significant development time in applications with various services.
Interoperability
gRPC tools and libraries are designed to work with multiple platforms and programming languages, including Java, JavaScript, Ruby, Python, Go, Dart, Objective-C, C#, and more. Due to the Protobuf binary wire format and efficient code generation for virtually all platforms, programmers can develop performant applications while still using full cross-platform support.
Security
The use of HTTP/2 over the TLS end-to-end encryption connection in gRPC ensures API security. gRPC encourages the use of SSL/TLS to authenticate and encrypts data exchanged between the client and server.
Usability and Productivity
As gRPC is an all-in-one RPC solution, it works seamlessly across various languages and platforms. Additionally, it features excellent tooling, with much of the required boilerplate code generated automatically. This saves considerable time and enables developers to focus more on business logic.
Built-in Commodity Features
gRPC provides built-in support for commodity features, such as metadata exchange, encryption, authentication, deadline/timeouts and cancellations, interceptors, load balancing, service discovery, and so much more.
Weaknesses of gRPC
As with every other technology, gRPC also has the following downsides that you need to be aware of when choosing it for developing applications.
Limited Browser Support
As gRPC heavily uses HTTP/2, it is impossible to call a gRPC service from a web browser directly. No modern browser provides the control needed over web requests to support a gRPC client. Therefore, a proxy layer and gRPC-web are required to perform conversions between HTTP/1.1 and HTTP/2.
Non-human Readable Format
Protobuf compresses gRPC messages into a non-human readable format. This compiler needs the message’s interface description in the file to deserialize correctly. So, developers need additional tools like the gRPC command-line tool to analyze Protobuf payloads on the wire, write manual requests, and perform debugging.
No Edge Caching
While HTTP supports mediators for edge caching, gRPC calls use the POST method, which is a threat to API-security. The responses can’t be cached through intermediaries. Moreover, the gRPC specification doesn’t make any provisions and even indicates the wish for cache semantics between server and client.
Steeper Learning Curve
Many teams find gRPC challenging to learn, get familiar with Protobuf, and look for tools to deal with HTTP/2 friction. It is a common reason why users prefer to rely on REST for as long as possible.
Subscribe to my newsletter
Read articles from Gaurav Bharadwaj directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by