1. gRPC development process
gRPC
The general development process is as follows:
1. Define the proto file, that is, define the Request and Response structures, and the service Service that contains multiple methods.
2. Use the protoc tool to generate a Stub for the corresponding language.
3. The server implements the interface defined in proto and writes logic codes.
4. The client calls the server through the generated Stub.
1.1 Write .proto description file
Go writes an rpc interface to obtain server time. time.proto
The content of the file is as follows:
syntax = "proto3";
package proto;
option go_package = "./base;base";
service BaseService {
rpc GetTime (TimeRequest) returns (TimeResponse) {}
}
message TimeRequest {}
message TimeResponse {
string time = 1;
}
1.2 Compile and generate .pb.go file
Generate the Stub code of the server and the client through protoc:
$ protoc --go_out=plugins=grpc:. time.proto
1.3 The server implements the agreed interface and provides services
Write server.go
the file:
package main
import (
"context"
pb "demo/base"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
"time"
)
type service struct {
// time.pb.go中的结构体
pb.UnimplementedBaseServiceServer
}
func main() {
listen, err := net.Listen("tcp", ":50051")
fmt.Println("Listen 50051!")
if err != nil {
fmt.Println(err)
}
s := grpc.NewServer()
reflection.Register(s)
pb.RegisterBaseServiceServer(s, &service{
})
s.Serve(listen)
}
// 实现接口
func (s *service) GetTime(ctx context.Context, in *pb.TimeRequest) (*pb.TimeResponse, error) {
now := time.Now().Format("2006-01-02 15:04:05")
return &pb.TimeResponse{
Time: now}, nil
}
# 启动server
$ go run server.go
Listen 50051!
1.4 The client calls the method in the .pb.go file to request the service according to the agreement
Write client.go
the file:
package main
import (
"context"
pb "demo/base"
"fmt"
"google.golang.org/grpc"
"time"
)
func main() {
conn, err := grpc.Dial(":50051", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
fmt.Println(err)
}
defer conn.Close()
c := pb.NewBaseServiceClient(conn)
getTime(c)
}
func getTime(client pb.BaseServiceClient) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, _ := client.GetTime(ctx, &pb.TimeRequest{
})
fmt.Println("Time:", r.Time)
return nil
}
# 启动client
$ go run client.go
Time: 2023-02-10 16:14:01
Final project structure:
$ tree demo
demo
├── base
│ └── time.pb.go
├── client.go
├── go.mod
├── go.sum
├── server.go
└── time.proto
1 directory, 6 files
2. gRPC entry case
By convention, here also starts with a Hello project, this project defines a Hello Service, the client sends a request containing a string name,
The server returns a Hello message.
2.1 Step1: Write the description file hello.proto
// 指定proto版本
// 表明使用proto3语法,如果你没有指定这个,编译器会使用proto2语法
syntax = "proto3";
// 指定默认包名
package hello;
// 指定golang包名
option go_package = "./hello";
// 定义Hello服务
service Hello {
// 定义SayHello方法
rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求结构
message HelloRequest {
string name = 1;
}
// HelloResponse 响应结构
message HelloResponse {
string message = 1;
}
hello.proto
A Hello Service is defined in the file, which contains a SayHello
method and declares HelloRequest
and
HelloResponse
Message structures are used for requests and responses. The client calls the method HelloRequest
with parameters SayHello
to request the server, and the server
The server responds to HelloResponse
the message. A simple service is defined.
2.2 Step2: Compile and generate .pb.go file
# 编译hello.proto
$ protoc -I . --go_out=plugins=grpc:. ./hello.proto
The file generated in the current directory hello.pb.go
, according to .proto
the instructions in the file, contains the description of the server interface HelloServer
, the client
Port interface and implementation HelloClient
, and HelloRequest
structure HelloResponse
.
2.3 Step3: Implement the server interface server.go
package main
import (
"context"
"fmt"
"log"
"net"
// 引入编译生成的包
pb "demo/hello"
"google.golang.org/grpc"
)
const (
// Address gRPC服务地址
Address = ":50052"
)
type server struct {
pb.UnimplementedHelloServer
}
// SayHello 实现Hello服务接口
func (s* server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
resp := new(pb.HelloResponse)
resp.Message = fmt.Sprintf("Hello %s.", in.Name)
return resp, nil
}
func main() {
listen, err := net.Listen("tcp", Address)
log.Println("Listen ", Address)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
// 实例化 grpc Server
s := grpc.NewServer()
// 注册HelloService
pb.RegisterHelloServer(s, &server{
})
if err := s.Serve(listen); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Instantiate grpc Server and register HelloService to start providing services.
# 启动服务
$ go run server.go
2023/02/10 18:26:39 Listen :50052
2.4 Step4: Implement the client to call client.go
package main
import (
pb "demo/hello" // 引入proto包
"golang.org/x/net/context"
"google.golang.org/grpc"
"log"
)
const (
// Address gRPC服务地址
Address = ":50052"
)
func main() {
// 连接
conn, err := grpc.Dial(Address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
// 初始化客户端
c := pb.NewHelloClient(conn)
// 调用方法
req := &pb.HelloRequest{
Name: "gRPC"}
res, err := c.SayHello(context.Background(), req)
if err != nil {
log.Fatalln(err)
}
log.Println(res.Message)
}
After the client initializes the connection, it can directly call the method hello.pb.go
implemented in SayHello
to initiate a request to the server. The usage posture is like calling
Same with native methods.
# 启动客户端
$ go run client.go
2023/02/10 18:30:08 Hello gRPC.
If you get Hello gRPC
a reply, the demo is running successfully.
# 项目结构
$ tree demo
demo
├── client.go
├── go.mod
├── go.sum
├── hello
│ └── hello.pb.go
├── hello.proto
└── server.go
1 directory, 6 files