Recently, service and microservices have become the mainstream of medium-and large-scale distributed systems, and RPC plays a crucial role in it. Here, let's take a brief look at what RPC is, and a simple example of grpc, to see how to develop with GRPC.
1. What is RPC
RPC (remoteProcedurecall), which is called remotely, is a way of communicating between processes. Unlike a function in alocal call where callers call the same address space, RPC allows a program to call a function of another address space (usually on another machine on a shared network) without the programmer explicitly encoding the details of the remote call, This makes the remote call and the local call have the same effect as the programmer's point of view.
The concept of RPC had already emerged early, and a concrete implementation was given in Bruce Jay Nelson's 1984 paper implementing RPC. At the same time, in this paper, Bruce gives the three benefits of RPC:
- simple and concise syntax : This makes it easier and more accurate to build distributed systems;
- Efficient : function calls via RPC are simple enough to make communication more rapid;
- General : The process in a stand-alone system is often the most important communication mechanism for different algorithms.
In fact, from the caller's point of view, there is no difference between RPC and ordinary local calls, it is the process of invoking other functions, but in the implementation of the point of view, RPC is through the network between the different machines to communicate, complete the normal call in the same address space can be completed by the parameters of the transfer and the result callback. Bruce's article, published in 1984, is a demonstration of his vision, and the RPC framework we use today is basically implemented in this design.
As we can see from the above introduction, the difference between RPC and ordinary local calls is in the way that parameters and results are passed. RPC is passed through the network, so for an RPC system you need to think carefully about the implementation details. Here we do not have too much involved, just understand the concept of good.
In this paper, Bruce points out that implementing an RPC system requires several parts :
- User;
- User-stub;
- Rpcruntime;
- Server-stub;
- Server.
The specific structure of these five parts:
Where User, User-stub, and one Rpcruntime instance run on the caller (caller) machine, and Server, Server-stub, and another Rpcruntime instance run on the callee ( Callee) on the machine.
The specific invocation process is as follows:
When a user wants to initiate a remote call, it actually first calls the relevant program in User-stub, and User-stub is responsible for getting what the specific remote program is called and passing the parameters of the call to the rpcruntime of the caller side. Rpcruntime will pass the parameters over the network to the rpcruntime of the target machine. The rpcruntime on the target machine passes the parameter to Server-stub after receiving the request, then server-stub parses the parameter and initiates a normal local call, which causes the server to execute. When the server finishes executing, the results are returned to server-stub and then passed back to caller over the network. The results of the caller end are returned to the user after the User-stub parsing results.
Now the RPC framework basically supports different languages, that is, user and server can be implemented in different languages, then the RPC framework needs to be in the middle of an interface definition and unification. This is the use of IDL (Interface definition Language) to define the interface, and then through the framework provided by the tool to each user and server to generate the corresponding language stub.
2. GRPC debut
GRPC is an RPC framework for Google Open source, which uses protocol buffers as IDL and the underlying message conversion format. As described above, the structure of the GRPC
Not much, let's look at a simple example of how to use GRPC.
3. HelloWorld in Grpc
In order to use GRPC, we need go 1.6 or later:
$ go version
If the go is not installed, you can refer to this. After you install go, you need to set the Gopath.
Next, we need to install GRPC. We can use the following command to install:
$ go get -u google.golang.org/grpc
But this installation requires scientific Internet access. If it is not scientific internet, can also be github.com
installed by.
First go to the first $gopath directory, the go get
default installation under the first Gopath, create a new google.golang.org
directory, pull the golang
github
image Library on:
$ cd /User/valineliu/go/src$ mkdir google.golang.org$ cd google.golang.org/$ git clone https://github.com/grpc/grpc-go$ mv grpc-go/ grpc
Then install PROTOBUF buffers v3. Protobuf buffers as a tool for GRPC IDL and underlying message conversion, we need to install the corresponding PROTOC compiler.
First download the relevant version of the file here:
$ wget https://github.com/google/protobuf/releases/download/<version>/protobuf-all-<version>.zip$ unzip protobuf-all-<version>.zip$ cd protobuf-all-<version>$ ./configure$ make$ make install
This completes the installation of the PROTOC.
Next install the Protoc go plugin:
$ go get -u github.com/golang/protobuf/protoc-gen-go
Protoc-gen-go is a plugin for PROTOC that is used to generate the go language code based on the IDL profile. The above command installs it in the $gopath/bin directory and adds it to the path:
$ export PATH=$PATH:$GOPATH/bin
So all the components are installed, and then we start HelloWorld
.
The directory structure of the project is as follows:
GOPATH |__src |__helloworld |__client |__helloworld |__server ...
Where client
directories are used to store client-side code, helloworld
directories are used to store the service's IDL definitions, and server
directories are used to store server-side code.
First, we need to define our services. helloworld
to enter the directory, create a new file:
$ cd helloworld$ vim helloworld.proto
Here I am using vim for editing. Defined as follows:
syntax = "proto3";package helloworld;message HelloRequest { string name=1;}message HelloReply { string message =1;}service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {}}
Here we do not look at the specific meaning. We then use protoc
this file to compile:
$ protoc -I. --go_out=plugins=grpc:. helloworld.proto
This way, there is one more file in the directory: helloworld.pb.go
.
Then go server
to the directory and create a new file:
$ cd ../server$ vim server.go
server.go
The contents are as follows:
package mainimport ( "log" "net" "golang.org/x/net/context" "google.golang.org/grpc" pb "helloworld/helloworld" "google.golang.org/grpc/reflection")const ( port = ":50051")type server struct{}func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil}func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v",err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v",err) }}
This is the server side of the code, again, we do not consider the specific details for the time being.
Then go to the client
directory to create a new file:
$ cd ../client$ vim client.go
client.go
The contents are as follows:
package mainimport ( "log" "os" "time" "golang.org/x/net/context" "google.golang.org/grpc" pb "helloworld/helloworld")const ( address = "localhost:50051" defaultName = "world")func main() { conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v",err) } defer conn.Close() c := pb.NewGreeterClient(conn) name := defaultName if len(os.Args) > 1 { name = os.Args[1] } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name:name}) if err!=nil { log.Fatalf("could not greet: %v",err) } log.Printf("Greeting: %s",r.Message)}
This is the client-side code.
So all the code is written, and then we're going to let it run.
First enter server
the directory and start our server:
$ cd ../server$ go run server.go
, the server started up:
Then open a different terminal, enter client
the directory, launch an RPG remote call:
$ cd $GOPATH/src/helloworld/client$ go run client.go // one $ go run client.go firework //two
Results
Success! Our first little example is done, this one here first, to be Continue.