preface
Hello everyone, I am fry fish, this chapter will use Go to write gRPC Server and Client, let them communicate with each other. The following libraries are used on top of this:
- google.golang.org/grpc
- github.com/golang/protobuf/protoc-gen-go
The installation
gRPC
go get -u google.golang.org/grpc
Copy the code
Protocol Buffers v3
Wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip unzip protobuf - all - 3.5.1 track of.zipcdProtobuf-3.5.1 /./configure make make installCopy the code
Check whether the installation is successful
protoc --version
Copy the code
If the following error occurs, execute ldConfig naming to resolve the problem
protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such file or directory
Copy the code
Protoc Plugin
go get -u github.com/golang/protobuf/protoc-gen-go
Copy the code
If there is a problem with the installation environment, please refer to my previous article “Introduction and environment installation” in detail, no further details
gRPC
This section begins the formal preparation of gRPC related procedures, get on the bus together 😄
graphic
The directory structure
├─ ├─ proto ├─ search. Proto ├─ ├.goCopy the code
IDL
write
In the search.proto file in the proto folder, write the following:
syntax = "proto3";
package proto;
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse) {}
}
message SearchRequest {
string request = 1;
}
message SearchResponse {
string response = 1;
}
Copy the code
generate
Run the following command in the proto folder:
$ protoc --go_out=plugins=grpc:. *.proto
Copy the code
- Plugins =plugin1+plugin2: Specify a list of child plug-ins to load
The proto file we defined refers to RPC services, and RPC code is not generated by default, so we need to give plugins to protoc-gen-go to say please support RPC (gRPC specified here).
- –go_out=. : Sets the directory to output the Go code
This directive loads the protoc-gen-go plug-in to generate go code with.pb.go as the file suffix
- : (colon)
The colon acts as a delimiter, followed by the desired set of arguments. If this does not involve RPC, the command can be simplified as follows:
$ protoc --go_out=. *.proto
Copy the code
Note: I suggest you look at the.pb.go files generated by the two commands. What are the differences
Generated after
After executing the command, you will get a.pb.go file with the following contents:
type SearchRequest struct {
Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SearchRequest) Reset() { *m = SearchRequest{} }
func (m *SearchRequest) String() string { return proto.CompactTextString(m) }
func (*SearchRequest) ProtoMessage() {}
func (*SearchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}
}
func (m *SearchRequest) GetRequest() string {
ifm ! = nil {return m.Request
}
return ""
}
Copy the code
By reading this section of code, you can see that it mainly involves the following aspects:
- Convert field names from underscore to capital hump mode (field export)
- Generates a set of Getters methods that make it easy to handle some cases of null pointer values
- The ProtoMessage method implements the proto.Message interface
- Generate Rest methods to restore the Protobuf structure to zero
- Repeated is converted to slice
type SearchRequest struct {
Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"`
}
func (*SearchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}
}
type SearchResponse struct {
Response string `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"`
}
func (*SearchResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{1}
}
...
func init() { proto.RegisterFile("search.proto". fileDescriptor_search_8b45f79ee13ff6a3) } var fileDescriptor_search_8b45f79ee13ff6a3 = []byte{ // 131 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4e, 0x4d, 0x2c, 0x4a, 0xce, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x9a, 0x5c, 0xbc, 0xc1, 0x60, 0xe1, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x09, 0x2e, 0xf6, 0x22, 0x08, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x55, 0xd2, 0xe1, 0xe2, 0x83, 0x29, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x92, 0xe2, 0xe2, 0x28, 0x82, 0xb2, 0xa1, 0x8a, 0xe1, 0x7c, 0x23, 0x0f, 0x98, 0xc1, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xe6, 0x5c, 0x6c, 0x10, 0x01, 0x21, 0x11, 0x88, 0x13, 0xf4, 0x50, 0x2c, 0x96, 0x12, 0x45, 0x13, 0x85, 0x98, 0xa3, 0xc4, 0x90, 0xc4, 0x06, 0x16, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xba, 0x74, 0x95, 0xc0, 0x00, 0x00, 0x00, }Copy the code
And this part of the code is basically around fileDescriptor, so fileDescriptor_search_8b45f79ee13ff6a3 is a compiled proto file, and each method has a Descriptor method, Represents the specific Message Field of this method in the fileDescriptor
Server
This section will write the basic gRPC Server template to complete a method call. Write the following to server.go:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
type SearchService struct{}
func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil
}
const PORT = "9001"
func main() {
server := grpc.NewServer()
pb.RegisterSearchServiceServer(server, &SearchService{})
lis, err := net.Listen("tcp".":"+PORT)
iferr ! = nil { log.Fatalf("net.Listen err: %v", err)
}
server.Serve(lis)
}
Copy the code
- Create a gRPC Server object that you can think of as an abstract object on the Server side
- Register the SearchService, which contains the server-side interface that needs to be invoked, with the gRPC Server’s internal registry. In this way, when a request is received, the server interface can be discovered through internal service discovery and transferred for logical processing
- Example Create Listen to Listen for TCP ports
- GRPC Server starts lis.accept until Stop or GracefulStop
Client
Next, write the basic template of gRPC Go Client, open the Client /client. Go file, and write the following:
package main
import (
"context"
"log"
"google.golang.org/grpc"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
const PORT = "9001"
func main() {
conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure())
iferr ! = nil { log.Fatalf("grpc.Dial err: %v", err)
}
defer conn.Close()
client := pb.NewSearchServiceClient(conn)
resp, err := client.Search(context.Background(), &pb.SearchRequest{
Request: "gRPC",})iferr ! = nil { log.Fatalf("client.Search err: %v", err)
}
log.Printf("resp: %s", resp.GetResponse())
}
Copy the code
- Create a connection interaction with a given target (server)
- Create a client object for the SearchService
- Sends an RPC request, waits for a synchronous response, and returns the response result after receiving a callback
- Output response results
validation
Start the Server
$ pwd
$GOPATH/github.com/EDDYCJY/go-grpc-example
$ go run server.go
Copy the code
Start the Client
$ pwd
$GOPATH/github.com/EDDYCJY/go-grpc-example/client
$ go run client.go
2018/09/23 11:06:23 resp: gRPC Server
Copy the code
conclusion
In this chapter, we have introduced Protobuf and gRPC Client/Server respectively. I hope you can write a Demo for in-depth understanding in combination with the content described in the article. It will certainly be better 🤔
?
If you have any questions or mistakes, welcome to raise questions or give correction opinions on issues. If you like or are helpful to you, welcome Star, which is a kind of encouragement and promotion for the author.
My official account
reference
Sample code for this series
- go-grpc-example