preface

Nowadays, microservices are becoming more and more popular, and communication between services is becoming more and more important. The essence of communication between services is the exchange of information, and the intermediary/bridge of information exchange is our API.

Admittedly, the most popular API to build today is still Restful (HTTP-JSON) because it’s simple, fast, and easy to understand.

But in this article, let’s try to explore using gRPC to build our API, and compare the similarities and differences between gRPC and Restful in the process.

Finally, let’s think about what an API should look like. From a macro perspective, an API should be very simple because it does two things:

  • 1. The client sends a Request.
  • 2. The server receives the request and returns a Response.

This idea is also evident in gRPC, and from a micro point of view, when building an API, we may need to consider:

  • 1. What data model do we use? JSON, XML, or binary streams
  • 2. What protocol do we use for transmission? HTTP, TCP, HTTP/2
  • 3. How do we call methods and handle errors
  • 4. How do we deal with large amounts of data
  • 5. How can we reduce the interface delay
  • .

Without further ado, let’s get down to business.

What is the gRPC

Before we talk about gRPC, let’s talk about RPC.

Remote Procedure Call (RPC) Generally speaking, using RPC to communicate, calling remote functions is just like calling local functions. The bottom layer of RPC will do a good job of data serialization and transmission, so that we can create distributed applications and services more easily.

GRPC, a type of RPC, is free and open source, made by Google. With gRPC, we just need to define the Request and Response for each API, and the gRPC framework will do the rest for us automatically.

In addition, gRPC typically uses Protobuf (full protocol buffers) as its Interface Definition Language (IDL), and the underlying message exchange format also uses ProtobuF.

GRPC basic communication process

Here is a picture from the official document, through which we can get a rough idea of gRPC communication flow:

1. The first step in gRPC communication is to define IDL, our interface document (suffix.proto)

2. The second step is to compile the Proto file to get the stub file shown in dark green.

3. The third step is for the Server (gRPC Server) to implement and start the interface defined in the first step, which is defined in the stub file

4. The last step is for the client to call the server function with the help of a stub file. Although the function called by the client is implemented by the server, it is called as if it were a local function.

The above is the basic flow of gRPC, as can be seen from the figure, since our proto file compilation supports multiple languages (Go, Java, Python, etc.), so gRPC is also cross-language.

gRPC VS Restful

The comparison between gRPC and Restful has always been a required course in learning gRPC. I will compare it from document specification, message encoding, transmission protocol, transmission performance, transmission form, browser support, data readability, security and other aspects.

Document specification

In my opinion, gRPC uses PROto file to write interface (API), document specification is better than Restful, because the syntax and form of proTO file is fixed, so more rigorous, unified style clear; Because Restful can be written using multiple tools (as long as people can understand it), each company and each person has a different style of writing, which can be confusing.

In addition, the obsolescence of Restful documents is well understood by many people, because it takes a lot of manpower and energy to maintain a timeless document, and companies tend to put business first. GRPC documentation is code, and changes to the interface are reflected in the code, which is one of the reasons I like gRPC because it takes less effort to maintain the documentation.

Message coding

For message encoding, gRPC uses Protobuf for message encoding, while Restful generally uses JSON for message encoding

Transfer protocol

In terms of transmission protocol, gRPC uses HTTP/2 as the underlying transmission protocol. It is said that it can also be replaced by other protocols, but it has not been verified yet. RestFul uses HTTP.

Transmission performance

Because gRPC uses a Protobuf for message encoding (i.e., serialization), messages that are serialized by a Protobuf are small in size (transmit less content, relatively fast). Coupled with the HTTP/2 protocol (further optimization of HTTP1.1), gRPC delivers better transport performance than Restful.

Transfer form

GRPC’s biggest advantage is streaming, which can be divided into four types (UNARY, Client Stream, Server Stream, bidirectional Stream). Restful does not support streaming.

Browser support

I don’t know if it is the reason for the late development of gRPC, the current browser support for gRPC is not very good, and the support for Restful can be said to be inseparable, which is also a disadvantage of gRPC, if the subsequent browser support for gRPC is more and more high, I don’t know whether gRPC can be Restful?

Readability and security of messages

Since gRPC serialized data is binary, and if you don’t know what Request and Response are defined, it is almost impossible to decrypt, so gRPC security is very high, but with the resulting readability is reduced, debugging will be more troublesome; Restful is the opposite (now that you have HTTPS, it’s actually quite secure)

Code writing

Stub files are used for functions and field names called by gRPC. Therefore, the code is less prone to errors, and the cost of joint tuning is lower. There are no low-level errors, such as incorrect field names and missing field names.

