Abstract

In this article, I mainly introduce gRPC to you.

Then, I’ll create a simple exercise project for gRPC’s Hello World project.

In this project, there is only a simple RPC function that illustrates how gRPC works.

In addition, I will share with you some of my first encounters with gRPC, mainly in the Proto-gen-Go plugin for the Protocol Buffer.

1. Brief introduction

In this section, I will briefly introduce you to gRPC. Remote Procedure Call (RPC) This is a protocol used to mask the details of a call in distributed computing, allowing you to call a remote function directly as if it were a local call. And what is gRPC? In the official words:

A high-performance, open-source universal RPC framework

gRPCRPC is a high-performance, open source, generic RPC framework.

In gRPC, we call the caller client and the called server. Like other RPC frameworks, gRPC is based on the idea of “service definition”. To put it simply, we describe a service in a way that is language independent. In this “service definition” process, we describe the service we provide, what the service name is, what methods can be called, what kind of input arguments those methods have, and what kind of callback arguments they have.

That is, after the services and methods are defined, gRPC hides the low-level details, and the client only needs to call the defined methods directly to get the expected results. On the server side, we also need to implement the methods we defined. GRPC also shields us from the low-level details, so we just need to implement the logic of the defined method.

As you can see from the above description, the so-called “service definition” is very close to defining the semantics of the interface. I prefer to think of it as a “convention” where the two parties agree on an interface, the server implements the interface, and the client calls a proxy object for the interface. For other details, please refer to gRPC.

In addition, gRPC is language independent. You can use C++ as the server, Golang, Java, etc as the client. To achieve this, we should be language independent in defining the service and in encoding and decoding.

Here is a picture from the official website:

Therefore, gRPC uses Protocol Buffers.

I will not expand Protocol Buffers here; you can think of it as a code generation and serialization tool. This tool converts the methods we define into language-specific code. For example, if you define a type of parameter, it will help you convert it to a struct structure in Golang, and if you define a method, it will help you convert it to a func function. In addition, as you send a request and receive a response, the tool encodes and decodes the data you are about to send into a format that gRPC can transmit, or the data you are about to receive into a format that the programming language can understand.

So much for a brief introduction to gRPC, let’s get straight to practice.

2. Configure the environment

In this section, a lot of things may not apply.

I don’t have space to list all of them. If you encounter problems in the process of installation, you can search for solutions on the Internet, or you can find my contact information at the end of the article, and we can study together.

2.1 gRPC

go get google.golang.org/grpc
Copy the code

This step installs the gRPC core library, but this step requires (special Internet access). So if you make a mistake during the installation process, you can either be scientific or find another installation method.

2.2 protocol buffers

On Mac OS, install directly with BREW.

brew info protobuf
Copy the code

2.3 protoc – gen – go

The protocol compiler was installed in the previous step. We mentioned that you can generate code in many different languages. Therefore, in addition to this compiler, we also need code generation tools for each language.

For Golang, it’s called protoc-gen-go.

But here there is a small pit, github.com/golang/protobuf/protoc-gen-go and google.golang.org/protobuf/cmd/protoc-gen-go is different.

The difference is that the former is the old version and the latter is the new version taken over by Google. The API between them is different, which means the commands used to generate, and the files generated are different.

Because the example in the current GRPC-Go source code is generated in the latter way, in order to keep pace with The Times, this article also adopts the latest way.

You need to install two libraries:

go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
Copy the code

Since these files have already been downloaded during GRPC installation, you can use install instead of get.

Then you look at your $GOPATH path and there should be two files marked 1 and 2:

At this point, all the preparatory work has been completed.

3. Create a proto file

Before we start development, let’s talk about our goals.

In this GRPC-Practice project, I want to implement a feature where the client can send a message to the server, and when the server receives the message, it returns a response to the client.

As mentioned earlier, we need to define the services before developing the Server and client.

Therefore, in this section, I will introduce you to writing proto files.

3.1 Project Structure

Before we do that, let’s look at the initial structure of the entire project.

In this section we will write the ‘*.proto’ file.

Create a message.proto file in the proto folder.

On the first line of the file, we say:

syntax = "proto3";
Copy the code

This is an indication that we are using proto3 syntax.

Then we should write:

option go_package = ".; message";Copy the code

The content of this section is about which directory and which package the final generated GO file is in. “Message” means the package name of the generated GO file is “Message”.

We then need to define a service in which we need a method that takes the parameters from the client and returns the response from the server.

So we could write this:

service MessageSender {
  rpc Send(MessageRequest) returns (MessageResponse) {}
}
Copy the code

You can easily see that we have defined a service called MessageSender with an RPC method called Send. This method sends a MessageRequest and returns a MessageResponse.

Let’s look at the specific MessageRequest and MessageResponse:

message MessageResponse {
  string responseSomething = 1;
}

message MessageRequest {
  string saySomething = 1;
}
Copy the code

