开发环境:
系统: ubuntu20.04
go版本: 1.19
编辑器: goland
grpc
:远程过程调用,使用场景很多,也是比较流行的技术之一。使用go开发grpc服务,除了必须的go语言开发环境之外,还需要安装grpc相关命令。
grpc环境配置
protoc安装
1 2 3 4
| $ sudo apt install -y protobuf-compiler $ protoc --version
libprotoc 3.6.1
|
如果是其他系统电脑,安装protoc可参考文档:Protocol Buffer Compiler Installation
protocol编译插件安装
1 2
| $ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
|
安装完成后可以在bin目录下看到相关指令:
1 2 3
| $ ls $GOPATH/bin
protoc-gen-go protoc-gen-go-grpc
|
项目开发
项目源码地址
项目目录结构
1 2 3 4 5 6 7 8 9 10 11 12
| ├── demo_3 │ ├── client │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── helloworld │ │ ├── helloworld_grpc.pb.go │ │ ├── helloworld.pb.go │ │ └── helloworld.proto │ ├── README.md │ └── server │ └── main.go
|
项目创建
1 2
| $ mkdir demo_3 && cd demo_3 $ go mod init
|
安装grpc依赖
1
| $ go get -u google.golang.org/grpc
|
编写proto文件
流式rpc
使用stream
关键字定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # helloworld/helloworld.proto
syntax = "proto3";
option go_package = "/helloworld"; package helloworld;
service Hello { rpc SayHello (stream HelloRequest) returns (HelloReply) {} }
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
|
生成go代码
1 2 3
| $ protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ helloworld/helloworld.proto
|
命令执行成功之后会在helloworld目录下生成两个文件: helloworld_grpc.pb.go
和helloworld.pb.go
,注意: 不要手动编辑这两个文件。
编写服务端代码
flag
用法可参考官方文档: https://pkg.go.dev/flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
package main
import ( "flag" "fmt" "github.com/silenceboychen/gostudy/demo_3/helloworld" "google.golang.org/grpc" "google.golang.org/grpc/reflection" "io" "log" "net" )
var ( port = flag.Int("port", 8080, "The server port") )
type server struct { helloworld.UnimplementedHelloServer }
func (s *server) SayHello(stream helloworld.Hello_SayHelloServer) error { for { res, err := stream.Recv() if err == io.EOF { return stream.SendAndClose(&helloworld.HelloReply{Message: "over"}) }
if err != nil { return err } log.Println(res.Name) } }
func main() { flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() reflection.Register(s) helloworld.RegisterHelloServer(s, &server{}) log.Printf("server listening at %v", lis.Addr()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
|
编写客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
package main
import ( "context" "flag" "github.com/silenceboychen/gostudy/demo_3/helloworld" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "log" "strconv" "time" )
var ( addr = flag.String("addr", "localhost:8080", "the address to connect to") name = flag.String("name", "world", "Name to greet") )
func main() { flag.Parse() conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := helloworld.NewHelloClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel()
stream, err := c.SayHello(ctx) if err != nil { log.Fatalf("Upload list err: %v", err) } for n := 0; n < 5; n++ { err := stream.Send(&helloworld.HelloRequest{Name: "stream client rpc " + strconv.Itoa(n)}) if err != nil { log.Fatalf("stream request err: %v", err) } } res, err := stream.CloseAndRecv() if err != nil { log.Fatalf("SayHello get response err: %v", err) } log.Println(res) }
|
项目运行
开启两个终端,分别运行服务端代码和客户端代码,服务端代码要先运行。
服务端
1 2 3 4 5 6 7 8
| $ go run server/main.go
2023/06/16 20:24:15 server listening at [::]:8080 2023/06/16 20:24:22 stream client rpc 0 2023/06/16 20:24:22 stream client rpc 1 2023/06/16 20:24:22 stream client rpc 2 2023/06/16 20:24:22 stream client rpc 3 2023/06/16 20:24:22 stream client rpc 4
|
客户端
1 2 3
| $ go run client/main.go
2023/06/16 20:24:22 message:"over"
|
下一篇将向大家介绍双向流式rpc。