GRPC实践

trip.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
// 请求数据格式
message GetTripRequest{
string id = 1;
}
// 返回数据格式
message GetTripResponse{
string id = 1;
Trip trip = 2;
}
// 服务
service TripService {
rpc GetTrip (GetTripRequest) returns (GetTripResponse);
}

生成代码,为了生成service的代码框架,需要加参数plugins=grpc

1
protoc -I=. --go_out=plugins=grpc,paths=source_relative:gen/go trip.proto

命令出错:

-go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC

版本问题解决办法:重新下载grpcprotoc-gen-go两个包

1
2
go get google.golang.org/grpc
go get github.com/golang/protobuf/protoc-gen-go

运行成功后,在trip.pb.go里面有客服端和服务端的接口

image-20220225140946052

image-20220225140922882

在server下面创建目录tripservice,创建文件trip.go来实现Service接口

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package trip

import (
"context"
trippb "coolcar/proto/gen/go"
)

type Service struct{}

func (*Service) GetTrip(c context.Context, req *trippb.GetTripRequest) (*trippb.GetTripResponse, error) {
return &trippb.GetTripResponse{
Id: req.Id,
Trip: &trippb.Trip{
Start: "abc",
End: "dfe",
DurationSec: 3600,
FeeCent: 10000,
},
}, nil
}

在server下创建main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
trippb "coolcar/proto/gen/go"
trip "coolcar/tripservice"
"log"
"net"

"google.golang.org/grpc"
)

func main() {
l, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatalf("fail to listen: %v", err)
}

s := grpc.NewServer()
trippb.RegisterTripServiceServer(s, &trip.Service{})
log.Fatal(s.Serve(l))
}

运行即可启动服务:

image-20220225145956411

模拟客户端:创建client/main.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
package main

import (
"context"
trippb "coolcar/proto/gen/go"
"fmt"
"log"

"google.golang.org/grpc"
)

func main() {
cc, err := grpc.Dial("localhost:8081", grpc.WithInsecure())
if err != nil {
log.Fatalf("cannot connect server:%v", err)
}
tsc := trippb.NewTripServiceClient(cc)
r, err := tsc.GetTrip(context.Background(), &trippb.GetTripRequest{
Id: "trip423",
})
if err != nil {
log.Fatalf("cannot call GetTrip:%v", err)
}
fmt.Println(r)
}

在TERMINAL运行:

1
go run client/main.go

image-20220225150134494

服务器内部GRPC都用TCP连接,要将GRPC暴露给外部使用,下图有两种方法。web转二进制其实不太方便,JavaScript和HTTP设计之初都是为了文本所考虑的。

image-20220225134502047

GRPC Gateway实现

image-20220225155044128

gen.bat文件是生成命令:

1
2
3
protoc -I=. --go_out=plugins=grpc,paths=source_relative:gen/go trip.proto

protoc -I=. --grpc-gateway_out=paths=source_relative,grpc_api_configuration=trip.yaml:gen/go trip.proto

trip.yaml是配置文件:

1
2
3
4
5
6
7
type: google.api.Service
config_version: 3

http:
rules:
- selector: coolar.TripService.GetTrip
get: /trip/{id}

运行gen.bat

image-20220225155335185

生成trip.pb.gw.go

image-20220225155416071

main.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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main

import (
"context"
trippb "coolcar/proto/gen/go"
trip "coolcar/tripservice"
"log"
"net"
"net/http"

"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
)

func main() {
log.SetFlags(log.Lshortfile)
go startGRPCGateway()
l, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatalf("fail to listen: %v", err)
}

s := grpc.NewServer()
trippb.RegisterTripServiceServer(s, &trip.Service{})
log.Fatal(s.Serve(l))
}

func startGRPCGateway() {
c := context.Background()
c, cancel := context.WithCancel(c)
// 调用cancel内部就和gateway断开
defer cancel()
//配置传出的数据格式,可以改为false测试不同。
mux := runtime.NewServeMux(runtime.WithMarshalerOption(
runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseEnumNumbers: true,
UseProtoNames: true}},
))
err := trippb.RegisterTripServiceHandlerFromEndpoint(
c,
//注册位置
mux,
":8081",
//连接方式
[]grpc.DialOption{grpc.WithInsecure()},
)
if err != nil {
log.Fatalf("cannot start gateway: %v", err)
}

err2 := http.ListenAndServe(":8080", mux)
if err2 != nil {
log.Fatalf("err2:%v", err2)
}
}

运行main.go启动服务,之后客户端调用:

image-20220225162646745

在网页上

image-20220225162818169