The message keyword, which you can think of as a structure in Golang. What’s special here is the “assignment” after the variable. Note that this is not an assignment, but rather a definition of the variable’s position within the message. I’ll cover more details in the source code analysis section.

In writing after the above content, in/GRPC – practice/SRC/helloworld/proto directory to perform the following commands:

protoc --go_out=. message.proto
protoc --go-grpc_out=. message.proto
Copy the code

These two commands generate the following two files:

These two files contain the GO language implementation of the method we defined, as well as the request we defined and the corresponding GO language implementation.

Protoc-gen-go has converted your defined language-agnostic message.proto to go language code for server and client to use directly.

Notice that by this point you might be a little confused.

In some tutorials on the web, there is a generation like this:

protoc --go_out=plugins=grpc:. helloworld.proto
Copy the code

This is done using the Github version of protoc-Gen-Go, which is currently being taken over by Google.

Go and XXX.pb. go files as shown in the figure above are not generated in this way, but only xxx.pb.go files are generated.

In addition, you may encounter this error:

protoc-gen-go-grpc: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--go-grpc_out: protoc-gen-go-grpc: Plugin failed with status code 1.
Copy the code

This is because you don’t have the protoc-gen-go-grPC plugin installed, and this problem should not occur in this article.

You might also run into questions like:

--go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=... ' to generate gRPCCopy the code

This is because you installed a newer version of Protoc-gen-Go, but you used the old build command.

But both approaches do the job, just with different apis. This article is based on the Google version of protoc-Gen-Go for demonstration.

For more detailed information, you can find it here: github.com/protocolbuf…

4. The service side

4.1 registered

We create a server.go file under the server directory.

Add the following code to the main function:

srv := grpc.NewServer()
message.RegisterMessageSenderService(srv, &message.MessageSenderService{})
Copy the code

It is easy to see that we created a Server in this section and then registered our Service.

In the second argument to the registration function, we pass in an instance of MessageSenderService.

Let’s see how this example is structured:

type MessageSenderService struct {
	Send func(context.Context, *MessageRequest) (*MessageResponse, error)
}
Copy the code

As you can see, there is a method in this instance, and that method is the send method we defined. In other words, this part requires us to implement the send method on the Server side.

So we create a method like this:

func handleSendMessage(ctx context.Context, req *message.MessageRequest) (*message.MessageResponse, error) {
	log.Println("receive message:", req.GetSaySomething())
	resp := &message.MessageResponse{}
	resp.ResponseSomething = "roger that!"
	return resp, nil
}
Copy the code

Note that “implementation-defined methods” do not mean that we need to create a method with the same name, but rather that we need to create a method with the same function signature. In other words, you need to have the same input and output parameters.

We then write this method into the registration function, which looks like this:

message.RegisterMessageSenderService(srv, &message.MessageSenderService{
		Send: handleSendMessage,
	})
Copy the code

At this point, we have successfully implemented our declared method on the server side.

4.2 listening

This process is similar to that of Golang’s Web server, where a Handler is created and then listens for the port.

So the same thing happens at this point.

listener, err := net.Listen("tcp", ":12345") if err ! = nil { log.Fatalf("failed to listen: %v", err) } err = srv.Serve(listener) if err ! = nil { log.Fatalf("failed to serve: %v", err) }Copy the code

Listen for the TCP connection on port 12345 and start the server.

At this point, the server development is complete.

5. The client

On the client side, we should establish a connection with the server side before we can call various methods.

Conn, err := grpc.Dial("127.0.0.1:12345", grpc.withInsecure (), grpc.withBlock ()) if err! = nil { log.Fatalf("did not connect: %v", err) } defer conn.Close()Copy the code

The above code is to connect to the local port 12345.

Then, by definition, we call server-side methods as easily as we call local methods.

So, here’s what we do:

client := message.NewMessageSenderClient(conn) resp, err := client.Send(context.Background(), &message.MessageRequest{SaySomething: "hello world!" }) if err ! = nil { log.Fatalf("could not greet: %v", err) }Copy the code

It is easy to understand that we can implement the logic we need by creating a client locally and calling the Send method we defined earlier.

In simple terms, we define the method in the *.proto file, then implement the specific logic of the defined RPC method on the server side, and call the method on the client side.

For other parts, Proto Buffer is responsible for converting the data structure stored in Golang to the data in RPC transmission, and GRPC is responsible for encapsulating all the logic.

When both the server and client are running, you should see something like this:

At this point, we have successfully Hello a World.

Write in the last

First of all, thank you for being here!

In this article, I will introduce you to the way of writing hello world and some possible pitfalls in the process of saying hello.

I think the biggest hole is in the protoc-gen-go plugin, because the two grammars confused me for a long time.

If during this period, you still have some problems to solve, welcome to leave a message, or direct public number to find me, we study together.

If there are any mistakes in the article, please do not hesitate to advise, thank you!

Finally, thanks again for being here!

According to the convention, throw a public number here, no matter whether there is a problem, welcome to find me to play ~

The official account has the same name as the Nuggets’ ID: Red Chicken fungus