Application scenarios of gRPC

From the above comparison between gRPC and Restful, we can also understand the advantages and disadvantages of gRPC from the side and deduce its application scenarios accordingly.

In general, gRPC is mainly used for service invocation within the company, with low performance consumption, high transmission efficiency and convenient service governance. Restful is mainly used externally, such as providing interfaces for front-end invocation and external services for others to invoke.

GRPC simple practice

Generally speaking, to achieve a gRPC server and client, mainly divided into these steps:

  • 1. Install protobuf dependencies
  • 2. Prepare PROTO file (IDL)
  • 3. Compile proto file (generate stub file)
  • 4. Write the server side to realize our interface
  • 5. Write the client to test our interface

1. Install protobuf dependencies

#1. Install the protoc
$ brew install protoc

#2. Check whether the installation is successful
$ protoc --versionLibprotoc 3.7.1
#3. Install and compile the plug-in
$ export GO111MODULE=on  # Enable Go Module
$go get google.golang.org/protobuf/cmd/protoc-gen-go \ google.golang.org/grpc/cmd/protoc-gen-go-grpc
Copy the code

2. Write proto files

syntax = "proto3";

package greeter.srv;

option go_package = "proto/greeter";

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

// The request message containing the user's name.
message HelloRequest {
    string name = 1;
}

// The response message containing the greetings
message HelloReply {
    string message = 1;
}
Copy the code

This is a relatively simple example using the official document example. First line syntax = “proto3” indicates that we are using proto3, not proto2, which is the previous version.

“Service Greeter” is a type of Greeter for a service that has a SayHello interface. The interface’s request is a HelloRequest structure. The return is the HelloReply interface body (the API is essentially Request+Response!). .

Multiple services can be defined in the same PROto file (not recommended, unless associated), and multiple interfaces can be defined under each service.

Note: for more protobuf syntax, please refer to other tutorials online

3. Compile the proto file

The compile command is:

protoc –go_out=. –go_opt=paths=source_relative \

--go-grpc_out=. --go-grpc_opt=paths=source_relative \
proto/greeter/greeter.proto
    
Copy the code

Note: The compilation of proto files is one of the things I most want to make fun of with Protobuf. First of all, the syntax is difficult to understand, such as the paths=source_relative mentioned above. Second, the location of stub files also requires many attempts to write to the desired location. Finally, there are the various protobuf plugins, which have no way of exporting versions, and are often used with some strange issues depending on the version.

4. Server programming

The server side is written as follows:

// greeter_server.go
type server struct{}// Implement our interface
func (s *server) SayHello(ctx context.Context, req *greeter.HelloRequest) (rsp *greeter.HelloReply, err error) {
	rsp = &greeter.HelloReply{Message: "Hello " + req.Name}
	return rsp, nil
}

func main(a) {
	listener, err := net.Listen("tcp".": 52001")
	iferr ! =nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// gRPC server
	s := grpc.NewServer()
	// Bind the server to the processor
	greeter.RegisterGreeterServer(s, &server{})

	//reflection.Register(s)
	fmt.Println("gRPC server listen in 52001...")
	err = s.Serve(listener)
	iferr ! =nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
Copy the code

In the proto file, we only defined the SayHello interface and did not implement it, so when we want to implement a server side, the first step is to implement our interface. Finally, there is the basic flow of a server (whether HTTP or gRPC), which is to declare a server instance, bind the processor, and finally run.

5. Client programming

// greeter_client.go
func main(a) {
	// Initiate a connection, WithInsecure means to use an insecure connection, i.e., not SSL
	conn, err := grpc.Dial("127.0.0.1:52001", grpc.WithInsecure())
	iferr ! =nil {
		log.Fatalf("connect failed: %v", err)
	}

	defer conn.Close()

	c := greeter.NewGreeterClient(conn)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()

    SayHello is implemented by the remote server, but is called just like a local function
	r, err := c.SayHello(ctx, &greeter.HelloRequest{Name: "World"})
	iferr ! =nil {
		log.Fatalf("call service failed: %v", err)
	}
	fmt.Println("call service success: ", r.Message)
}
Copy the code

The client-side implementation is also relatively simple: initiate a connection, create a client instance, call a method, and get a result.

The source code

All the source code for this article is on my Github, click here if you need it

Write in the last

Writing here, I believe you have a certain understanding of gRPC, if you feel useful, trouble to give me a thumbs-up!

I am Yan Gan, welcome to follow my public account Yan Gan Coding, your likes and attention are the biggest motivation for my creation! !