玩转微服务之gRPC快速入门

通过一个简单的Demo快速入门gRPC

0x0. Demo结构

目录结构

依赖参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module grpc-example

go 1.18

require (
github.com/golang/protobuf v1.5.2 // indirect
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/grpc v1.48.0 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

0x1. 环境准备

  1. 安装依赖
1
go get google.golang.org/grpc
  1. 安装插件
1
2
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest // 用于生成*.pb.go文件
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest // 用于生成*_grpc.pb.go文件

0x2. 安装protoc编译器

protobuf GitHub:https://github.com/protocolbuffers/protobuf/releases

安装好之后将bin文件夹添加至环境变量

0x3. 编写proto文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 这个就是protobuf的中间文件

// 指定的当前proto语法的版本,有2和3
syntax = "proto3";

// 指定包位置和名称
option go_package = ".;api";

// 方法
service MessageSender {
rpc Send(MessageRequest) returns (MessageResponse) {}
}

// 响应体
message MessageResponse {
string responseSomething = 1;
}

// 请求体
message MessageRequest {
string saySomething = 1;
}

0x4. 生成代码

1
2
3
4
5
6
// 进入proto文件所在的文件夹
cd ./server/api
// 自动生成代码
protoc --go_out=. --go-grpc_out=. *.proto
// 自动生成代码 不生成mustEmbedUnimplemented*方法
protoc --go_out=. --go-grpc_opt=require_unimplemented_servers=false --go-grpc_out=. *.proto

运行过后,server/api文件夹内会多两个文件,*pb.go*_grpc.pb.go

0x5. Client

client/client.go文件内容如下:

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
package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc-example/server/api"
"log"
)

func main() {
conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", 8082), grpc.WithInsecure())
if err != nil {
log.Println(err)
}

defer conn.Close()

client := api.NewMessageSenderClient(conn)
send, err := client.Send(context.Background(), &api.MessageRequest{
SaySomething: "你好!",
})
if err != nil {
log.Println(err)
}
fmt.Println(send.ResponseSomething)
}

0x6. Server

server/api/api.go文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package api

import (
"context"
"fmt"
)

type ServerApi struct {
}

func (s *ServerApi) mustEmbedUnimplementedMessageSenderServer() {
fmt.Println("implement me")
}

func (s *ServerApi) Send(ctx context.Context, request *MessageRequest) (*MessageResponse, error) {
return &MessageResponse{ResponseSomething: fmt.Sprintf("收到了,你说的是:%s", request.GetSaySomething())}, nil
}

server/server.go文件内容如下:

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
package main

import (
"google.golang.org/grpc"
"grpc-example/server/api"
"log"
"net"
)

func main() {
// 1. new一个grpc的server
rpcServer := grpc.NewServer()

// 2. 将刚刚我们新建的ProdService注册进去
api.RegisterMessageSenderServer(rpcServer, new(api.ServerApi))

// 3. 新建一个listener,以tcp方式监听8082端口
listener, err := net.Listen("tcp", ":8082")
if err != nil {
log.Fatal("服务监听端口失败", err)
}

// 4. 运行rpcServer,传入listener
_ = rpcServer.Serve(listener)
}

0x7. 运行

首先运行server/server.go然后运行client/client.go,运行效果如下:

1
2
3
4
5
6
7
GOROOT=D:\Applications\Go #gosetup
GOPATH= #gosetup
D:\Applications\Go\bin\go.exe build -o C:\Windows\Temp\GoLand\___go_build_grpc_example_client.exe grpc-example/client #gosetup
C:\Windows\Temp\GoLand\___go_build_grpc_example_client.exe
收到了,你说的是:你好!

Process finished with the exit code 0